@strifeapp/astro
v1.4.1
Published
Official Strife integration for Astro — connect your Astro site to a RavenDB-backed Strife content store via a strife:store virtual module.
Downloads
650
Readme
@strifeapp/astro
Official Strife integration for Astro. It connects your Astro site to a Strife (RavenDB-backed) content store and exposes an initialized document store to your pages through a strife:store virtual module — resolved at runtime, so the same build runs against any environment and secrets are never baked into the bundle.
Contents
Installation
npx astro add @strifeapp/astroOr install manually and add the integration to your config yourself:
npm install @strifeapp/astroRequirements
- Astro 5 or 6 —
astrois a peer dependency (^5.0.0 || ^6.0.0). Astro 5+ is required because the integration usesastro:envfor runtime-safe secret resolution. - Node 22.12+.
- A Strife / RavenDB content store and a client certificate registered with it (PFX, any key type — RSA or ECDSA).
ravendb is installed as a dependency; you do not need to install it yourself.
Usage
Register the integration in your Astro config:
import { defineConfig } from 'astro/config';
import strife from '@strifeapp/astro';
export default defineConfig({
integrations: [
strife({
// All fields are optional; environment variables take precedence (see Configuration).
urls: ['https://your-ravendb.example.com'],
database: 'your-database',
collections: [{ name: 'Posts' }, { name: 'Pages' }],
}),
],
});Reading content
Read from the store anywhere in your site through the strife:store virtual module:
---
import { store } from 'strife:store';
const session = store.openSession();
// The integration deploys a `Content/ByUrl` index for URL-based lookups.
const page = await session
.query({ indexName: 'Content/ByUrl' })
.whereEquals('url', Astro.url.pathname)
.firstOrNull();
---Telemetry: <Insights />
Drop-in Web Vitals + accessibility telemetry. Add it once to your layout <head>, the same way you'd add Astro's <ClientRouter />:
---
import Insights from '@strifeapp/astro/Insights.astro';
---
<head>
…
<Insights />
</head>On every page load it reports Core Web Vitals (LCP, INP, CLS, FCP, TTFB) and, when the browser is idle, an axe-core accessibility audit (re-running on Astro View Transitions). Both are sent with navigator.sendBeacon to the hosted Strife insights API and stored as RavenDB time series on the page's content document. The beacon carries your public team id — resolved server-side from your Strife env, so no secret reaches the browser — letting the API attribute metrics to your workspace and validate the request against your registered domains.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| endpoint | string | https://api.strife.app/insights/collect | Override the collection endpoint. |
| accessibility | boolean | true | Set false to skip the axe-core audit (Web Vitals still reported). |
The API only stores a beacon when its
Originis one of the team's registered domains. For local development, run the API inDevelopmentand addlocalhostto your team'sDomains.
TypeScript: typing strife:store
Typed ambient declarations for the strife:store virtual module are not bundled in this release. Until a typed surface is published, add your own declaration (e.g. in src/env.d.ts):
declare module 'strife:store' {
import type { DocumentStore } from 'ravendb';
export const store: DocumentStore;
}Direct Vite plugin
The underlying Vite plugin is also exported standalone:
import { vitePluginStrifeStore } from '@strifeapp/astro/vite-plugin-strife-store';Configuration
strife(options?) accepts:
| Option | Type | Description |
| --- | --- | --- |
| urls | string[] | RavenDB server URLs. |
| database | string | Database name. |
| collections | { name: string }[] | Collections to index (default: Posts). |
Credentials are not build-time options.
certificateandpasswordare not accepted as integration options — passing them would bake the secret into the built bundle. Provide them at runtime viaSTRIFE_SECRETor theSTRIFE_CERTIFICATE/STRIFE_CERTIFICATE_PASSWORDenv vars (below). The integration strips anycertificate/passwordit receives as options before generating the store module.
Environment variables
The integration registers STRIFE_* as astro:env server secret variables, so you do not need import 'dotenv/config' or any other loader — just set them:
| Variable | Maps to |
| --- | --- |
| STRIFE_SECRET | All of the below, packed into one value (see Generating STRIFE_SECRET) |
| STRIFE_DATABASE_URLS | urls (comma-separated) |
| STRIFE_DATABASE | database |
| STRIFE_CERTIFICATE | base64-encoded PFX (optional in development) |
| STRIFE_CERTIFICATE_PASSWORD | certificate password |
Set them in a local .env for development, or in your host's environment (Vercel project settings, Kubernetes secrets, etc.) for deployment. Because they are astro:env server secrets, they are read at runtime via getSecret and never inlined into the built bundle.
STRIFE_SECRET is a single, compact value that bundles the URLs, database, certificate, and password — convenient when one secret is easier to manage than four. It is additive: when set, each field it carries takes priority; when unset, the four individual variables work exactly as before.
Resolution order, per field: STRIFE_SECRET → individual STRIFE_* env var → non-secret integration option (urls/database only) → built-in default.
Reading via
astro:env(rather thanprocess.env) is deliberate: inastro dev, Astro does not populateprocess.envfrom.env, so aprocess.env-based read would be empty locally.astro:env'sgetSecretreads.envin dev and the host environment in production, with no extra setup.
Generating STRIFE_SECRET
STRIFE_SECRET has the form v1.<meta>.<cert>: a version tag, your urls/database/password as base64url-encoded JSON, and your PFX certificate as base64url-encoded raw bytes (encoded once — ~33% smaller than embedding an already-base64 cert in JSON). The format is key-algorithm-agnostic: the same STRIFE_SECRET works whether the certificate inside is RSA or ECDSA.
The blob format is stable, so you can generate it yourself in a few lines: base64url-encode JSON.stringify({ urls, database, password }), base64url-encode the raw .pfx bytes, and join them as v1.<meta>.<cert>. Contributors working in the repository can use the bundled helper:
# password via env var (recommended)
STRIFE_PFX_PASSWORD='your-cert-password' node --experimental-strip-types \
scripts/pack-secrets.mjs \
--pfx ./client.pfx \
--urls https://your-ravendb.example.com \
--database your-database
# …or pipe the password instead of putting it in the environment
printf '%s' "$PFX_PW" | node --experimental-strip-types \
scripts/pack-secrets.mjs --pfx ./client.pfx --urls … --database … --password-stdinThe password is read only from STRIFE_PFX_PASSWORD or --password-stdin — never a CLI flag, since process arguments are world-readable. The helper prints STRIFE_SECRET=<blob>; pipe it straight into your host's secret store rather than echoing it (it otherwise lands in shell history). --experimental-strip-types is unnecessary on Node ≥ 23.6 / 22.18. (The helper script ships with the repository, not the published package; a strife secrets pack CLI command is planned.)
Platform size limits
The certificate dominates the value's size, and base64 of an encrypted PFX is incompressible — so a large certificate may not fit a platform's per-variable env limit. This is a property of the certificate, not of STRIFE_SECRET (the standalone STRIFE_CERTIFICATE has the same ceiling).
| Platform | Per-variable limit | A ~2 KB ECDSA cert (~1.8 KB blob) | A ~4 KB RSA-4096 cert (~5.5 KB blob) | | --- | --- | --- | --- | | Kubernetes secret | ~1 MiB | ✅ | ✅ | | Vercel (serverless) | 64 KB total | ✅ | ✅ | | Vercel (edge) / Cloudflare Workers | ~5 KB | ✅ | ❌ | | Netlify build-time | 5,000 chars | ✅ | ❌ | | AWS Lambda / Netlify Functions (SSR) | 256 chars / 4 KB total | ❌ | ❌ |
If you hit a limit, the fix is a smaller certificate — RavenDB authenticates clients by thumbprint, not key algorithm, so you can register a compact ECDSA P-256 client certificate (≈1.2 KB PFX) and use it instead of an RSA-4096 one. No encoding change helps a large cert; a smaller key does.
How it works
On astro:config:setup the integration registers the STRIFE_* env schema and a Vite plugin. When the strife:store module is first evaluated (at build for prerendered pages, at request time for SSR), the plugin:
- Resolves configuration at runtime via
getSecret(never baked into the bundle). - Connects to RavenDB using the resolved URLs, database, and client certificate.
- Deploys a
Content/ByUrlmulti-map index (deploymentMode: 'Rolling') for URL-based content lookup. - Bulk-inserts the configured collections into the
Templatescollection. - Exposes the initialized RavenDB
DocumentStorethrough thestrife:storevirtual module.
The
Content/ByUrlindex is deployed with a bundled helper source (localized-content-index.js) that runs inside RavenDB's Jint engine. It is intentionally plain ES5 — do not transpile it.
License
ISC © Strife
