Ledger & provenance
The provenance ledger is Paybond's append-only signed journal of every intent state transition. It is the ground truth for Signal rollups, audit exports, and dispute arbitration packages.
- Ledger service:
crates/paybond-ledger. Backing store: sled (filesystem tree, separate from the Harbor intent sled). - Consumer of record:
go/signal-indexertails ledger events, applies them idempotently, and produces signed operator receipts.
Event shape
Each ledger event is a signed envelope containing:
| Field | Role |
|---|---|
seq | Monotonic sequence number. Unique per ledger. |
tenant_id | Authenticated tenant realm. Harbor refuses to append without it. |
kind | Event type (intent_created, intent_funded, evidence_submitted, intent_released, intent_refunded, intent_disputed, dispute_resolved, …). |
payload | Canonical JSON body; the exact keys depend on kind. |
parent_hash | 32-byte BLAKE3 digest of the previous event's signed envelope. Forms the Merkle chain. |
signature | Ed25519 signature over the canonical envelope with the Harbor signing key. |
The current tip is the most recent sequence and entry commitment for the tenant. Harbor exposes it at GET /ledger/v1/tip. Paginated history for Signal and operators is GET /ledger/v1/events with after_seq (exclusive cursor; default 0) and limit (Harbor clamps to 256). The deployment verifying key is GET /ledger/v1/authority; the latest Merkle anchor envelope is GET /ledger/v1/merkle/latest.
For intent_created, Harbor now mirrors both operator_did and principal_did into the signed
event payload. Signal uses that tenant-scoped principal/operator edge only for additive
cluster-detection and shadow-risk explanation; it is not a canonical score input in
score_version = 1.0.
Tenant scoping
Ledger appends are rejected before persistence unless the tenant envelope matches the authenticated context. On multi-tenant deployments that share one ledger, the tenant id is part of the signed envelope; Signal and audit queries filter by tenant on every read.
Replay semantics
The Signal indexer consumes events starting from its checkpoint. Inserts into tenant-scoped Postgres tables are idempotent (ON CONFLICT DO NOTHING on intent keys and terminal outcomes). Consequences:
- Restarting the indexer is safe and does not double-count.
- Lowering the checkpoint alone does not re-apply counters. A controlled rebuild requires
PAYBOND_SIGNAL_ALLOW_DESTRUCTIVE_RESET=trueandPAYBOND_SIGNAL_RESET_MODE=full_tenant_signalon the indexer.
Audit exports
The Gateway builds audit ZIP bundles by joining:
- A tenant-scoped slice of the provenance ledger (Harbor events).
- Signal operator rows and signed receipts.
- Dispute case rows from Postgres.
- Stripe settlement rows for the same wall-clock window.
Every bundle includes a signed manifest.json linking file SHAs to the ledger tip at build time, so a downstream auditor can verify the bundle matches a specific ledger position. Contract: docs/api/gateway-audit-export-openapi.yaml and docs/operations/audit-export-compliance.md.
Signing keys and rotation
- Harbor ledger signing key is configured via
PAYBOND_HARBOR_LEDGER_SIGNING_KEY_HEX(or an HSM-backed variant in production). Rotation is additive: new keys come online, old keys remain valid for verifying historical events. - Signal receipts and portfolio artifacts use separate Ed25519 keys (
PAYBOND_SIGNAL_RECEIPT_SIGNING_KEY_HEX,PAYBOND_SIGNAL_ARTIFACT_SIGNING_KEY_HEX). - Audit export manifests use a dedicated signing key on the Gateway.
Key rotation runbook: docs/security/secrets-and-key-rotation-v1.md.
Backup and DR
- Back up the ledger sled tree and the Harbor intent sled tree together. They share consistency invariants (for example a
fundedrow points at a ledger sequence). - Restores: copy both sled paths to the new host with identical file permissions; validate the tip hash on startup.
- Drill record:
docs/operations/backup-restore-drill-record-v1.md.