Chio/Docs

Reconciliation & Watchdog

Reconciliation is what turns a settled rail confirmation into a closed ledger entry: the kernel pre-charges a quoted amount, the tool runs, the meter records what actually happened, the rail confirms the move, and the budget delta gets credited or debited. Watchdog automation catches the cases where one of those steps stalls.

Use this page when

You need the reconcile cycle, exposure-ledger fields, watchdog job types, or the loss lifecycle. Don't use this if you want the rail-side mechanics (see Settlement Rails) or how the regulator reads the resulting receipts (see Regulatory APIs).

The Reconciliation Cycle

Five steps, each producing an artifact that the next step can verify:

  1. Pre-charge. Before the call executes, the kernel resolves a MeteredBillingQuote: a quoted unit count and a quoted monetary cost. For must_prepay orhold_capture modes, the rail authorizes that amount.
  2. Dispatch. The governed call runs. Cost metering observes actual compute time, data bytes transferred, and any per-API-call upstream cost.
  3. Capture observed cost. The meter produces an observed_units count plus an evidence digest. The rail's capture() finalizes the authorization at the observed amount; release() unwinds any unused hold.
  4. Debit/credit budget delta. The exposure ledger moves the receipt from reserved_units to settled_units. Any difference between quoted and observed cost is credited back to the agent's grant or debited if the observed cost overshot the quote.
  5. Emit reconciled receipt. The receipt now carries a final SettlementStatus (Settled or Failed), the rail-side payment_reference, and the metered evidence digest.

Cycle Diagram

rendering…

Three Cost Layers

Every reconciled receipt resolves three cost values. They are recorded separately so each one can be audited on its own:

LayerSourceFinalized whenCarried on receipt as
Pre-chargedMeteredBillingQuote.quoted_costAt intent signingmetered_billing.quote
ObservedCost meter, post-executionWhen the tool returns or errorsmetered_billing.usage_evidence.observed_units
ChargedRail capture amountWhen capture() returns Settledpayment_reference + settlement_status

Quoted and observed are the truth claims. Charged is the rail-side confirmation that the truth claim turned into actual money movement. Reconciliation is the act of asserting that all three line up, or recording a delta if they do not.


The Exposure Ledger

Across many in-flight settlements, the operator needs a single view of total open exposure. chio-credit provides this as the exposure ledger:chio.credit.exposure-ledger.v1. Each currency has its own position record:

chio-credit/src/lib.rs
pub struct ExposureLedgerCurrencyPosition {
    pub currency: String,
    pub governed_max_exposure_units: u64,
    pub reserved_units: u64,
    pub settled_units: u64,
    pub pending_units: u64,
    pub failed_units: u64,
    pub provisional_loss_units: u64,
    pub recovered_units: u64,
    pub quoted_premium_units: u64,
    pub active_quoted_premium_units: u64,
}

Each receipt entry in the report carries a SettlementStatus plus an action_required flag. Receipts move through reserved_units on authorize, into pending_units once dispatched, and finally land in settled_units or failed_units on rail confirmation. Provisional and recovered amounts capture the loss lifecycle described below.

The ledger query (ExposureLedgerQuery) requires at least one anchor (capability, agent subject, or tool server) and an optional time window. Operators run it for live monitoring, regulators query it for jurisdictional audits.


The Settlement Watchdog

Settlements stall. Counterparties go offline; rails delay; chains reorg. The watchdog automates detection and the safe-recovery primitives without ever taking unilateral action that would lose truth.

Watchdog jobs are defined in chio-settle/src/automation.rs. Schema: chio.settlement-automation-job.v1.

chio-settle/src/automation.rs
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SettlementWatchdogKind {
    EscrowTimeout,
    FinalityObservation,
    BondExpiry,
}

pub struct SettlementWatchdogJob {
    pub schema: String,
    pub job_id: String,
    pub kind: SettlementWatchdogKind,
    pub trigger_kind: SettlementAutomationTriggerKind,
    pub chain_id: String,
    pub replay_window_secs: u64,
    pub cron_expression: String,
    pub state_fingerprint: String,
    pub operator_override_required: bool,
    pub reference_id: String,
}

Three watchdog kinds:

  • EscrowTimeout. An on-chain escrow has been EscrowLocked past the dispute window and neither dual-signature nor merkle release has fired. The watchdog signals; the operator dispatches refund.
  • FinalityObservation. A previously confirmed settlement needs its finality recomputed (confirmation count drift, reorg detection). Built by build_settlement_watchdog_job.
  • BondExpiry. A bond is approaching the configured expiry; the operator must either renew or release the underlying capital. Built by build_bond_watchdog_job.

Every watchdog job carries operator_override_required = true. Execution outcomes are explicit:

chio-settle/src/automation.rs
pub enum SettlementAutomationOutcome {
    Executed,
    DuplicateSuppressed,
    DelayedButSafe,
    ManualOverrideRequired,
}

assess_watchdog_execution verifies that the execution's observed state fingerprint still matches the job's recorded fingerprint, that delays past the replay window are explicitly marked DelayedButSafe, and that duplicate suppression is recorded as such rather than silently executed twice. If the operator override flag is set on the job, the execution must explicitly carry operator_override_used = true or the assessment fails.

Watchdog never settles unilaterally

Even when a watchdog job fires, it does not move money on its own. It detects the stall, classifies the recovery action (SettlementRecoveryAction: WaitForConfirmations, RetrySubmission, ResubmitAfterReorg, ExecuteRefund, ManualReview, ExpireBond), and presents it to the operator. The operator's signer is what actually broadcasts the recovery transaction.

The Loss Lifecycle

Some settlements never recover. The depositor disappears; the provider takes a chargeback; the chain stays reorged. The exposure ledger handles this with two paired fields: provisional_loss_units and recovered_units.

  1. A receipt's action_required flag flips on when the watchdog can no longer auto-recover.
  2. The operator marks a provisional loss against the exposure ledger. The amount is added to provisional_loss_units.
  3. If the loss is recoverable from a bond (operator-funded collateral in ChioBondVault), the bond is impaired by prepare_bond_impair; the recovered amount is credited to recovered_units.
  4. If the loss is recoverable from a credit facility (covered in Credit Facilities), the facility absorbs the residual exposure and the operator documents the drawdown.
  5. Net unrecoverable loss settles to the operator's P&L. The receipt continues to carry the final Failed status; the loss is now closed in the ledger.

Billing Export

Reconciled receipts feed the billing export pipeline in chio-metering/src/export.rs. Schema: chio.billing-export.v1. Each export is a flat, denormalized record-per-receipt:

chio-metering/src/export.rs
pub struct BillingRecord {
    pub schema: String,
    pub receipt_id: String,
    pub timestamp: u64,
    pub timestamp_iso: String,
    pub session_id: Option<String>,
    pub agent_id: String,
    pub tool_server: String,
    pub tool_name: String,
    pub compute_time_ms: u64,
    pub data_bytes: u64,
    pub cost_units: Option<u64>,
    pub currency: Option<String>,
    pub provider: Option<String>,
}

pub struct BillingExport {
    pub schema: String,
    pub exported_at: u64,
    pub record_count: u64,
    pub total_cost: Option<MonetaryAmount>,
    pub records: Vec<BillingRecord>,
}

The export aggregates total cost across records when all share a currency; mixed-currency exports drop the rolled-up total_cost field rather than emit a misleading sum. The hands-on workflow for producing exports lives at Export Billing Records.


Operator Workflow

The day-to-day reconciliation activities an operator performs:

  • Live monitoring. Run the exposure ledger query against active capabilities to watch pending_units versus reserved_units and catch backlogs before they become losses.
  • Periodic close. Trigger the billing export at the operator's billing cadence (daily, weekly, monthly). The export contains only reconciled receipts, not pending ones.
  • Watchdog review. Inspect the watchdog log for jobs that fired without an Executed outcome. Anything in ManualOverrideRequired is the action queue.
  • Loss documentation. When a settlement turns into a confirmed loss, mark the provisional loss, drive bond impair if applicable, and record the recovery transaction id in the exposure ledger.
  • Quarterly facility review. Cross-check reconciled volume against the configured facility ceiling; rebalance if utilization is consistently high.

Worked Example: $0.15 Refund

An agent has a capability bound to a Stripe Api rail and settlement_mode = hold_capture. The capability quotes $1.00 per call. Today's call hits a cheap cache path and the actual cost is $0.85.

Step 1: Quote

The intent is signed with quoted_cost = $1.00 and settlement_mode = hold_capture.

Step 2: Authorize

The Stripe adapter creates a PaymentIntent with amount = 100 cents, capture_method = manual. Returns PaymentAuthorization{ authorization_id: "pi_3...", settled: false }. The exposure ledger records reserved_units += 100 for currency USD.

Step 3: Dispatch

Tool runs. Cost meter records observed_units = 85 cents with an evidence digest from the upstream provider's API response.

Step 4: Capture

The Stripe adapter calls capture(authorization_id, 85, "USD", reference). Stripe captures 85 cents and releases the unused 15 cents back to the customer's available limit. The kernel records payment_reference = "pi_3...", settlement_status = Settled.

Step 5: Budget Delta

The exposure ledger transitions: 100 leaves reserved_units; 85 enters settled_units. The 15-cent delta is credited back to the agent's grant budget so the next call sees the unused headroom.

Step 6: Reconciled Receipt

The receipt now carries:

json
{
  "metered_billing": {
    "settlement_mode": "hold_capture",
    "quote": {
      "quoted_units": 100,
      "quoted_cost": { "units": 100, "currency": "USD" }
    },
    "usage_evidence": {
      "evidence_kind": "stripe-api",
      "evidence_id": "ch_3...",
      "observed_units": 85
    }
  },
  "payment_reference": "pi_3...",
  "settlement_status": "settled"
}

At the next billing export cycle, this receipt becomes a BillingRecord with cost_units = 85, currency = "USD", and provider = "stripe". The 15-cent overage was a quote error, not a charge, and never appears in the export.


In the Procurement Tour

This is station 6 of the Procurement Tour: the buyer kernel reconciles 1000 quoted units against 850 observed units and credits the $0.15 delta back to the grant budget.

Picks up from previous station. The signed bilateral receipt is in hand; the cost meter has emitted observed_units = 850 with an evidence digest from the SOC 2 review tool.

The buyer kernel computes the reconciliation delta. With unit_price = $0.001/unit from the manifest and settlement_mode = hold_capture, the kernel was holding $1.00 reserved against did:chio:9b4f...'s grant. The capture finalizes at the observed amount and the ledger position moves accordingly:

reconcile-soc2-review.json
{
  "schema": "chio.reconciliation.v1",
  "receipt_id": "rec_soc2_2026_04_28_001",
  "buyer": "did:chio:9b4f...",
  "provider": "did:chio:c87a...",
  "tool": "soc2-review",
  "unit_price": { "units": 1, "currency": "USD", "scale": 1000 },
  "quoted": {
    "units": 1000,
    "amount": { "units": 100, "currency": "USD" }
  },
  "observed": {
    "units": 850,
    "amount": { "units": 85, "currency": "USD" },
    "evidence_kind": "vanguard-soc2-review",
    "evidence_id": "vng_soc2_run_8821"
  },
  "charged": {
    "amount": { "units": 85, "currency": "USD" }
  },
  "delta": {
    "amount": { "units": 15, "currency": "USD" },
    "direction": "credit_to_grant"
  },
  "ledger_transition": {
    "currency": "USD",
    "before": {
      "reserved_units": 100,
      "settled_units": 0,
      "grant_balance_units": 9900
    },
    "after": {
      "reserved_units": 0,
      "settled_units": 85,
      "grant_balance_units": 9915
    }
  },
  "settlement_status": "pending_dispatch"
}

The reconciliation record carries forward the charged_amount = $0.85, the receipt id, and the settlement_status = pending_dispatch flag. The next station picks the rail that will move the $0.85 from the buyer's escrow to Vanguard.

Continue at next station, where the buyer kernel selects the configured Web3 rail and builds the dispatch artifact.

See Also

Reconciliation & Watchdog · Chio Docs