Chio/Docs

BuildEconomics

Tool Pricing

Tool servers publish signed prices; the kernel checks each call against the capability budget at dispatch.

Read Budgets & Metering first

This guide assumes you already understand how budgets are shaped on capability grants. Start with Budgets & Metering for the budget side, then come back here for the pricing side. The two are independent layers that meet at dispatch time.

Price, Budget, and Charged Cost

Chio keeps pricing and budgeting conceptually separate. A tool server publishes a price. An authority issues a budget. The kernel runs at dispatch time and checks whether the budget covers another invocation at the advertised price. Keep those values separate: they come from different actors and are used at different times.

ValueSourceRole
Advertised priceSigned tool manifestOperator input; the kernel does not enforce it directly
Issued budgetCapability grantKernel-enforced ceiling
Kernel-charged costReceipt metadataWhat actually got deducted
Post-execution usageTrust-control sidecarReconciliation record

A tool server publishes pricing in a signed manifest. An operator or authority reads that quote. The authority issues a capability whose monetary budget is consistent with the advertised price plus a local safety margin. The kernel enforces the issued budget at invocation time.


Pricing models

Chio supports four pricing models in manifests, spelled Flat, PerInvocation, PerUnit, and Hybrid in the PricingModel enum. Each corresponds to a builder method on NativeTool in Rust. Rust is currently the supported way to author priced tools.

ModelRust builderBehavior
flatflat_price(units, currency)One fixed base price; no parameter sensitivity
per_invocationper_invocation_price(units, currency)Each invocation uses the same fixed price; billing unit is "invocation"
per_unitper_unit_price(units, currency, billing_unit)Price scales with a declared unit such as 1k_tokens or MB
hybridhybrid_price(base_units, unit_units, currency, billing_unit)Fixed base plus a per-unit variable component

All prices are declared in minor currency units (cents for USD, the smallest denomination for other currencies), matching the MonetaryAmount type used throughout chio.


Manifest Pricing Fields

Every ToolDefinition may carry a pricing object. The four models use the same shape with different populated fields.

tool-manifest-fragment.json
{
  "name": "greet",
  "description": "Returns a personalized greeting",
  "input_schema": { "...": "..." },
  "pricing": {
    "pricing_model": "per_invocation",
    "unit_price":    { "units": 25, "currency": "USD" },
    "billing_unit":  "invocation"
  }
}

For hybrid, a base_price field is also present:

hybrid-pricing.json
{
  "pricing": {
    "pricing_model": "hybrid",
    "base_price":   { "units": 100, "currency": "USD" },
    "unit_price":   { "units": 5,   "currency": "USD" },
    "billing_unit": "1k_tokens"
  }
}

The populated fields differ by model. For flat, the advertised amount lives in base_price; unit_price is absent and no billing_unit is set. For per_invocation, the amount lives in unit_price, billing_unit is "invocation", and base_price is absent. For per_unit, the amount lives in unit_price and billing_unit names the scaling dimension (common values: 1k_tokens, MB, GB, row). The manifest validator enforces this split: it requires base_price for flat, unit_price plus a billing_unit for per_invocation and per_unit, and both amounts plus a billing_unit for hybrid.


Declaring Pricing in Rust

The maintained native example in examples/hello-tool publishes pricing directly from NativeTool. The builder adds pricing directly to a tool definition.

hello-tool/src/main.rs
use chio_core::crypto::Keypair;
use chio_mcp_adapter::{NativeChioServiceBuilder, NativeTool};

// $0.25 per call, priced per invocation.
let greet = NativeTool::new(
    "greet",
    "Returns a personalized greeting",
    serde_json::json!({
        "type": "object",
        "properties": { "name": { "type": "string" } },
        "required": ["name"]
    }),
)
.read_only()
.per_invocation_price(25, "USD");

// Price by token count; $0.005 per 1k tokens.
let summarize = NativeTool::new(
    "summarize",
    "Condense input text",
    serde_json::json!({
        "type": "object",
        "properties": { "text": { "type": "string" } },
        "required": ["text"]
    }),
)
.per_unit_price(5, "USD", "1k_tokens");

// Fixed $1.00 setup fee plus $0.05 per MB.
let archive = NativeTool::new(
    "archive",
    "Compress and store data",
    serde_json::json!({
        "type": "object",
        "properties": { "data": { "type": "string" } },
        "required": ["data"]
    }),
)
.hybrid_price(100, 5, "USD", "MB");

// new() takes the server id and the server's public-key hex (a manifest
// metadata field, not a signing key).
let server_kp = Keypair::generate();
let service = NativeChioServiceBuilder::new("srv-hello", server_kp.public_key().to_hex())
    .tool(greet)
    .tool(summarize)
    .tool(archive)
    .build()?;

// build() only validates the assembled manifest. Signing is a separate step.
let signed = chio_manifest::sign_manifest(service.manifest(), &server_kp)?;

NativeChioServiceBuilder::build() validates the assembled manifest and returns the service; it does not sign. The public-key hex passed to NativeChioServiceBuilder::new(server_id, public_key) is a manifest metadata field, not a signing key. After build(), call chio_manifest::sign_manifest(service.manifest(), &keypair), which re-validates and signs the whole manifest. The signature covers the pricing block, so any post-hoc change to price requires re-signing. Callers can verify the signature before extending trust to the declared prices.

Flat vs. Per-Invocation

flat_price and per_invocation_price can advertise the same per-call amount, but they are not aliases: they emit different manifest shapes and are not interchangeable.

rust
// flat_price -> pricing_model: Flat, amount in base_price, no billing_unit.
tool.flat_price(25, "USD");
// pricing = { pricing_model: "flat", base_price: { units: 25, currency: "USD" } }

// per_invocation_price -> pricing_model: PerInvocation, amount in unit_price,
// billing_unit "invocation".
tool.per_invocation_price(25, "USD");
// pricing = { pricing_model: "per_invocation",
//             unit_price: { units: 25, currency: "USD" },
//             billing_unit: "invocation" }

Reach for flat_price when a tool has no parameter sensitivity and the manifest should record that; use per_invocation_price when the price is a fixed per-call unit. A caller reading the manifest must branch on pricing_model and read the amount from the correct field.


Declaring Pricing Is Rust-Only Today

Priced-tool authoring lives on NativeTool in chio-mcp-adapter, shown above. There is no TypeScript or Python equivalent. The published TypeScript packages are all framework-scoped middleware (@chio-protocol/express, fastify, next, workers, and siblings), and the Python packages (chio-sdk-python, chio-sdk) are clients to the sidecar and to hosted MCP sessions. None of them expose a tool()/pricing.* builder or a ChioToolServer.

A tool server written in another language still advertises pricing the same way: it serves the signed ToolDefinition manifest with the pricing block described above. Authoring that manifest with the native builder is the supported path today. TypeScript and Python callers read the pricing block from a fetched manifest (see Advertising Price to Callers); they do not author it.


Budget Planning from a Quote

Translate the quote into a capability-grant budget. The planning rules depend on the pricing model:

ModelPer-call capTotal budget
flatFlat quoteflat * allowed_invocations + margin
per_invocationQuoted unit priceunit_price * allowed_invocations + margin
per_unitConservative per-call estimate from expected unit ceilingper_call_estimate * allowed_invocations + margin
hybridbase + unit * expected_units_per_callper_call * allowed_invocations + margin

A worked example with the greet tool above: the manifest advertises per_invocation at 25 USD minor units. The expected workload is 40 calls. A straightforward planning pass:

bash
expected_total = 40 * 25 = 1000
safety_margin  = 200
grant_total    = 1200
per_call_cap   = 25

The corresponding capability grant:

grant.rs
use chio_core::capability::{MonetaryAmount, Operation, ToolGrant};

let grant = ToolGrant {
    server_id: "srv-hello".to_string(),
    tool_name: "greet".to_string(),
    operations: vec![Operation::Invoke],
    constraints: vec![],
    max_invocations: None,
    max_cost_per_invocation: Some(MonetaryAmount {
        units: 25,
        currency: "USD".to_string(),
    }),
    max_total_cost: Some(MonetaryAmount {
        units: 1200,
        currency: "USD".to_string(),
    }),
    dpop_required: Some(true),
};

The quote is not the enforcement boundary

The manifest quote tells the authority what budget to issue. The grant budget tells the kernel what to enforce. Do not collapse those concepts. A quoted price with no matching grant budget is unenforceable.

Dispatch-Time Validation

At dispatch, the kernel calls try_charge_cost(). This is an atomic operation that checks three things together, in a single transaction on the budget store:

  1. Invocation count. Does the grant have any max_invocations left?
  2. Per-invocation cap. Is this call's planned cost at or below max_cost_per_invocation?
  3. Total budget. Does the grant have at least the planned cost remaining under max_total_cost?

If any check fails the call is denied with a specific reason code. If all three pass, the planned cost is deducted and the call is dispatched. The deduction is visible on the receipt as metadata.financial.cost_charged.

bash
# The planned cost that the kernel deducts:
#
# flat              -> flat price
# per_invocation    -> unit price
# per_unit          -> unit price * expected_units_per_call (from intent)
# hybrid            -> base price + unit price * expected_units_per_call
#
# The grant's max_cost_per_invocation should be at least this value.
# Post-execution, observed units may differ from expected. That
# reconciliation happens in the trust-control sidecar, not in the
# signed receipt.

Metered Billing and Governed Quotes

Manifest pricing is advisory discovery data. When an operator wants to bind a concrete pre-execution quote into a governed request, Chio supports a typed governed_intent.metered_billing block. It carries:

FieldMeaning
settlement_modemust_prepay, hold_capture, or allow_then_settle
quote.quote_idStable identifier from the metering or billing system
quote.providerBilling authority that issued the quote
quote.billing_unitUnit name (invocation, 1k_tokens, ...)
quote.quoted_unitsEstimated billable units
quote.quoted_costEstimated monetary amount
quote.issued_at / expires_atQuote validity window
max_billed_unitsExplicit upper bound for the governed request

With metered_billing, the pre-execution quote and settlement configuration live on the governed intent; kernel-charged cost lives in metadata.financial on the receipt; post-execution usage is reconciled through a mutable trust-control sidecar keyed by receipt_id. The signed receipt is immutable. Reports can show the quote, kernel-charged cost, and external metering separately.


Cross-Currency Transactions

Tools may quote in one currency while agents hold budgets in another. Chio handles cross-currency settlement through an OracleConversionEvidence record on the receipt's metadata.financial.oracle_evidence field. A Chainlink feed (or other policy-configured oracle) provides the conversion rate at dispatch time; the kernel records the rate as an integer rate_numerator / rate_denominator pair, along with the feed source, address, and timestamp, so an auditor can reconstruct the math. The Settlement guide's Oracle Price Verification section documents the same record from the resolver side.

receipt-fx-evidence.json
{
  "metadata": {
    "financial": {
      "grant_index":        0,
      "cost_charged":       250,
      "currency":           "USD",
      "budget_remaining":   950,
      "budget_total":       1200,
      "delegation_depth":   0,
      "root_budget_holder": "did:chio:z6Mk...",
      "settlement_status":  "settled",
      "oracle_evidence": {
        "schema":               "chio.oracle-conversion-evidence.v1",
        "base":                 "JPY",
        "quote":                "USD",
        "authority":            "chainlink",
        "rate_numerator":       1096,
        "rate_denominator":     100000,
        "source":               "AggregatorV3Interface.latestRoundData",
        "feed_address":         "0x...",
        "updated_at":           1700000125,
        "max_age_seconds":      3600,
        "cache_age_seconds":    12,
        "converted_cost_units": 250,
        "original_cost_units":  22800,
        "original_currency":    "JPY",
        "grant_currency":       "USD"
      }
    }
  }
}

Use one currency unless you need FX

Multi-currency deployments are more complex than single-currency ones. Keep the grant currency and tool-pricing currency identical unless the capability needs a conversion. This avoids conversion and reconciliation work.

Changing Prices

A tool's manifest is signed. Changing the price of an existing tool requires:

  1. Updating the pricing fields on the ToolDefinition.
  2. Re-signing the manifest with the server's key.
  3. Publishing the new manifest version (with an updated version and published_at).

Callers who fetched the previous manifest still see the old price until they re-fetch. When they do re-fetch, they will see the new price before they attempt any delegation or capability issuance. The kernel does not silently adopt new prices: the authority that mints a grant must read the current manifest and issue a budget that is consistent with the current quote. If the price has risen and the grant budget no longer covers a call, the kernel denies at dispatch.

Price increases need operator action

A price increase on a tool will cause existing grants to start denying at dispatch once the per-call cost exceeds the grant's max_cost_per_invocation. This is working as intended. Operators should watch the receipt query API for a spike in budget-cap denies after a price change and re-issue grants with a higher ceiling where appropriate.

Advertising Price to Callers

Before an agent (or an authority on its behalf) delegates a capability for a tool, it fetches the signed manifest during tool discovery and inspects the pricing block. Once parsed, the block is plain structured data in any language:

plan-delegation.ts
// `manifest` is the signed ToolDefinition you fetched during tool discovery.
const pricing = manifest.pricing;

if (pricing?.pricing_model === "per_invocation") {
  const { units, currency } = pricing.unit_price;
  const plannedCalls = 40;
  const margin = 200; // minor units
  const totalBudget = units * plannedCalls + margin;

  console.log(`Quoted at ${units} ${currency} per call.`);
  console.log(`Planning budget of ${totalBudget} ${currency} for ${plannedCalls} calls.`);
}
plan_delegation.py
# `manifest` is the signed ToolDefinition fetched during tool discovery.
pricing = manifest.get("pricing")

if pricing and pricing["pricing_model"] == "per_invocation":
    units    = pricing["unit_price"]["units"]
    currency = pricing["unit_price"]["currency"]
    planned_calls = 40
    margin = 200
    total_budget = units * planned_calls + margin
    print(f"Quoted at {units} {currency} per call.")
    print(f"Planning budget of {total_budget} {currency} for {planned_calls} calls.")

Current Limitations

The policy YAML and HushSpec authoring path does not yet encode monetary default-capability issuance. Operators perform monetary planning in capability-issuance or authority code. Current flow:

  1. Publish pricing in the manifest.
  2. Read the manifest in operator or authority code.
  3. Issue a budgeted capability explicitly.

There is no YAML pricing-to-budget configuration yet. Use the three steps above until that feature ships.


Safety Notes

  • Keep pricing currency and grant currency identical until multi-currency support ships.
  • Size max_total_cost with HA overrun headroom in clustered deployments. Per-kernel budget state does not coordinate across replicas.
  • Require DPoP on spend-bearing grants so quoted authority stays bound to the intended subject.
  • Use manifest pricing as operator input. Receipts and the metered sidecar record billed usage.
  • When a tool may exceed the advisory quote, set the grant from the worst-case amount you are willing to authorize, not from the optimistic quote.

  • Budgets & Metering walks through the budget side of the same transaction.
  • Settlement covers how observed costs reconcile against quoted costs after the tool runs.
  • Economics is the conceptual overview of how money moves through chio.