Certificates

Turnkey TLS-certificate manager. Wraps b.acme.create (RFC 8555 client + RFC 9773 ARI renewal-window respect), sealed persistence via b.vault.seal, the renewal scheduler from b.safeAsync.repeating, OCSP-stapling via b.network.tls.ocsp, and the operator's choice of ACME challenge solver (HTTP-01 / DNS-01 / TLS-ALPN-01).

The operator passes a declarative manifest of certificates + storage + an ACME directory URL + per-challenge solver callbacks; the manager handles ordering, finalization, retrieval, periodic renewal, key rotation on renew, OCSP refresh, and sealed-disk persistence of every artifact.

Composes: - b.acme.create → ACME orders, JWS, ARI fetch - b.vault.seal → sealed-disk persistence of certs + keys + account material - b.safeAsync.repeating → renewal scheduler with drop-silent error path - b.network.tls.ocsp → fetches + caches a validated OCSP response per cert for server-side stapling - b.audit → cert.* lifecycle audit chain - b.compliance → validates the declared posture names; storage-confidentiality postures hold because keys/certs are always sealed at rest

Does NOT ship the challenge-solver implementations (HTTP-01 server, DNS provider integrations, TLS-ALPN-01 socket). Those are operator- side adapters — the manager calls operator-provided provision(challengeParams) / cleanup(challengeParams) callbacks for whatever solver the operator wires.

Key escrow: when keyEscrow: { recipient } is set, the renewed private key is also encrypted to the recipient's public key via b.crypto.encryptEnvelope and persisted alongside the sealed key. The recipient is operator-controlled (typically an offline break-glass key); the escrow copy is for legitimate key-recovery under break-glass policy, NOT for routine access.

b.cert.create(opts) #

stable0.11.22
{
  storage: {
    type:    "sealed-disk",         // only backend in v1 — operator-supplied storage extensible via the same shape
    rootDir: string,                // required — directory under which sealed artifacts land
    vault:   b.vault.Store,         // optional — defaults to b.vault.getDefaultStore()
  },
  acme: {
    directory:    string,           // required — RFC 8555 directory URL (https://)
    contactEmail: string,           // optional — mailto: contact registered on account
    accountKey:   { privatePem, publicPem } | "auto",   // "auto" → generate + persist on first start; sealed via storage.vault
    timeoutMs:    number,           // optional — per-HTTP-call timeout; defaults from b.acme.create
    ariCompliant: boolean,          // optional, default true — RFC 9773 ARI renewalInfo respect
  },
  certs: Array<{
    name:      string,              // required — unique manifest identifier; used as subdirectory + lookup key
    domains:   Array,       // required — first entry is the CN subject; rest are SANs
    keyAlg:    "ecdsa-p256" | "ecdsa-p384" | "rsa-2048" | "rsa-3072" | "rsa-4096", // default "ecdsa-p256"
    challenge: {
      type:      "http-01" | "dns-01" | "tls-alpn-01",
      provision: async function (params) { ... },     // required — operator wires the solver
      cleanup:   async function (params) { ... },     // required — runs after authorization completes
    },
    keyEscrow: {                    // optional — break-glass-only key recovery
      recipient: string | { publicKey, ecPublicKey },  // ML-KEM-1024 pubkey PEM, or a
                                    //   b.crypto.generateEncryptionKeyPair() hybrid pair; the
                                    //   renewed key is sealed to it via b.crypto.encrypt and
                                    //   recovered offline with b.crypto.decrypt
    },
  }>,
  renew: {
    intervalMs:          number,    // default 6h — poll cadence
    minDaysBeforeExpiry: number,    // default 14 — renew if ,         // optional — posture names (e.g. ["hipaa"]); validated against b.compliance.KNOWN_POSTURES (throws on an unknown name) + surfaced on getContext().compliance. Cert keys/certs are always sealed at rest, so storage-confidentiality postures hold by construction.
}

Build a turnkey cert-management handle. Composes b.acme.create for the ACME protocol layer, b.vault.seal for sealed-disk persistence, b.safeAsync.repeating for the renewal scheduler, and b.network.tls.ocsp for stapling.

The handle exposes: - start() — ensures every manifest cert exists (issues if absent); starts the renewal scheduler. - stop() — halts the renewal scheduler; releases sealed handles. - getContext(name) — returns { cert, key, ca, expiresAt, fingerprintSha256 } (PEM strings + meta) for the named cert. - sniCallback — function (servername, cb) suitable for https.createServer({ SNICallback }) — looks up by SNI hostname, falls back to the first registered cert. - refresh(name) — force-renew the named cert NOW (operator override). - on(event, fn)cert.issued / cert.renewed / cert.renew-failed / cert.ocsp-refreshed.

var mgr = b.cert.create({
  storage: { type: "sealed-disk", rootDir: "/var/lib/blamejs/certs" },
  acme: {
    directory:    "https://acme-v02.api.letsencrypt.org/directory",
    contactEmail: "ops@example.com",
    accountKey:   "auto",
  },
  certs: [
    {
      name:      "main",
      domains:   ["example.com", "www.example.com"],
      keyAlg:    "ecdsa-p256",
      challenge: {
        type:      "http-01",
        provision: async function (p) { await myHttp01Server.add(p.token, p.keyAuthorization); },
        cleanup:   async function (p) { await myHttp01Server.remove(p.token); },
      },
    },
  ],
});
await mgr.start();
var ctx = await mgr.getContext("main");
typeof ctx.cert;     // → "string" (PEM chain)
typeof ctx.key;      // → "string" (PEM)
typeof ctx.expiresAt; // → "number" (epoch ms)

Last updated 2026-08-08T16:39:49.652Z by seeder.