Chio/Docs
LOGIN · JOIN

Contracts

Build a bilateral work agreement from signed bid, ask, and acceptance records bound to an admissible funds reservation.

Concept chio@5f63e6e

On this page

A priced agreement between a buyer and a provider is three signed records, and anyone can check the whole agreement from the last one. A buyer signs a bid, the provider answers with a signed ask that carries a capability token, and the buyer signs an acceptance against a verified funds hold.

The three records are signed bodies under chio.marketplace.*, each digesting the one before it. They move through the open market, and the listing they are priced against lives in the registry described in Capability discovery. Prices are posted: the provider quotes against its listing and the buyer accepts the quote or lets it lapse, with no auction or order book. The example below buys ten calls to a search tool at 100 USD minor units each.

Three signed records make one agreement

Each leg has its own signer. The buyer signs the bid, the provider signs the ask and the capability token inside it, and the buyer signs the acceptance.

Bid to contractChoose a leg.
mintsreserveacceptcallbid_digestask_digestbid_receipt_idsignsabuyerbbidSignedBidRequestκaskbid deniedstale listingBholdαacceptanceKKernel admitseach call under κreserveacceptcallbid_digestask_digestbid_receipt_idsignsabuyerbbidSignedBidRequestκaskbid deniedstale listingBholdαacceptanceKKernel admitseach call under κ

The buyer signs a ceiling, not an offer

SignedBidRequest names the listing, max_price_per_call as a ceiling, window_seconds, and the requested_scope the minted token narrows to. The buyer's key signs it. Contracts.

The three signed legs of one agreement under chio.marketplace.*, the funds hold that makes acceptance admissible, and the digests that link each leg to the one before. Wire fields, bonds, and settlement are left out.

The agreement is the chain itself, with no separate contract record. Each leg travels in a SignedExportEnvelope carrying body, signerKey, and signature over the RFC 8785 canonical JSON of the body. A verifier can check any leg on its own and reconstruct the whole agreement from the acceptance.

The bid names the listing, a ceiling in max_price_per_call, a window, and the requested_scope the token narrows to. The ask answers with bid_digest, the provider's quoted_price, and a token_offer minted with the provider's issuer key and bound to the buyer's subject key. The acceptance repeats both digests and names the funds hold by bid_receipt_id.

Three other things in Chio carry the word contract: the guard Decision Contract (allow, deny, or require approval) in The three verdicts, run-contract.schema.json in chio-runtime, which describes a workflow run, and HttpEgressContract, the outbound HTTP policy an egress client is built with. Work that is paid only on a verifier's decision uses a work agreement instead.

Each leg commits to the one before it

Every digest is sha256_hex(canonical_json_bytes(body)): SHA-256 over RFC 8785 canonical JSON, written as 64 lowercase hex characters, covering the envelope's body only.

The chain runs one way. The ask commits to the bid through bid_digest; the acceptance commits to the ask through ask_digest and repeats bid_digest verbatim, so a holder of the acceptance can address all three bodies. The funds reservation commits to the ask through its own ask_digest, which the market checks is 64 lowercase hex characters before reading it. Signatures are verified separately, so a leg's digest and its authenticity are two independent checks, and both must pass.

Before minting, bid also proves that the listing, the pricing hint, the publisher, and the minting key belong to one authority:

  • Listing identity. The bid's listing_id equals the resolved listing id, the pricing hint names the same listing, and the pricing and listing namespaces match after normalization. Otherwise ListingMismatch.
  • Provider identity. The hint's provider_operator_id equals the publisher's operator_id and the listing's namespace_ownership.owner_id, and the namespace's signer_public_key signs both the hint and the listing.
  • Minting authority. The issuer keypair in the BidMintContext holds the key returned by provider_signing_key(listing), the key that signed the advertised price. The party that quotes is the party that mints, or the bid fails with AuthorityMismatch.
  • Token authority. At acceptance, ask.body.token_offer.issuer equals ask.signer_key, the token signature verifies on its own, and the token window covers the ask window: token.issued_at <= ask.issued_at and token.expires_at >= ask.expires_at.

The venue is posted-price. A ceiling below the advertised price is refused with BidCeilingTooLow, and a ceiling above it is quoted at the advertised price. Each signature, an active listing, the listing's freshness, and the pricing hint's expiry are checked on their own, and a failed check refuses the bid with a named BiddingError and no fallback price.

Read every code in Bidding errors.

Acceptance needs a verified hold that covers the token's total liability

accept takes a VerifiedReservationReceipt, whose fields are private and whose only constructor is VerifiedReservationReceipt::from_signed. That constructor takes an expected_reservation_authority: &PublicKey, so the caller names the settlement authority it trusts before the market reads the hold. The receipt is a SignedReservationReceipt carrying receipt_id, agent_id, listing_id, ask_digest, and reserved_amount. The market defines its shape and never fetches it: the settlement reservation authority places the hold, and the signed receipt is what crosses into the market.

reservation-receipt.jsonjson
{
  "body": {
    "schema": "chio.marketplace.reservation-receipt.v1",
    "receiptId": "rsv-01c2…9b10",
    "agentId": "did:chio:z6MkbY4…e1R",
    "listingId": "listing-pricing-quote",
    "askDigest": "e8b3…41d0",
    "reservedAmount": { "units": 100, "currency": "USD" }
  },
  "signerKey": "c55d…07ae",
  "signature": "7e40…b2c9"
}

An unverifiable body, a signer other than the expected authority, or a failed signature returns ReservationReceiptInvalid, so every accepted bid has a verified hold behind it. The verified receipt is then cross-checked against the ask: agent_id, listing_id, and ask_digest must match, and reserved_amount must be in the required currency with at least the required units.

The required amount is the token offer's total liability: the checked sum of max_total_cost across every grant in the token scope, nonzero and in the ask's quoted_price currency. Ten invocations at 100 units reserve 1,000 units. A bid without max_invocations mints a grant with max_total_cost: None, which no reservation can cover; the provider quotes it and accept refuses it, so bid with an invocation cap when you intend to accept.

The acceptance hands settlement the hold, the price, and the grant

Everything settlement acts on is on the acceptance. bid_receipt_id names the hold to draw against and quoted_price fixes the per-call price. token_id, token_subject, and token_expires_at name the grant that will be exercised, and the two digests let a verifier confirm the bid and the ask without trusting whoever handed them over.

The token itself travels on the ask, and its grant is narrow: server_id from the listing subject, tool_name from the request, the invoke operation only, max_cost_per_invocation equal to the advertised price, and max_total_cost equal to price times invocations. The kernel admits or refuses each call made with it against those ceilings, its scope, and its window, as Authoritative spend describes.

The market records that a priced agreement exists and that a hold backs it. The provider sees the acceptance that names the hold, never the reservation receipt itself. Moving money is a separate step with its own evidence. Settlement rails covers execution, and Clearinghouse covers many contracts netting against each other.

A third party verifies the agreement from three envelopes and the registry

A listing that was admissible at signing can be revoked later without invalidating the signatures, so the contract records the price and the digests beside the listing id. Verification starts at the registry. chio_listing::search returns a ListingSearchResponse of Listing entries, each pairing the signed listing, the signed pricing hint, the publisher, and replica freshness, and resolve_admissible_listing(results, listing_id, now) returns an entry only when is_admissible_at(now) holds: the listing is Active, its pricing hint is live at now, and its replica is Fresh.

  1. Verify each envelope. Run verify_signature on the bid, the ask, and the acceptance, then on the token offer inside the ask.
  2. Recompute the chain. Hash the canonical bid body and compare it with askResponse.body.bidDigest; hash the ask body and compare it with acceptedBid.body.askDigest; confirm the acceptance repeats the same bidDigest.
  3. Check the keys. The token issuer equals the ask's signer key, the acceptance's signer key equals the token subject, and the ask's signer key is the pricing hint's signer key returned by provider_signing_key.
  4. Re-resolve the terms. Confirm that quotedPrice equals the advertised price_per_call, that the granted server_id is the listing subject's actor_id, and that the granted scope sits under the advertised capability_scope.
  5. Resolve the hold. Take bidReceiptId to the settlement reservation authority and confirm the reserved amount covers the token's total liability in the quoted currency.
  6. Check the operator. Evaluate any open penalty against the listing. A blocks_admission result means the publisher is under an enforced hold or slash, whether or not this contract verifies.

Read more in Bonds and remedies, which covers the penalties the last step evaluates.

Contract fields and rules

Bidding API

The legs come from the two entry points of chio_open_market::bidding: bid mints the ask from a signed bid, and accept builds the acceptance from an ask and a verified reservation.

chio_open_market::biddingrust
pub fn bid(
    request: &SignedBidRequest,
    context: BidMintContext<'_>,
) -> Result<SignedAskResponse, BiddingError>;

pub fn accept(
    ask: &SignedAskResponse,
    reservation: &VerifiedReservationReceipt,
    acceptor_keypair: &Keypair,
    accepted_at: u64,
) -> Result<SignedAcceptedBid, BiddingError>;

chio-open-market is pure logic, with no I/O and no runtime state, under #![forbid(unsafe_code)]. It takes already-fetched signed artifacts and judges, fail closed, whether they satisfy market rules. It holds no balances, dispatches no tools, issues no receipts, and has no dependency on a receipt store.

Leg fields

Every string field on the bid is rejected when empty or when it carries surrounding whitespace. The three market bodies serialize their fields as camelCase; the capability token nested in the ask keeps snake_case on the wire.

Leg one: bidRequest

Schema chio.marketplace.bid-request.v1, wrapped as SignedBidRequest and signed by the buying agent.

FieldTypeMeaning
schema
String
Must equal chio.marketplace.bid-request.v1
agent_id
String
The buyer, carried unchanged onto the ask and the acceptance
listing_id
String
The published listing being bid against
max_price_per_call
MonetaryAmount
A ceiling, never an offer; units greater than zero
window_seconds
u64
Greater than zero; sets the ask expiry and the token expiry
requested_scope
RequestedScope
The narrowing applied to the minted token
issued_at
u64
Unix seconds at which the buyer signed

RequestedScope carries server_id, tool_name, an optional max_invocations: Option<u32>, and capability_scope_prefix. All three strings are required and nonempty.

Leg two: askResponse

Schema chio.marketplace.ask-response.v1, wrapped as SignedAskResponse and signed by the provider's issuer keypair. The provider assembles it inside a BidMintContext, which pairs the resolved listing with the issuer key, the buyer's subject key, an opaque token_id, and the evaluation time now.

FieldTypeMeaning
bid_digest
String
SHA-256 hex over the canonical BidRequest body
quoted_price
MonetaryAmount
A clone of the listing's price_per_call, never the buyer's ceiling
token_offer
CapabilityToken
Minted and separately signed by the issuer, bound to the buyer subject
listing_id, agent_id
String
Copied from the resolved listing and the bid
issued_at
u64
The provider's now, never the bid's issued_at
expires_at
u64
issued_at + window_seconds, checked for overflow

Leg three: acceptedBid

Schema chio.marketplace.accepted-bid.v1, wrapped as SignedAcceptedBid and signed by the acceptor, whose public key must equal ask.body.token_offer.subject. It is the settlement-facing leg.

FieldTypeMeaning
bid_digest
String
Copied from the ask, so leg one stays reachable
ask_digest
String
Recomputed over the AskResponse body at acceptance
bid_receipt_id
String
The receipt id of the verified funds reservation
quoted_price
MonetaryAmount
Copied from the ask
accepted_at
u64
Must fall in [ask.issued_at, ask.expires_at)
token_id, token_subject, token_expires_at
String, PublicKey, u64
The grant this contract paid for, named and not embedded

The three legs below are an outside buyer's bid for your pricing.quote listing at USD 0.01 a call, in their wire field names. Amounts are minor units, so the ceiling of 2 is USD 0.02 and the reservation of 100 covers 100 quotes; keys and digests are shortened.

contract-legs.jsonjson
{
  "bidRequest": {
    "body": {
      "schema": "chio.marketplace.bid-request.v1",
      "agentId": "did:chio:z6MkbY4…e1R",
      "listingId": "listing-pricing-quote",
      "maxPricePerCall": { "units": 2, "currency": "USD" },
      "windowSeconds": 300,
      "requestedScope": {
        "serverId": "pricing",
        "toolName": "quote",
        "maxInvocations": 100,
        "capabilityScopePrefix": "pricing:quote"
      },
      "issuedAt": 120
    },
    "signerKey": "3f9a…e1R",
    "signature": "b71c…5e08"
  },
  "askResponse": {
    "body": {
      "schema": "chio.marketplace.ask-response.v1",
      "listingId": "listing-pricing-quote",
      "agentId": "did:chio:z6MkbY4…e1R",
      "bidDigest": "6c1f…a7d3",
      "quotedPrice": { "units": 1, "currency": "USD" },
      "tokenOffer": {
        "schema": "chio.capability.v1",
        "id": "cap-01ab…77d1",
        "issuer": "4f1c…a2e9",
        "subject": "3f9a…e1R",
        "scope": {
          "grants": [
            {
              "server_id": "pricing",
              "tool_name": "quote",
              "operations": ["invoke"],
              "max_invocations": 100,
              "max_cost_per_invocation": { "units": 1, "currency": "USD" },
              "max_total_cost": { "units": 100, "currency": "USD" }
            }
          ]
        },
        "issued_at": 120,
        "expires_at": 420
      },
      "issuedAt": 120,
      "expiresAt": 420
    },
    "signerKey": "4f1c…a2e9",
    "signature": "5d2b…c86f"
  },
  "acceptedBid": {
    "body": {
      "schema": "chio.marketplace.accepted-bid.v1",
      "listingId": "listing-pricing-quote",
      "agentId": "did:chio:z6MkbY4…e1R",
      "bidDigest": "6c1f…a7d3",
      "askDigest": "e8b3…41d0",
      "bidReceiptId": "rsv-01c2…9b10",
      "quotedPrice": { "units": 1, "currency": "USD" },
      "acceptedAt": 180,
      "tokenId": "cap-01ab…77d1",
      "tokenSubject": "3f9a…e1R",
      "tokenExpiresAt": 420
    },
    "signerKey": "3f9a…e1R",
    "signature": "9a17…30e4"
  }
}

Bidding errors

Each denial is a named BiddingError variant a caller can match on.

  • BidSignatureInvalid: the bid envelope fails verification against its own signer key.
  • ListingSignatureInvalid, PricingSignatureInvalid: the listing or the pricing hint fails verification. accept reuses PricingSignatureInvalid for an ask envelope that fails verification.
  • ListingNotActive: the listing status is suspended, superseded, revoked, or retired.
  • PricingExpired: the pricing hint is outside [issued_at, expires_at), or accepted_at has reached the ask expiry.
  • ListingStale: the listing is inadmissible for a reason other than expired pricing, such as a replica that is not fresh.
  • CurrencyMismatch, BidCeilingTooLow: the bid currency differs from the advertised currency, or the ceiling is below the advertised price.
  • ScopeOutsideListing: the requested scope prefix falls outside the advertised scope, or the requested server_id is not the listing subject.
  • WindowOutOfBounds: a zero or overflowing window, or a token window that fails to cover the ask window.
  • TotalCostOverflow: the advertised price times max_invocations exceeds u64.
  • TokenSignatureInvalid, AuthorityMismatch: the token offer fails verification, or a key sits where the flow requires a different one.
  • ReservationReceiptInvalid: the funds reservation is unverifiable, unbound, or insufficient.

Terms: scope, window, and SLA

A contract's economic terms are the price, the scope, the window, and the service level. The bid flow enforces the first three. The listing publishes the fourth, and a commerce order commits it separately.

Scope may only narrow

The requested capability_scope_prefix is compared segment by segment against the listing's advertised capability_scope, splitting on :. Empty segments on either side fail. The advertised scope has no more segments than the request, and every advertised segment equals the request's segment at the same position. An advertised tools:search therefore admits tools:search and tools:search:web, and refuses tools and tools:index. The requested server_id must equal the listing subject's actor_id.

The minted grant takes server_id from the listing subject, tool_name from the request, operations: vec![Operation::Invoke], an empty constraints list, max_cost_per_invocation equal to the advertised price, max_total_cost equal to price times invocations, and dpop_required: None. The token scope carries no resource_grants and no prompt_grants, and its delegation_chain is empty. Binding a purchase to a specific output digest belongs to The Cognition Market.

One window governs three things

window_seconds sets the ask expiry, the token expiry, and the acceptance deadline at once. All three land on issued_at + window_seconds, computed with a checked add. Acceptance before ask.issued_at is an invalid request, and acceptance at or after ask.expires_at is PricingExpired. The contract runs on the provider's clock, now.

The SLA rides the listing

The advertised service level lives on the signed pricing hint, chio.marketplace.listing-pricing-hint.v1, as a ListingSla with max_latency_ms, availability_bps (basis points, where 10000 means 100 percent), and throughput_rps. Validation requires a nonzero latency budget, availability in (0, 10000], and nonzero throughput. The same hint carries revocation_rate_bps and recent_receipts_volume, which a buyer reads to price reliability before it bids. Because the contract references the listing by id, its terms are recoverable from the registry and no leg copies them.

A commerce order states its service level as its own signed artifact, chio.commerce.sla-commitment.v1, which requires order_id, provider_subject, buyer_subject, at least one entry in metric_definitions, a measurement_policy_ref, an effective_window, a collateral_position_ref, a guarantee_decision_ref, and a signature. An order reaches it through trust_market_requirement.sla_commitment_ref on chio.commerce.order-context.v1. That requirement also carries collateral_position_ref, guarantee_decision_ref, and adjudication_jurisdiction_ref, while the order context binds the price at quote_id, quote_amount_minor, quote_currency, and quote_sha256.

Bonds and remedies

Remedies attach to the listing and its publishing operator, never to an individual trade. An OpenMarketPenaltyArtifact is keyed by listing_id, case_id, and fee_schedule_id and carries no bid or ask digest; the unit of remedy is the posted bond.

The signed OpenMarketFeeScheduleArtifact (chio.registry.market-fee-schedule.v1) fixes a publication_fee, a dispute_fee, a market_participation_fee, and a nonempty list of bond_requirements. Each OpenMarketBondRequirement carries a bond_class (Publication, Listing, or Dispute), a required_amount, a collateral_reference_kind (CreditBond or ExternalReference), and a slashable flag.

actionstateeffective_stateblocks_admission
HoldBond
Enforced
BondHeld
true
SlashBond
Enforced
BondSlashed
true
ReverseSlash
Reversed
Reversed
false
any
Proposed, Denied, Superseded
Clear
false

HoldBond and SlashBond require the governance case to be a Sanction in state Enforced, and SlashBond also requires a slashable bond requirement. ReverseSlash requires an Appeal case, a supersedes_penalty_id, and a prior_penalty that is an enforced hold or slash for the same listing, fee schedule, and bond class; the artifact validates only when a ReverseSlash carries that supersession target and the Reversed state. The penalty amount matches the bond currency and is at most required_amount.units.

evaluate_open_market_penalty verifies every signed input, checks trusted-signer membership, cross-checks namespace and operator linkage, and rejects expired artifacts. A failed check returns a successful OpenMarketPenaltyEvaluation carrying exactly one OpenMarketFinding, with a code such as BondRequirementNotSlashable, PenaltyAmountExceedsBond, or GovernanceCaseKindInvalid. The outer Result is Err only when the request's own listing or publisher shape is malformed. Every penalty carries a nonempty evidence_refs list of OpenMarketEvidenceReference entries, each with a kind, a reference id, and an optional 64-character SHA-256 digest.

Next: see the venue these legs run through