Chio/Docs
LOGIN · JOIN

EconomyInsurance

Parametric Insurance

A signed policy, a deterministic predicate over proven receipt evidence, and a payout schedule that computes the claim amount.

Indemnity coverage settles a claim through an adjudicator who assesses a loss. A parametric policy replaces that assessment with a rule: one typed predicate over receipt-observable events, evaluated against an evidence corpus whose completeness is proven, producing a payout amount by fixed arithmetic. Any holder of the same corpus recomputes the same verdict and the same claim id.

Where the code lives

chio-parametric is a directory under spec/schemas/ and nothing else. No crate carries that 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.

The 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.jsondefs.schema.jsonunregistered
evidence-corpus-manifest.schema.jsonChio Parametric Evidence Corpus Manifest V1unregistered
policy.schema.jsonChio Parametric Policy V1chio.parametric.policy.v1
trigger-instance-key.schema.jsonChio Parametric Trigger Instance Key V1unregistered

The lifecycle records are stamped with their own schema ids in the crate, and each body's validate rejects any other value in its schema field. They have no published envelope schema, so a verifier reaches them through the module rather than through the registry.

Record schema idRust typeModule
chio.parametric.evidence-source-checkpoint.v1EvidenceSourceCheckpointV1parametric/evidence.rs
chio.parametric.fired-trigger-record.v1ParametricFiredTriggerRecordV1parametric/lifecycle.rs
chio.parametric.claim-record.v1ParametricClaimRecordV1parametric/claim.rs
chio.parametric.claim-opening-state.v1ParametricClaimOpeningStateV1parametric/lifecycle.rs
chio.parametric.contest.v1ParametricContestV1parametric/claim.rs
chio.parametric.payout-preparation-binding.v1ParametricPayoutPreparationBindingV1parametric/claim.rs
chio.parametric.payout-binding.v1ParametricPayoutBindingV1parametric/claim.rs
chio.parametric.payout-intent.v1ParametricPayoutIntentV1parametric/claim.rs

Alongside these the module defines domain separators rather than schemas: 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.


The policy

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

rust
// 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 both 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 then maps a cutoff inside that period to the one window that contains it by integer division, and re-checks that the result starts on the grid and lies inside the effective period. An evaluator cannot choose its own interval.

VerifiedParametricPolicy::verify binds the policy to a canonical SignedLiabilityBoundCoverage supplied by the caller as trusted input. It checks the coverage signature against the trusted coverage-authority key, resolves the provider id out of the placement lineage and compares it to the trusted authority id, then requires the policy to match 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 of those comparisons are derivations rather than copies: both beneficiary_id and subject_key must equal the subject key resolved out of the bound risk package, so the payee is the insured subject rather than a caller-supplied identity.

ParametricPayoutMode declares when a claim becomes payable. Automatic opens the claim payable. Contestable carries a window_seconds that validation requires to be positive, and it is the only field it carries. The claim record stores its own opened_at from the trusted clock and computes contest_deadline by checked addition, so nothing supplied later can shorten the window.


Trigger predicates

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

PredicateRequired sourcesMagnitude and fire condition
GuardDenialRate { min_events, threshold_bps }ReceiptStoreBasisPoints 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 }DriftReportsCount of members whose observation is DriftReport { critical: true }; fires at min_critical or above
SettlementFailureCount { min_failures }ReceiptStore and SettlementReconciliationsCount of reconciliations marked failed; fires at min_failures or above

TriggerPredicate::validate rejects a zero threshold on every class and rejects a threshold_bps above 10_000. The denial rate widens nothing: it takes checked_mul(10_000) on the denial count and returns a typed error rather than a wrapped or saturated value, then divides by the total. SettlementFailureCount refuses to count at all unless 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 its result as a TriggerMagnitude, itself tagged: 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, requires a positive per_unit_minor, and requires the linear magnitude_unit to equal the unit the predicate reports. The payout is

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

with no floating point anywhere. Either checked operation returning overflow 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 is applied at load time to the fixed amount and to the linear base, so a policy that could exceed its coverage never verifies.

The block below shows the decision-bearing fields of a policy body. The identity, digest, and rail fields the section above lists are required too, and a body missing any of them fails validation.

json
{
  "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 }
}

The evidence corpus

A predicate reads members, and a member counts only when a signed checkpoint proves it was in the index. The evaluator verifies one range per required source before it counts anything. EvidenceSourceCheckpointV1 is signed by an evidence source the caller has registered in a TrustedEvidenceSourceRegistry, and verification compares the checkpoint's index namespace, signature domain, anchor epoch, and signer key epoch against that registry entry. A mismatched epoch is a typed error of its own: StaleEvidenceAnchorEpoch or StaleEvidenceSignerEpoch.

The checkpoint must be taken at or after the window ends, and no later than max_checkpoint_lag_seconds after it. Each selected member carries a Merkle proof against the checkpoint's query_index_root, and the completeness argument is the leaf indexes. 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 must occupy consecutive leaves, an optional predecessor must sit immediately before the first and order before the window, and an optional successor must sit immediately after the last and order after it. Absent a predecessor, the first selected leaf must be leaf zero; absent a successor, the last must be the final leaf of the tree. A gap or a missing boundary is IncompleteEvidenceBoundaries, so a submitter cannot drop unfavorable members from the middle or the edge of the range.

Verification then derives the manifest rather than trusting one: EvidenceSourceRangeV1 is built from the checkpoint and the proven members, and its sequence range comes from the members themselves. The corpus digest is taken over a reduced view of those ranges, which keeps the source kind, source id, index namespace, subject key, window, sequence range, expected count, and selected-member root, and drops the checkpoint id, the checkpoint root, the checkpoint time, the whole-index root, the range-proof digest, and both epochs. A later append-only checkpoint or a key rotation that proves the same members therefore yields the same digest.

evaluate_trigger returns VerifiedTriggerVerdictV1::Fired carrying the policy digest, the claim identity, and the magnitude, or NotFired. There is no third verdict: an incomplete or untrusted corpus never reaches evaluation, because verify_evidence_corpus returns a typed error instead of a corpus.


The claim record and its states

Claim identity is semantic. 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, and the two ids are domain-separated digests over it:

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

Duplicate or concurrent evaluations of one semantic trigger therefore produce one claim id, and one payout-intent id derived from it. 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 earlier than the end of the window it is opening for, then computes the payout amount and stores it, so the amount is fixed by the magnitude that fired. The record's version and lifecycle_fence both start at one and must stay equal; every transition advances both by checked addition and refuses a caller whose expected head does not match the current one. A repeated transition against the previous head is recognized as a replay and returns the record unchanged rather than advancing it twice.

StateWireHow the record reaches it
ReadyreadyOpened under Automatic. No contest deadline.
ContestOpencontest_openOpened under Contestable, with the deadline stored.
Contestedcontestedfile_contest before the deadline, storing the contest envelope digest.
UncontestedReleaseduncontested_releasedrelease_uncontested at or after the deadline, with no contest stored.
PayoutReservedpayout_reservedThe shared economic coordinator. validate refuses this state 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 bounded reason code of CorpusIntegrity, PredicateEvaluation, PolicyBinding, or DuplicateEvidence, and between one and 64 evidence digests in strictly ascending order. The claim store supplies the receipt time rather than the contestant, and the transition requires that time to fall inside the contest's own validity window and before the stored deadline. The contestant id must equal the policy's coverage authority.


The 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, and then replays the binding against the record field by field. A contested claim is not eligible.

The capital instruction is checked against the binding rather than trusted: the action must be TransferFunds, the source kind FacilityCommitment, the source id the policy's funding facility, the owner role FacilityProvider, the counterparty role AgentCounterparty, the subject and counterparty the beneficiary, the amount the binding's amount, and the rail kind the binding's rail kind. The signature must verify against a trusted instruction signer.

Coverage is allocated where the coordinator can see it. The binding's effect slot must sit 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 therefore addresses one resource scope, and the coordinator's compare-and-swap on that head is what serializes them.


The adjudication panel domain

Every adjudicator in the indemnity claim chain is a single signer: LiabilityClaimAdjudicationArtifact.adjudicator is one string, and the award rules attach to that one decision. FrostAuthorizationDomain reserves a value for an n-of-m replacement, AdjudicationPanelDecision, which serializes as chio.frost.adjudication-panel-decision.v1.

The reservation is a name and no more. frost_action_registration resolves no entry for it, so it has no ladder action class, no quorum, and no action preimage schema, and FrostActionRegistration::ladder_entry returns FrostAuthorizationError::DisabledDomain for that domain alone, in crates/trust/chio-federation/src/frost/registry.rs. The parametric claim states carry no panel outcome. Read the value as a reserved wire identifier that a roster may list, not as an authorization path.

What a fired trigger proves

A Fired verdict means that the proven corpus satisfied the predicate the policy declared. It does not upgrade the receipts in that corpus from asserted to observed, and it does not move funds: the payout intent is a signed record whose dispatch is a separate governed action. Chio is not the insurer of record, and the records on this page are not a regulated insurance product.

See also

  • Liability Coverage for the indemnity coverage and the SignedLiabilityBoundCoverage allocation a parametric policy binds to. Use that page for coverage bound against an assessed loss.
  • Claims Lifecycle for the single-adjudicator claim chain, which is where a human ruling on a filed claim belongs.
  • Underwriting Risk Taxonomy for the risk inputs that price the premium behind the bound coverage.
  • Schemas and Errors for the whole signed-artifact registry, including the families this page does not list.