Node.js SDK
@florianjs/opaque-node is the simplest adapter: a single bootstrap() function that fetches secrets and injects them into process.env before your app starts.
Works with Node.js >= 18 and Bun >= 1.1.
Installation
npm install @florianjs/opaque-node
# or
bun add @florianjs/opaque-nodeSetup
Add the three opaque environment variables to your deployment config:
OPAQUE_PRIVATE_KEY='{"kty":"OKP","crv":"Ed25519","d":"...","x":"..."}'
OPAQUE_VAULT_URL="https://vault.example.com"
OPAQUE_PROJECT="my-app"Usage
Call bootstrap() as the very first thing in your entry point, before any other imports that might read from process.env:
// server.ts
import { bootstrap } from "@florianjs/opaque-node";
await bootstrap();
// All secrets are now in process.env — safe to import the rest of your app
import { createServer } from "./app";
const server = createServer();
server.listen(3000);Bootstrap must be first
bootstrap() must be awaited before any code that reads from process.env. In TypeScript/ESM, top-level await works in the entry module. In CommonJS, wrap in an async IIFE:
// CommonJS entry point
const { bootstrap } = require("@florianjs/opaque-node");
async function main() {
await bootstrap();
const { app } = require("./app"); // require after bootstrap
app.listen(3000);
}
main().catch(console.error);Express example
// index.ts
import { bootstrap } from "@florianjs/opaque-node";
await bootstrap();
// Express reads process.env.PORT, process.env.DATABASE_URL, etc.
import express from "express";
import { Pool } from "pg";
const app = express();
const db = new Pool({ connectionString: process.env.DATABASE_URL });
app.get("/health", (_req, res) => res.json({ ok: true }));
app.listen(Number(process.env.PORT ?? 3000));Fastify example
// server.ts
import { bootstrap } from "@florianjs/opaque-node";
await bootstrap();
import Fastify from "fastify";
const server = Fastify({ logger: true });
server.get("/", async () => {
return { message: "hello" };
});
await server.listen({ port: Number(process.env.PORT ?? 3000) });Explicit configuration
If you prefer not to rely on environment variables, pass config directly:
import { fetchSecrets, injectEnv } from "@florianjs/opaque";
const secrets = await fetchSecrets({
vaultUrl: "https://vault.example.com",
privateKey: process.env.OPAQUE_PRIVATE_KEY!,
project: "my-app",
env: "production", // override NODE_ENV
});
injectEnv(secrets, process.env as Record<string, string>);Force override existing values
By default, injectEnv does not overwrite values already set in process.env. This means existing environment variables take precedence over vault values. To force vault values:
import { fetchSecrets, injectEnv } from "@florianjs/opaque";
const secrets = await fetchSecrets({
/* ... */
});
injectEnv(secrets, process.env as Record<string, string>, { force: true });Bun
@florianjs/opaque-node works identically in Bun. No additional configuration is needed:
// index.ts (Bun)
import { bootstrap } from "@florianjs/opaque-node";
await bootstrap();
// your appbun run index.ts