Skip to content
Sigilbase

Reconciling a registry#

A registry promises more than a stream: every request that reached it was decided under a published policy, recorded, and receipted, and each commitment key accepted at most once. Reconciling checks that promise from the evidence alone, with no access to Sigilbase.

What you need#

  1. The registry's evidence bundle and its decision log's, each exported from sequence 1. Export the log first and the registry after it: both are written in time order, so the verifier can tell a record made after the other export from one missing from it.
  2. Optionally, the receipts submitters hold, as a directory of JSON files (the PHP SDK's exportReceipts() writes one).
  3. The open-source verifier, 1.8 or later, with Sigilbase's receipt keys (built in, or --receipt-keys with the file from GET /api/v1/receipt-keys).
php verify.php --reconcile filings.zip filings-decisions.zip --receipts receipts/

What it checks#

Both bundles are verified in full first. Then each rule, in order; a failure names the first rule broken, as reason in --json output.

Rule Reason when broken
The log is this registry's (decision_log_for), both bundles start at sequence 1, and in each every received_at is no earlier than the one before NOT_DECISION_LOG, LOG_NOT_DENSE, NOT_TIME_ORDERED
The log's first event is policy.committed FIRST_NOT_POLICY
Each policy, its windows joined back from policy.windows, hashes to its policy_hash POLICY_HASH
Each policy names this registry; versions run 1, 2, 3; only a policy under notice (an hour to a hundred years) is amended, never while an amendment is pending, and no sooner than its notice period POLICY_AMENDMENT
Every decision names the policy in force and fits it: an acceptance, replay or conflict inside its key's window, a replay or conflict repeating the key's logged acceptance, an early or late refusal early or late, an unknown key absent; a refusal for a pause or archive only while the log shows one, and nothing else then DECISION_POLICY
Every accepted decision matches exactly one registry event by entry hash, request digest and received_at, and every registry event matches exactly one acceptance ACCEPTED_UNMATCHED, PARENT_UNMATCHED
At most one acceptance per commitment key DOUBLE_ACCEPTANCE
Every window closes once, saying committed exactly when an acceptance came before close_at, and every window that closed more than five minutes before the log's last record has its close record WINDOW_CLOSE
Every acceptance came from a credential the log shows able to submit at that point, from the opening credentials.snapshot and every issue, revocation and expiry since CREDENTIAL_NOT_VALID
Every receipt supplied is logged at its decision_seq, field for field, and its event was sealed by its seal_by RECEIPT_NOT_LOGGED, RECEIPT_SUBJECT, MSD_BREACH

A record past the other bundle's range is missing from it, and fails, when it came before that bundle's last record, or when it was due to be sealed (fifteen minutes after received_at, plus two minutes' grace) by the time that bundle was known complete: its latest checkpoint, or a trusted timestamp over one. Otherwise it may have been made after that bundle's export, and the run is unconfirmed until the bundle is exported again. An unconfirmed run never passes. Scope refusals, throttles and an invalid decision's reasons other than unknown_key are recorded but not checked: the log holds nothing to check them against.

A receipt Sigilbase signed whose decision is not in the log is proof the operator omitted a decision: only Sigilbase's receipt key could have signed it. The run also prints how many decisions the log holds, per outcome and per credential.

What it cannot prove#

A request that never reached Sigilbase cannot be shown by anyone: there is nothing to sign, record or receipt. Reconciling proves what was received and how it was decided, not what a submitter sent. A submitter can keep its own attempt log (the PHP SDK does) to show attempts that never got a receipt apart from decided ones.