Skip to content

Production: api-billing-v2 (verifiable API billing)

Deploy checklist for durable replay, signed transparency, and strict verification.

0. Local setup (keys + .env)

From repo root (writes gitignored verifier-api/.env):

bash
npm run setup:billing-v2-env
npm run setup:billing-v2-env -- --production-secrets   # also production/secrets/.env.production
npm run setup:billing-v2-env -- --all --force          # both targets, rotate keys

Re-run with --force to regenerate billing keys. Then apply migrations (section 1) and start the API.

1. Database migrations

Run on production PostgreSQL (in order):

bash
psql "$DATABASE_URL" -f verifier-api/src/infrastructure/database/migrations/030_vdi_billing_v2.sql
psql "$DATABASE_URL" -f verifier-api/src/infrastructure/database/migrations/031_vdi_billing_replay_crash_safe.sql

030 creates vdi_billing_replay_guard and vdi_billing_transparency_log.
031 adds status, expires_at, committed_at for crash-safe reservations.

2. Cryptographic keys (generate once, store in secrets manager)

bash
./scripts/generate-billing-meter-key.sh

Generate a separate log operator key (recommended):

bash
openssl genpkey -algorithm ed25519 -out log-operator.pem

Generate manifest authority seed (64+ hex, no 0x) for strict profile:

bash
openssl rand -hex 32  # 64 hex chars — keep offline; also set VDI_MANIFEST_AUTHORITY_SIGNING_KEY

3. Required environment variables

VariablePurpose
VDI_ATTEST_ENABLED=trueEnable attest routes
VDI_SIGNING_KEY64+ hex — stable VDI issuer
VDI_ATTEST_SECRETHeader X-VDI-Attest-Secret on attest routes
VDI_BILLING_METER_PRIVATE_KEY_PEM + VDI_BILLING_METER_KIDServer-side meter signing or trust anchors for meter_envelope
VDI_BILLING_LOG_OPERATOR_PRIVATE_KEY_PEM + VDI_BILLING_LOG_OPERATOR_KIDSigned transparency log entries (SLE)
VDI_BILLING_VERIFY_PROFILE=VDI_VERIFY_STRICT_V1Production default strict verify
VDI_MANIFEST_AUTHORITY_SIGNING_KEYEd25519 seed (64+ hex) — manifest authority_binding for strict profile
VDI_BILLING_REVOCATION_JSONInline revocation feed (dev/pilot)
VDI_BILLING_REVOCATION_URLHTTPS JSON feed (production; polled with cache)
VDI_BILLING_REVOCATION_CACHE_SEC60

Optional:

VariableDefaultPurpose
VDI_BILLING_REPLAY_RESERVE_TTL_SEC900In-flight reservation TTL; expired rows are reclaimable after crashes

4. Revocation feed (strict profile)

VDI_BILLING_REVOCATION_JSON must be a JSON object:

json
{
  "revokedAttestations": [],
  "revokedKids": [],
  "snapshotTime": 1716300000
}

Publish updates from your key-status pipeline at https://api.quantzk.com/.well-known/vdi-billing-revocation.json (served by the verifier API from VDI_BILLING_REVOCATION_JSON); do not rely on the empty dev snapshot in production.

5. Crash-safe replay behavior

  1. Reservestatus=reserved, expires_at=now+TTL before attest.
  2. Commitstatus=committed after successful attest + transparency append.
  3. Release — delete reservation on caught failures only.
  4. Recovery — expired reserved rows are reclaimed idempotently (same event_id can be retried).

6. Auditor endpoints

  • GET /.well-known/vdi-billing-revocation.json — published strict-profile revocation snapshot.
  • GET /api/vdi/billing/transparency/head — current Merkle root, tree size, latest entry hash.
  • POST /api/vdi/billing/verify — pass log_inclusion_proof from attest response.

Planned: signed tree-head checkpoints, consistency proofs, external witness publishing.

7. CI / smoke

Billing pilot integration tests (14/14) — canonical command:

bash
cd verifier-api
DATABASE_URL="postgresql://zkcaptcha:development@localhost:5432/zkcaptcha" \
REDIS_ENABLED="false" \
REDIS_DISABLED="true" \
NODE_ENV=test \
DB_SSL=false \
npm run test:billing-pilot

Setup and troubleshooting (Railway DATABASE_URL override, Docker/apt Postgres): docs/dev/verifier-api-test-setup.md.

Production smoke (live api.quantzk.com):

bash
./scripts/smoke-billing-phase2.sh

Evidence template: api-billing-smoke-evidence.md.

8. Phase-2 in-circuit commitment binding

Phase-2 attaches a small Groth16 proof (apiBillingV2 circuit, 402 constraints, 1 public signal) under attestation.billing_proof that binds Poseidon8(field-images of public_commitments) cryptographically — not via the Ed25519 attestation wrapper. The verifier returns incircuit_binding_verification on /api/vdi/billing/verify.

Enable on production:

VDI_BILLING_INCIRCUIT_BINDING=true
# VDI_BILLING_INCIRCUIT_DIR=/repo/verifier-api/circuits/apiBillingV2  # optional override

Circuit artifacts ship in the verifier-api image at /repo/verifier-api/circuits/apiBillingV2/ (mirrored from zk-cp/build/apiBillingV2/). The ARTIFACT_MANIFEST.json in that directory is hash-validated on boot by validateArtifacts(). Regenerate artifacts + re-run Phase-2 trusted setup with operator entropy via zk-cp/scripts/compile-api-billing-v2.sh.

binding_version: bn128-poseidon8-v1 · circuit_hash: 0xa1b1898a9e24021943711c3eb292fb704d4ae19e8c57fa00679a8859d70bb727 · vkey_hash: 0x5f3d88f98cd208e6937752c51377d26cd2081ea6c42824a605087cb80b77eb1e.

9. Roadmap (largely shipped)

  • Shipped: attestation.billing_commitments + proof.public_signals (bn128 field split of digest; signed on attestation) — Phase 1
  • Shipped: attestation.billing_proof (apiBillingV2 Groth16 in-circuit binding) — Phase 2
  • Shipped: transparency consistency proofs (GET /api/vdi/billing/transparency/consistency)
  • Shipped: VDI_BILLING_REVOCATION_URL live feed + @quantzk/vdi-billing/client verifier helper
  • Next: multi-party Phase-2 trusted setup ceremony (rotate dev entropy out before adversarial production trust)

Verification keys are embedded in attestations. The verifier is open source.