Chio/Docs

EconomyTools

Capability Discovery

How a signed tool manifest becomes a searchable, priced listing and reaches other organizations through bilateral federation.

Registration with a kernel

Tool servers join a kernel by presenting a SignedManifest. The kernel validates the structure, verifies the Ed25519 signature against a registered public key, and registers the tools (see Signed Tool Manifests).

  • Registration is per-kernel and out-of-band. The operator decides which servers to admit through configuration or a control-plane API.
  • The manifest specifies tool schemas, side-effect flags, latency hints, required permissions, and advertised pricing.
  • Updates require a fresh signed manifest. There is no partial-update path.

Marketplace listings

Beyond kernel-local registration, the implementation provides marketplace listing types in the chio-listing crate. Operators publish signed listings into a generic registry; a sidecar ListingPricingHint attaches price and SLA without coupling to listing publication.

Generic listing

The base record is a GenericListingArtifact with schema chio.registry.listing.v1. Each listing has a status (Active, Suspended, Superseded, Revoked, Retired), an actor kind (ToolServer, CredentialIssuer, CredentialVerifier, LiabilityProvider), and an explicit GenericListingBoundary that defaults to:

crates/economy/chio-listing/src/listing.rs73-81rust
impl Default for GenericListingBoundary {
    fn default() -> Self {
        Self {
            visibility_only: true,
            explicit_trust_activation_required: true,
            automatic_trust_admission: false,
        }
    }
}

The boundary is enforced by the listing crate's validate: a listing that drops these guarantees is rejected. Marketplace listings remain visibility-only until local trust activation.

Signed pricing hint

The pricing hint attaches price-per-call, SLA, recent receipt volume, and revocation rate to a published listing. It is signed by the provider's key, not the registry owner's, so listing publication and pricing can rotate independently. Its schema is chio.marketplace.listing-pricing-hint.v1 (LISTING_PRICING_HINT_SCHEMA).

crates/economy/chio-listing/src/discovery.rs48-74rust
pub struct ListingPricingHint {
    pub schema: String,
    /// Listing this hint applies to.
    pub listing_id: String,
    /// Namespace of the listing (must match the listing body).
    pub namespace: String,
    /// Provider / operator advertising the price (must match the listing
    /// publisher).
    pub provider_operator_id: String,
    /// Capability scope prefix covered by this hint (e.g.
    /// `"tools:search"` or `"tools:search:*"`). Queries filter against this.
    pub capability_scope: String,
    /// Fixed price charged per invocation under the advertised scope.
    pub price_per_call: MonetaryAmount,
    /// Advertised SLA for invocations under this hint.
    pub sla: ListingSla,
    /// Rolling revocation rate over recent invocations, in basis points.
    /// `0` means "no revocations in the window"; `10_000` means "100%".
    pub revocation_rate_bps: u32,
    /// Number of receipts the provider has produced in the recent window.
    pub recent_receipts_volume: u64,
    /// Unix seconds when the hint was issued.
    pub issued_at: u64,
    /// Unix seconds when the hint expires. Past expiry, the hint is stale
    /// and the listing falls out of the marketplace.
    pub expires_at: u64,
}

Local trust activation

A visible listing requires a GenericTrustActivationArtifact (schema chio.registry.trust-activation.v1). It is the single-operator admission-review mechanism. Cross-organization activation uses FederationActivationExchangeArtifact used between federated peers.

Each activation carries a GenericTrustAdmissionClass (PublicUntrusted, Reviewable, BondBacked, RoleGated), a GenericTrustActivationDisposition (PendingReview, Approved, Denied), a GenericTrustActivationEligibility block (allowed actor kinds, publisher roles, statuses, bond-backing and freshness constraints), and a review context pinning the publisher and replica freshness at review time.


Searching listings

The search entry point is chio_listing::discovery::search. It takes a slice of GenericListingReport replicas, a slice of SignedListingPricingHint, a ListingQuery, and the current time. It returns a signed ListingSearchResponse.

crates/economy/chio-listing/src/discovery.rs144-170rust
pub struct ListingQuery {
    /// Capability scope prefix to match against the hint's
    /// `capability_scope`. Matching is a literal prefix match after trim.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub capability_scope_prefix: Option<String>,
    /// Namespace filter. Same normalization as [`GenericListingQuery`].
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub namespace: Option<String>,
    /// Actor-kind filter. Defaults to `ToolServer` when unset.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub actor_kind: Option<GenericListingActorKind>,
    /// Only return listings whose price per call is less than or equal to
    /// this ceiling. Currency must match; listings with differing currency
    /// are filtered out.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_price_per_call: Option<MonetaryAmount>,
    /// Require a specific provider operator id (matches hint and publisher).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub provider_operator_id: Option<String>,
    /// Require fresh listings only. When set to `true` any stale/divergent
    /// listing is rejected. Defaults to `true`.
    #[serde(default = "default_require_fresh")]
    pub require_fresh: bool,
    /// Maximum number of results to return.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub limit: Option<usize>,
}

Filtering rules from the shipped function:

  • Listings without a matching, signed, non-expired pricing hint are dropped.
  • Listings whose pricing hint fails validate() or signature verification are dropped; the error is recorded in ListingSearchResponse.errors.
  • When max_price_per_call is set, listings with a different currency or higher price are dropped.
  • When require_fresh is true (the default), listings whose freshness is Stale or Divergent are dropped.
  • The default actor kind filter is ToolServer.
  • Limit defaults to 100 and is clamped to MAX_MARKETPLACE_SEARCH_LIMIT (which equals MAX_GENERIC_LISTING_LIMIT = 200).

The signed response carries one row per surviving listing, in rank order, plus errors from verifying hints. Each row pairs the signed listing, the verified pricing hint, the publisher, and the freshness record. A normalization helper compare projects a set of Listing entries into a ListingComparison with a price index in basis points so callers see relative cost at a glance.


Search by dimension

The shipped query dimensions cover the operational basics.

DimensionFieldNotes
Capability scopecapability_scope_prefixLiteral prefix match against the hint's declared scope, e.g. tools:search:.
NamespacenamespaceSame normalization as GenericListingQuery.
Actor kindactor_kindDefaults to ToolServer; can also filter to issuers, verifiers, or liability providers.
Price ceilingmax_price_per_callCurrency-strict; mismatched currencies fall out.
Providerprovider_operator_idMatch on hint and listing publisher together.
Freshnessrequire_freshWhen true, drops Stale and Divergent listings.

Jurisdiction is not a built-in filter

The shipped query does not have a jurisdiction field. Operators encode regulatory scope through the listing namespace and the policy reference attached to the underlying trust scope. An operator's catalog UI can apply a jurisdictional filter using those fields.

Reputation aggregation across listings

The pricing hint carries two reputation-adjacent signals directly: revocation_rate_bps (rolling rate over recent invocations in basis points) and recent_receipts_volume (count of receipts in the recent window). Together they let a caller filter on operator activity without contacting any third party.

  • A listing whose hint advertises high recent volume and a low revocation rate is operationally healthy.
  • A listing with zero recent receipts but a long-lived hint is a quiet operator; the caller may still admit it but with smaller initial budget.
  • Native reputation projections (see Reputation) inform how an operator decides whether to trust a listing.

Revocation rate and receipt volume are operator-attested, signed by the provider's key. They are not third-party verified scores; they are claims an auditor can compare against the issuing operator's receipt log.


Federation discovery

Cross-org reach is peer-to-peer through pinned bilateral federation peers (see Bilateral Federation). There is no central directory linking operators.

  • Per-pair listings: a peer can publish listings into the other's registry replica through normal generic-listing publication, subject to the bilateral FederationImportControl.
  • Visibility-only by default: imported listings stay visibility-only until the operator explicitly activates them. Discovery does not silently widen runtime authority.
  • No transitive pull: a listing from Org A's peer Org B does not become visible to Org A's peer Org C just because B and C are also peers. Each pair carries its own evidence.

The activation record is FederationActivationExchangeArtifact, which bundles a listing reference, a trust scope, delegation controls, and import controls. The trust control plane stores and signs this record when an operator approves a partner listing for local use.

rendering
A signed manifest becomes a published listing, reaches a peer operator through a bilateral activation exchange, and stops there until that operator activates it.

Accessing search

There is no chio listing subcommand. The search and compare functions are reached in two ways: in-process by calling chio_listing::discovery::search and chio_listing::discovery::compare directly against local replicas and pricing hints, or over HTTP through the trust-control public registry endpoint.

The network endpoint GET /v1/public/registry/listings/search takes a GenericListingQuery as query parameters and returns a signed GenericListingReport:

terminalbash
$ curl "$REGISTRY_URL/v1/public/registry/listings/search?actorKind=tool_server&namespace=tools.search&limit=25"

# Returns a signed GenericListingReport of the surviving listings for
# the query. The public endpoint filters on listing metadata only:
# namespace, actorKind, actorId, status, limit. The query type renames
# to camelCase, so a snake_case parameter is simply not read.

The richer, pricing-hint-joined filtering described above ( capability-scope prefix, price ceiling, currency-strict matching, and the freshness gate) lives in the in-process search function, which pairs each listing with its verified SignedListingPricingHint before ranking. compare normalizes a set of Listing entries into a ListingComparison with a basis-point price index so callers see relative cost at a glance.


Open-market economics and abuse enforcement

Listings make capabilities discoverable; the chio-open-market crate defines the economics and abuse enforcement that sit on top of a decentralized marketplace. It is the top of the economic stack: fee schedules, bond requirements, and penalty enforcement for a namespace of listings.

An OpenMarketFeeScheduleArtifact is a signed fee schedule scoped to a namespace, defining four charge types: a publication_fee, a dispute_fee, a market_participation_fee, and a list of bond_requirements (per-bond-class collateral amounts, each with a configurable slashable flag).

Misbehavior is penalized through an OpenMarketPenaltyArtifact carrying an OpenMarketAbuseClass ( SpamPublication, FraudulentListing, ReplayPublication, or UnverifiableListingBehavior) with explicit bond actions (HoldBond, SlashBond, ReverseSlash). The pure function evaluate_open_market_penalty gates enforcement behind nine validation conditions before any bond is touched:

  1. Each signed record (listing, fee schedule, charter, governance case, activation, penalty) has a valid signature.
  2. Namespace consistency across those records.
  3. Operator authority consistency.
  4. Fee-schedule scope matching (operator ids, actor kinds, admission classes).
  5. Temporal validity: fee schedule, charter, case, and penalty are not expired.
  6. Bond-requirement matching for the penalty's bond class.
  7. Governance case-kind validity: sanctions require an enforced sanction case; reverse-slash requires an appeal case.
  8. Prior-penalty validity for reverse-slash operations.
  9. Currency and amount coherence.

Enforcement is not self-contained: the crate integrates with chio-governance for charter-based authority scoping and case management. Sanctions and appeals flow through the governance layer before an economic penalty is enforced.


Capability marketplace bidding

Search and compare are the read side of the marketplace. The write side, turning a listing into a purchased, time-bounded capability, is the bid, ask and accept protocol in chio-open-market's bidding module. It is a library interface: a caller links the crate and passes signed records in memory.

A BidRequest (schema chio.marketplace.bid-request.v1) is an agent's signed offer to buy a capability under a published listing. bid() resolves the listing through chio_listing::search, applies the discovered pricing hint, mints a scoped CapabilityToken, and returns a signed AskResponse (chio.marketplace.ask-response.v1) whose token_offer binds the ask to the quote. accept() signs an AcceptedBid (chio.marketplace.accepted-bid.v1) against a VerifiedReservationReceipt, a funds reservation (chio.marketplace.reservation-receipt.v1) whose signature is checked against the expected reservation authority before acceptance, so a settlement layer can verify the canonical bid/ask/accept triple.

crates/economy/chio-open-market/src/bidding.rsrust
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>;

bid() refuses to mint when the resolved listing is not Active (revoked, retired, suspended, superseded), when its pricing hint is stale past expires_at, or when its freshness window has elapsed. It rejects a bid whose currency does not match the advertised pricing, whose ceiling is below the quoted price, whose requested scope falls outside the listing's capability scope, or whose listing, pricing, or issuer authority is not bound to the same provider. The BiddingError enum enumerates each refusal. The protocol carries sixteen integration tests plus an in-crate unit-test module.

Where the bidding protocol runs

The signed bid, ask and accept protocol runs in process against local listings and verified pricing hints. It has no HTTP route and no chio subcommand, so a deployment that wants it over the network carries its own transport and decides for itself what the wire looks like.

Failure modes

  • Pricing hint signature invalid: listing is dropped from the result and the verification error is recorded in ListingSearchResponse.errors.
  • Stale hint: hint expires_at has passed; the listing falls out of the marketplace until a fresh hint is published.
  • Currency mismatch: a price ceiling in USD against an EUR-priced hint is dropped silently from the result; callers should issue a separate query in the other currency or rely on cross-currency conversion at the consuming layer.
  • Divergent freshness: replica disagreement between mirrors triggers a Divergent freshness state. With require_fresh = true, such listings drop out.
  • Listing status not Active:Suspended, Superseded, Revoked, or Retired listings do not surface in marketplace results.