Skip to content

VDI Billing v2 Cryptographic Binding Model

This note clarifies the current trust surface for api-billing-v2, including why logs often show publicSignals: 0 and how security-critical facts are still bound.

Why publicSignals: 0 appears

zk-cp currently uses a private-witness-heavy Groth16 circuit configuration for causalProof and returns zero public outputs for this proof path. This is intentional in the present design, not a runtime accident.

That means proof validity alone is not the full story. Security depends on layered binding:

  • proof verification (verification.valid)
  • attestation canonicalization and signature binding
  • receipt attestation-hash/signature binding (receipt_verification.valid)
  • billing v2 fingerprint equality (billing_check.valid)
  • public billing commitments digest (billing.public_commitments_digest at attest time)
  • transparency inclusion proof verification (log_proof_verification.valid)
  • durable replay guard (vdi_billing_replay_guard: reservedcommitted, TTL reclaim after crashes)
  • signed attestation commitment binding (attestation.billing_commitments, proof.public_signals field split)
  • signed transparency log entries (PostgreSQL vdi_billing_transparency_log, operator Ed25519 SLE)

In-circuit vs off-circuit binding

Billing v2 factBound in current public proof outputBound off-circuit
usage_unitsNo (not exposed as public signal)Included in billing fingerprint compare and attestation private-input pipeline
tariff_versionNoIncluded in billing fingerprint compare
expected_charge_microsNoIncluded in billing fingerprint compare
meter_event_digestNoIncluded in billing fingerprint compare + envelope signature verification
meter_signing_kidNoIncluded in billing fingerprint compare + trust-anchor lookup
Receipt integrityNoverifyReceipt over signed receipt + attestation binding
Transparency inclusionNolog_proof_verification (canonical receipt hash + Merkle proof + operator SLE)
Public commitments bundleNobilling.public_commitments + public_commitments_digest (canonical hash of finance-facing fields)
Replay / double-spendNovdi_billing_replay_guard unique (tenant_id, billing_window_id, event_id)

Verification signals and expected failure modes

POST /api/vdi/billing/verify returns independent checks:

  • verification, attestation/circuit-profile verification
  • receipt_verification, receipt integrity and attestation binding
  • billing_check, v1 arithmetic or v2 fingerprint equality
  • log_proof_verification, optional inclusion-proof validation against receipt hash

Expected negative behaviors:

  • Altered usage/tariff values: billing_check.valid = false (others remain true)
  • Modified receipt fields: receipt_verification.valid = false
  • Broken inclusion proof node/hash: log_proof_verification.valid = false

Maturity posture

This architecture can be secure and auditable when the off-circuit bindings are treated as protocol-critical and tested as first-class invariants. The E2E/API suite should continue asserting which signal fails per tamper class, not just pass/fail globally.

For future hardening, consider exposing selected commitments as public outputs (or a committed public root) to move more semantics onto the proof surface while keeping privacy goals.

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