@strifeapp/astro
v1.2.0
Published
Official Strife integration for Astro — connect your Astro site to a RavenDB-backed Strife content store via a strife:store virtual module.
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();
---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
