Reference implementation · Apache-2.0 · Python 3.12+

If Harness is for one agent,
Rigging is for the fleet.

The typed, trust-bearing, schema-mediated coupling layer that composes heterogeneous harnessed agents into a single coherent system. Signed cards. Delegation contracts. Mechanical blame.

$ pip install rigging
  • 5packages
  • 97tests
  • 10ADRs
  • 4specs
  • 6examples
  • 0LLM code in core
A tall ship at night. The rigging is the load-bearing web that translates wind into motion.

“You can have a great harness on every agent and still have terrible rigging.
If you’re not the model, and you’re not the harness, you’re the rigging.”

CONCEPT.md

Why this layer needs a name

You are open-coding it already.

Before route_to_agent()

# prod-grade glue, every team writes it differently
trace_id = uuid()
contract = {"caller": "planner", "callee": callee}
try:
    out = await callee.run(req)
except Exception:
    out = await fallback.run(req)  # silent swap
cost[caller] += out.cost  # 🤞
return out

With Rigging

result = await rig.call(
    caller=planner,
    callee_did=worker.did,
    capability="translate_pdf",
    input={"uri": "s3://…"},
    cost_budget=("usd", "0.50"),
    verifier=quality.did,
)

Every multi-agent stack ships a route_to_agent that reinvents identity, contracts, budgets, and retries — incorrectly, inconsistently, and invisibly. Rigging makes the invisible layer a typed primitive.

The three primitives a rig refuses to live without

Cards. Contracts. Blame.

Signed agent cards, delegation contracts, blame chains
Read the long-form essay (≈2k words)

A rig is a runtime that does three things, and refuses to do anything else. It makes capability advertisements explicit. It makes delegation contracts explicit. It makes blame attribution mechanical. Everything else in a rig exists to keep those three primitives honest.

The most important discipline a rig enforces is refusing to silently fix things. A friendly system absorbs partial failure and gives the caller a working answer. A friendly system also destroys the chain of evidence that blame attribution depends on. Keep reading →

Interactive · click any scenario

Walk a real blame chain.

Pick a failure mode. The runtime emits a signed envelope per step; the extractor walks the DAG backwards until it finds the proximate cause. No guesswork.

Happy path 3 signed envelopes · verdict accept

    Interactive · press

    Watch a contract become a trace.

    Six steps. Each one is a checkpoint at which the rig can refuse. No animation magic — every box is a span the runtime actually emits.

    step 0/6

    Why budgets compose

    Sub-budgets are carved, not borrowed.

    Cost is a property of a contract, not of an agent. B may subcontract to C only by carving a sub-budget from B's own allocation. C's overruns hit B's ledger; A's budget is inviolable.

    Cost attribution waterfall — A → B → C

    The agentic version of letting a subcontractor put arbitrary charges on the general contractor's credit card is exactly what cost-as-an-agent-property allows. Rigging refuses it structurally. See ADR-0006.

    No edge labelled "silently retry"

    Every contract terminates explicitly.

    Contract state machine

    Terminal states are fulfilled, rejected, and voided. Retries are first-class events with their own contracts and their own identifiers, because a retry against a fresh callee produces output signed by an identity the caller did not address. See ADR-0009.

    Interactive · drag the sliders

    What does a runaway subcontractor cost?

    A → B → C. Each contract has its own carved sub-budget. C overspends. Move the sliders; watch where the overrun lands. A's budget is structurally inviolable — that is the entire point.

    The runtime debits against the contract that holds the spend. A's ledger never sees C's overrun. Try setting C beyond B's sub-budget to trigger BudgetOverrun.

    A's ledger (parent, top-level caller)

    budget$1.00

    committed to B$0.20

    remaining$0.80

    B's ledger (sub-budget carved from A)

    sub-budget$0.20

    spent on C$0.33

    statusoverrun

    BudgetOverrun raised against the B→C contract.
    A is unaffected. The blame chain points at C.

    Three teams, three problems

    Where a rig saves your weekend.

    Synthesised from real conversations with practitioners. Names and numbers redacted; the patterns are the patterns.

    finops

    The runaway subcontractor

    Before: A planner agent at a fintech ramped a research subtask onto a chain of three reasoning subagents. One agent silently spun off a sibling that re-issued the same query in a retry loop. $8,400 in token spend on one task before alerting fired.

    With rigging: The retry would have been a new contract, with its own signed identifier. The sibling's budget would have been a carved sub-allocation from the parent. The runaway would have hit BudgetOverrun after $0.50, not $8,400.

    incident-response

    The 3 AM "which agent broke it"

    Before: A code-review pipeline composed of four agents from three vendors auto-merged a regression. The post-mortem took six engineers eight hours to attribute fault — three of the agents were running in different repos, with non-aligned trace formats.

    With rigging: One trace. One blame chain. rig trace inspect would have named the reviewer agent whose verdict was wrong, with its signed envelope as the proof. Six engineer-hours back.

    compliance

    The "who said what" audit

    Before: A regulated-industry customer asked: "for last month's automated decisions, prove which agent made each call and under what budget." The team spent two weeks reconstructing it from logs.

    With rigging: Every decision is a signed contract; every output is a signed envelope; the trace is the audit. The same question becomes SELECT * FROM traces WHERE … against the OpenTelemetry backend.

    One-direction dependency graph

    Five small packages. No clever monolith.

    Five packages: rigging-core, rigging-identity, rigging-trace, rigging-adapters, rigging-runtime.
    • rigging-core — schemas · protocols · errors. No I/O. No crypto. No LLMs.
    • rigging-identity — the only crypto package. Ed25519, JCS, JWS, DIDs.
    • rigging-trace — OTel span processor + blame-chain extractor.
    • rigging-adapters — bridges to LiteLLM, MCP, local Python. <500 LOC.
    • rigging-runtime — the Rig orchestrator. Contract state machine. Verifier wiring.

    Rigging-Bench v0 · honest scoring

    Five axes. No 100%-everything theatre.

    Capability fidelity0.50

    Half honest, half dishonest agents — the floor is structural.

    Contract expressiveness1.00

    All four canonical patterns expressible.

    Identity propagation0.85

    Spoofing, tampering, wrong-key covered. Revocation is v1.

    Cost attribution1.00

    Zero L1 error on the synthetic chain.

    Blame resolution0.70

    Leaf attribution solid. Planner-misroutes & verifier-itself-wrong: v1.

    Overall0.81

    We name the gaps so they cannot be hidden in headline numbers.

    Where Rigging sits in the stack

    It is not MCP. It is not A2A. It is not a supervisor.

    MCP A2A Harness Supervisor
    (LangGraph, CrewAI)
    Rigging
    Tool wire format↗ reuses
    Agent-to-agent wire↗ reuses
    Single agent looppartial
    Multi-agent routing✓ typed
    Signed capability advertisementpartial
    Typed delegation contract
    Per-contract budget enforcementper agent✓ recursive
    Verifier as first-class participantconv.
    Cross-agent blame extraction
    Refuses silent retriesn/an/apolicypolicy✓ structural

    A rig uses MCP and A2A as wire formats. It sits one floor up from supervisor patterns. It does not replace your harness — it composes across harnesses.

    Why this layer needs a name in 2026

    The agent stack, named in chronological order.

    Timeline 2022 → 2026 — model · tools · MCP · A2A · rigging

    Every layer in this stack was open-coded by every team before it had a name. Kubernetes was the loop your ops engineer ran in tmux. MCP was a function called call_tool() in five styles. A2A was a hand-written REST shim. The layer that comes next — the typed coupling across trust domains — needs a name too.

    One screen of code

    What a rig run actually looks like.

    # build a rig and register three agents
    from rigging.runtime import Rig
    from rigging.adapters import LocalPythonAdapter
    from rigging.identity import KeyPair
    
    rig = Rig(name="my-system")
    rig.register(planner, keypair=planner_key)
    rig.register(worker,  keypair=worker_key)
    rig.register(quality, keypair=quality_key)
    
    # delegate with a signed, typed contract
    result = await rig.call(
        caller=planner,
        callee_did=worker.did,
        capability="translate_pdf",
        input={"uri": "s3://docs/contract.pdf",
               "target_language": "fr"},
        cost_budget=("usd", "0.50"),
        verifier=quality.did,
    )
    
    # export the trace + blame chain
    trace = rig.finish()
    trace.write("trace.json")

    No silent retries. No transparent fallback. No tribal knowledge. A typed exception names the responsible agent, every time.

    The art of this layer is in what it refuses

    Six refusals.

    The six refusals: cards, contracts, retries, cost, verification, scope

    FAQ

    The questions we get every week.

    Is this another wire protocol?

    No. Rigging sits above MCP and A2A. Tool calls inside a harness flow over MCP; the contract flows over A2A; the trace flows over OpenTelemetry. A rig that invents a new wire format is, by our definition, doing it wrong.

    Is the verifier privileged?

    No — and we tried that first. A verifier is just an agent whose card declares a verify capability. The runtime invariants apply to it uniformly. Disagreement becomes a composition problem (vote, recurse), not a runtime problem. See ADR-0007.

    Why no silent retries?

    A retry against a fresh agent produces output signed by an identity the caller did not address. The trace shows A → B but the work was done by B′. Blame analysis terminates in a contradiction. Retries are first-class events with their own contracts and their own identifiers.

    Why Ed25519 and not OIDC?

    v0 only needs to answer "is this card real and unchanged?" Long-lived per-agent Ed25519 keys solve that with no infrastructure. OAuth/OIDC is a v1 conversation.

    Does the rig know about LLMs?

    No. rigging-core and rigging-runtime contain zero LLM-specific code. All provider concerns live under rigging-adapters.

    How do you stop one caller from putting charges on another caller's card?

    Cost is a property of a contract, not of an agent. B may subcontract to C only by carving a sub-budget from its own allocation. C's overruns hit B's ledger; A's budget is inviolable. See ADR-0006.

    Will you ship a web dashboard?

    Not in v0. The TUI is sufficient. This page is the visual demo. A dedicated rigging-viz is on the roadmap.

    What is the name about?

    Maritime, not fraudulent. The rigging of a sailing ship is the load-bearing web that translates wind into motion. The sails do not move the ship. The hull does not move the ship. The rigging does.

    Curator

    Maintained by Betty Guo

    Dongxin (Betty) Guo

    PhD candidate, Department of Computer Science, The University of Hong Kong. Advised by Prof. Siu-Ming Yiu.

    Research interests: trust-bearing infrastructure for multi-agent systems, applied cryptography, and the systems substrate beneath modern AI agents. Rigging is the reference implementation of a concept she has been developing through her PhD.

    Build a system, not a stack of agents.

    Star it on GitHub. Run the four examples. File an issue. We answer.