Chio/Docs

Parametric Insurance

Compute a claim from a signed policy, a deterministic trigger over proven receipt evidence, and the payout schedule both parties agreed in advance.

Concept chio@5f63e6e

On this page

A parametric policy pays a predeclared amount when receipt evidence crosses a predeclared threshold, with no loss assessment in between. Anyone holding the same proven evidence window recomputes the same verdict and the same payout, to the minor unit.

The running example watches guard denials. Its policy fires when a one-day window holds at least 500 guard decisions and its denial rate reaches 2,000 basis points, then pays USD 1,000 plus 50 minor units per basis point, against USD 50,000 of bound coverage, after a 48-hour contest window.

Evidence window to payoutChoose an outcome.
Firedπsigned policywwindowttriggerincompleteno countcclaimContestOpen∅NotFiredcreleasedContestedineligibleipayout intentParametricPayoutIntentV1Firedπsigned policywwindowttriggerincompleteno countcclaim∅NotFiredcreleasedContestedineligibleipayout intentone per claim

A proven window that meets the predicate opens one claim

The window holds 500 guard decisions and 100 denials, a rate of 2,000 basis points, which meets the policy's min_events and threshold_bps. The claim opens in ContestOpen with its USD 2,000 amount and a deadline 48 hours after opened_at. Parametric insurance.

One guard_denial_rate policy deciding one evidence window under Contestable terms. The thresholds and amounts follow the illustrative policy fragment; checkpoint signatures, range proofs, and the economic coordinator are left out.

A denial rate becomes a payout by integer arithmetic

These are the decision-bearing fields of the example policy body. A complete body also carries the identity, digest, and rail fields listed under the policy body, and validation refuses a body missing any of them.

policy.jsonjson
{
  "schema": "chio.parametric.policy.v1",
  "subjectKey": "<the risk package subject key>",
  "coverageAmount": { "units": 5000000, "currency": "USD" },
  "windowAnchor": 1720569600,
  "windowSeconds": 86400,
  "maxCheckpointLagSeconds": 3600,
  "predicate": { "kind": "guard_denial_rate", "minEvents": 500, "thresholdBps": 2000 },
  "payoutSchedule": {
    "kind": "linear",
    "base": { "units": 100000, "currency": "USD" },
    "perUnitMinor": 50,
    "magnitudeUnit": "basis_points"
  },
  "payoutMode": { "kind": "contestable", "windowSeconds": 172800 },
  "evaluatorAuthority": { "authorityId": "<evaluator>", "keyId": "<key>", "keyEpoch": 7 }
}

Take a verified window with 500 guard decisions, 100 of them denied. The rate is 100 × 10,000 ÷ 500 = 2,000 basis points, so the window meets both the 500-event minimum and the 2,000-basis-point threshold. The linear schedule pays the 100,000 minor-unit base plus 50 × 2,000 = 100,000 more: 200,000 minor units, or USD 2,000, well inside the 5,000,000 minor units of coverage.

Every step is checked integer arithmetic, with no floating point. The USD 2,000 is the claim amount the record stores; it settles only when a payout clears the contest window and a rail executes it.

Only a complete, proven window reaches the trigger

A predicate counts members, and a member counts only when a signed checkpoint proves it sits in the evidence index. VerifiedParametricPolicy::verify_evidence_corpus verifies one range per source the predicate requires before anything is counted.

  1. The checkpoint. An evidence source registered in the caller's TrustedEvidenceSourceRegistry signs an EvidenceSourceCheckpointV1 at or after the window ends, within max_checkpoint_lag_seconds.
  2. The members. Each selected member carries a Merkle proof against the checkpoint's query_index_root, and the selected members occupy consecutive leaves.
  3. The boundaries. A predecessor leaf sits immediately before the window and a successor immediately after it, or the range runs to the end of the tree. A gap or a missing boundary returns IncompleteEvidenceBoundaries.

The boundary rule keeps a submitter from dropping unfavorable members from the middle or the edge of the range, so completeness comes from the leaf indexes, never from how many receipts someone selected. An incomplete window is repaired at its checkpoint or range proof; its missing observations are never read as a low denial rate.

Read more in the evidence corpus, which lists the epochs, the digest view, and the typed errors.

One semantic trigger opens exactly one claim

A fired trigger binds the policy, subject, window, predicate, and evidence range into a trigger identity, and the claim id derives from it. Duplicate or concurrent evaluations of one window therefore converge on one claim, and on one payout intent.

  1. Verify. Verify the signed policy against its bound coverage, then call verify_evidence_corpus with the intended window, the range proofs, and the trusted source registry.
  2. Evaluate. Call evaluate_trigger on the verified corpus. It returns Fired with the policy digest, the claim identity, and the magnitude, or NotFired, which opens no claim.
  3. Prepare the opening. Pass the verified economic state, policy, corpus, fired trigger, and trusted opening time to prepare_parametric_claim_opening. It returns a proposed opening or a verified replay of the retained one.
  4. Commit. Persist a proposed transition through the economic-state integration. Preparing an opening leaves state unchanged until that commit.

Opening the claim computes the payout from the magnitude that fired and stores it, so the amount is fixed from the first record onward.

The contest window decides when a payout may be prepared

An Automatic policy opens its claim in Ready. The example's Contestable policy opens in ContestOpen with a deadline 48 hours after the trusted opened_at, computed by checked addition so nothing supplied later can shorten it.

  • Contested. The coverage authority files a signed ParametricContestV1 before the deadline, and the claim moves to Contested.
  • Released. release_uncontested at or after the deadline, with no contest stored, moves the claim to UncontestedReleased.
  • Payable. The payout validator accepts a claim in Ready or UncontestedReleased; a contested claim stays ineligible.

A payout intent pairs a payout binding with a signed capital instruction. Payout preparation, authorized rail execution, and observed reconciliation are separate steps, and each retains its own evidence.

Read more in Reconciliation, which matches rail observations against signed instructions.

A fired trigger shows the predicate held, and the payout is a separate action

A Fired verdict means the proven corpus satisfied the predicate the policy declared. Each receipt in that corpus stays asserted or observed exactly as it was, and the payout intent is a signed record whose dispatch is a separate governed action. Chio is not the insurer of record, and these records are not a regulated insurance product.

Read more in Liability Coverage, which binds the coverage every parametric policy draws on.

Policy, evidence, and claim records

Policy body

ParametricPolicy is a closed camelCase body inside a SignedExportEnvelope, and a signed policy is capped at one mebibyte of canonical bytes.

ParametricPolicy fieldsrust
// schema: chio.parametric.policy.v1
pub struct ParametricPolicy {
    pub schema: String,
    pub issued_at: u64,                      // in [bound_at, effective_from] of the coverage
    pub subject_key: String,
    pub bound_coverage_body_digest: String,  // 64-hex
    pub bound_coverage_envelope_digest: String,
    pub coverage_authority_id: String,
    pub payer_id: String,
    pub beneficiary_id: String,
    pub funding_facility_id: String,
    pub pre_action_authority_digest: String,
    pub coverage_amount: MonetaryAmount,     // units > 0, ISO currency
    pub effective_from: u64,
    pub effective_until: u64,                // > effective_from
    pub window_anchor: u64,                  // <= effective_from
    pub window_seconds: u64,                 // > 0
    pub max_checkpoint_lag_seconds: u64,     // > 0
    pub predicate: TriggerPredicate,
    pub payout_schedule: PayoutSchedule,
    pub payout_mode: ParametricPayoutMode,
    pub payout_rail: ParametricPayoutRail,   // { kind, rail_id, destination_account_digest }
    pub evaluator_authority: EvaluatorAuthorityRef,  // { authority_id, key_id, key_epoch }
}

The anchor and the window fix the evaluation grid. validate requires effective_from and effective_until to sit a whole number of windows after window_anchor, so the effective period is an exact run of windows. window_at maps a cutoff inside that period to the one window containing it by integer division, then re-checks that the result starts on the grid and lies inside the period. The evaluator never chooses its own interval.

VerifiedParametricPolicy::verify binds the policy to a canonical SignedLiabilityBoundCoverage the caller supplies as trusted input. It checks the coverage signature against the trusted coverage-authority key, resolves the provider id from the placement lineage and compares it to the trusted authority id, then matches the policy to that coverage field by field: both digests, the payer, the funding facility, the pre-action authority digest, the payout rail, the evaluator authority, the coverage amount, and the effective window.

Two comparisons are derivations: beneficiary_id and subject_key must both equal the subject key resolved from the bound risk package, so the payee is the insured subject. ParametricPayoutMode is Automatic, which opens the claim payable, or Contestable, whose only field is a positive window_seconds. The claim record stores its own opened_at from the trusted clock and computes contest_deadline by checked addition.

Trigger predicates

TriggerPredicate is internally tagged: the wire object carries a kind of guard_denial_rate, drift_severity, or settlement_failure_count, with its own camelCase fields beside it. Each class names the evidence sources it requires, and a corpus carrying a different set of sources is rejected before any counting.

PredicateRequired sourcesMagnitude and fire condition
GuardDenialRate { min_events, threshold_bps }
ReceiptStore
BasisPoints equal to denied * 10_000 / total over the selected guard-decision members; fires when total is at least min_events and the magnitude is at least threshold_bps
DriftSeverity { min_critical }
DriftReports
Count of members whose observation is DriftReport { critical: true }; fires at min_critical or above
SettlementFailureCount { min_failures }
ReceiptStore and SettlementReconciliations
Count of reconciliations marked failed; fires at min_failures or above

TriggerPredicate::validate rejects a zero threshold on every class and a threshold_bps above 10_000. The denial rate takes checked_mul(10_000) on the denial count and returns a typed error in place of a wrapped or saturated value, then divides by the total. SettlementFailureCount counts only when every obligation id in the receipt range appears exactly once in the reconciliation range and the two sets are equal, so a partial reconciliation view cannot under-report failures.

A predicate reports a tagged TriggerMagnitude: BasisPoints { value } for the denial rate and Count { value } for the two count predicates.

Payout schedules

PayoutSchedule is Fixed { amount } or Linear { base, per_unit_minor, magnitude_unit }, tagged the same way. Load-time validation requires the schedule currency to equal the coverage currency, a positive per_unit_minor, and a linear magnitude_unit equal to the unit the predicate reports.

Payout amounttext
amount = checked_add(base.units, checked_mul(per_unit_minor, magnitude.value))

Overflow in either checked operation is ScheduleOverflow, which produces no claim and no payout. A zero result is rejected as an invalid amount, and a result above coverage_amount.units is ScheduleExceedsCoverage. The same ceiling applies at load time to the fixed amount and the linear base; the computed linear payout is checked against coverage at evaluation.

Evidence corpus

Verification compares each checkpoint's index namespace, signature domain, anchor epoch, and signer key epoch against its TrustedEvidenceSourceRegistry entry. A mismatched epoch has its own typed error: StaleEvidenceAnchorEpoch or StaleEvidenceSignerEpoch.

A member is selected when its subject key matches and its observation time falls in the half-open window, and the index orders members by that pair. Selected members occupy consecutive leaves. An optional predecessor sits immediately before the first and orders before the window; an optional successor sits immediately after the last and orders after it. Absent a predecessor, the first selected leaf is leaf zero; absent a successor, the last is the final leaf of the tree. A gap or a missing boundary is IncompleteEvidenceBoundaries.

Verification derives the manifest from the proof: EvidenceSourceRangeV1 is built from the checkpoint and the proven members, and its sequence range comes from the members. The corpus digest covers a reduced view of those ranges:

  • Kept. The source kind, source id, index namespace, subject key, window, sequence range, expected count, and selected-member root.
  • Dropped. The checkpoint id, checkpoint root, checkpoint time, whole-index root, range-proof digest, and both epochs, so a later append-only checkpoint or a key rotation that proves the same members yields the same digest.

evaluate_trigger returns VerifiedTriggerVerdictV1::Fired, carrying the policy digest, the claim identity, and the magnitude, or NotFired. An incomplete or untrusted corpus never reaches evaluation, because verify_evidence_corpus returns a typed error in place of a corpus.

Claim record and states

TriggerInstanceKeyV1 carries the parametric-policy body digest, the bound-coverage body digest, the subject key, the trigger-predicate body digest, the window bounds, and the evidence-range digest. The two ids are domain-separated digests over it:

Claim identitytext
triggerInstanceId = SHA256(
  "chio.parametric.trigger-instance.v1" || 0x00 || RFC8785(TriggerInstanceKeyV1)
)
claimId = SHA256(
  "chio.parametric.claim.id.v1" || 0x00 || RFC8785({ "triggerInstanceId": ... })
)

Changing the policy, the subject, the window, the predicate, or the evidence range changes the key and produces a different claim. ParametricClaimRecordV1::open refuses a trusted open time before the end of its window, then computes and stores the payout amount. The record's version and lifecycle_fence start at one and stay equal; every transition advances both by checked addition and refuses a caller whose expected head differs from the current one. A repeated transition against the previous head is recognized as a replay and returns the record unchanged.

StateWireHow the record reaches it
Ready
ready
Opened under Automatic, with no contest deadline.
ContestOpen
contest_open
Opened under Contestable, with the deadline stored.
Contested
contested
file_contest before the deadline, storing the contest envelope digest.
UncontestedReleased
uncontested_released
release_uncontested at or after the deadline, with no contest stored.
PayoutReserved
payout_reserved
The shared economic coordinator; validate refuses it on a bare record with PayoutReservationRequiresCoordinator.

A contest is a signed ParametricContestV1 binding the claim id, the policy body digest, the expected claim head, the contestant, a validity window, a reason code of CorpusIntegrity, PredicateEvaluation, PolicyBinding, or DuplicateEvidence, and one to 64 evidence digests in strictly ascending order. The claim store supplies the receipt time, which must fall inside the contest's validity window and before the stored deadline. The contestant id must equal the policy's coverage authority.

Payout intent

ParametricPayoutIntentV1 pairs a ParametricPayoutBindingV1 with a signed SignedCapitalExecutionInstruction. The binding restates the claim identity, the expected claim version and lifecycle fence, the coverage reservation and its head digest, the parties, the rail, the effect slot, and the exact MonetaryAmount. The intent id is a domain-separated digest of the claim id alone, so one claim admits one intent.

validate_against_eligible_claim accepts only a claim in Ready or UncontestedReleased whose version and fence equal the binding's expected head, then replays the binding against the record field by field.

The capital instruction is checked against the binding, and its signature must verify against a trusted instruction signer:

  • The action is TransferFunds and the source kind FacilityCommitment, with the policy's funding facility as source id.
  • The owner role is FacilityProvider and the counterparty role AgentCounterparty, with the beneficiary as subject and counterparty.
  • The amount and the rail kind equal the binding's.

The binding's effect slot sits in the parametric namespace, under resource family liability_coverage, with effect kind parametric_payout, a scope id equal to the bound-coverage body digest, a resource id equal to the coverage reservation id, and a resource head digest equal to the stored coverage head. Every parametric claim against one bound coverage addresses one resource scope, and the coordinator's compare-and-swap on that head serializes them.

Adjudication panel domain

Indemnity adjudication has a single signer: LiabilityClaimAdjudicationArtifact.adjudicator is one string, and the award rules attach to that one decision. FrostAuthorizationDomain reserves AdjudicationPanelDecision, serialized as chio.frost.adjudication-panel-decision.v1, as the wire identifier for an n-of-m panel. The parametric claim states carry no panel outcome.

Parametric schemas

The published family holds 4 schema files, of which the registry lists 1: the policy is the signed artifact a counterparty verifies. The rest hold the shared definitions and the two bodies a digest is taken over, the evidence corpus manifest and the trigger instance key.

FileTitleRegistry id
defs.schema.json
defs.schema.json
unregistered
evidence-corpus-manifest.schema.json
Chio Parametric Evidence Corpus Manifest V1
unregistered
policy.schema.json
Chio Parametric Policy V1
chio.parametric.policy.v1
trigger-instance-key.schema.json
Chio Parametric Trigger Instance Key V1
unregistered

The lifecycle records carry their own schema ids, stamped in the crate, and each body's validate rejects any other value in its schema field. A verifier reaches them through the module, since they have no published envelope schema.

Record schema idRust typeModule
chio.parametric.evidence-source-checkpoint.v1
EvidenceSourceCheckpointV1
parametric/evidence.rs
chio.parametric.fired-trigger-record.v1
ParametricFiredTriggerRecordV1
parametric/lifecycle.rs
chio.parametric.claim-record.v1
ParametricClaimRecordV1
parametric/claim.rs
chio.parametric.claim-opening-state.v1
ParametricClaimOpeningStateV1
parametric/lifecycle.rs
chio.parametric.contest.v1
ParametricContestV1
parametric/claim.rs
chio.parametric.payout-preparation-binding.v1
ParametricPayoutPreparationBindingV1
parametric/claim.rs
chio.parametric.payout-binding.v1
ParametricPayoutBindingV1
parametric/claim.rs
chio.parametric.payout-intent.v1
ParametricPayoutIntentV1
parametric/claim.rs

The module also defines domain separators: chio.parametric.trigger-instance.v1, chio.parametric.claim.id.v1, chio.parametric.trigger-predicate.v1, and chio.parametric.evidence-range.v1 prefix a digest preimage and never appear in a schema field.

Implementation sources

chio-parametric names the schema directory under spec/schemas/, and no crate carries the name. The implementation is the parametric module of the chio-market crate: crates/economy/chio-market/src/parametric.rs with its submodules parametric/evidence.rs, parametric/claim.rs, and parametric/lifecycle.rs. The FROST authorization types the panel domain belongs to sit in chio-federation.

Next: compare it with an adjudicated claim