Dispatch
How a request travels from your code through identity middleware into the engine pipeline and out as an audited response.
This page is the conceptual narrative. For the strict implementation, see the engine pipeline.
You call
Your code — Rust SDK, TypeScript SDK, CLI, MCP client, raw HTTP — produces a HandleRequestInput describing one operation against one protocol.
await map.dispatch({
protocol: 'MARC',
version: 'v1.0.0',
operation: 'reasoning_task',
input: { intent: 'is this treaty enforceable?', budget: { tokens: 8000, deadline_ms: 12000 } },
tenant_id: 'org_acme'
});The gateway authenticates
services/gateway/ validates the Authorization bearer or session cookie, extracts the caller DID, and builds a base InvokeContext. The traceparent header is propagated; a fresh correlation_id is minted if none was supplied.
Identity middleware enriches
IdentityMiddleware::enrich calls the bound resolver (OasResolver in prod) and attaches the ResolvedAgentIdentity to the context. Failed resolution falls back to ResolvedAgentIdentity::unresolved(did) — the request still flows, but typically denies at security gating.
The engine takes over
MapEngine::handle_request runs eight stages in order:
- Version Resolution — the registry picks the best installed version
- Context Enrichment — tenant context, correlation, timestamp
- Rate Limiting — token bucket per tenant
- Security Gating — hierarchical capability check
- Circuit Breaking — is the target endpoint healthy?
- Load Balancing — pick an endpoint
- Router Invocation — call
ProtocolModule::invoke - Result Handling — audit + metric
Each stage can refuse. Refusals carry reasons and are audited the same as success.
The protocol module reasons (or doesn't)
The protocol crate's invoke method matches on the operation and runs the actual logic. For a protocol like MAX this is fast and stateless. For an agent like MARC this may consult MIND, attest sources with MAVEN, weight evidence with MARE, and return a derivation tree with a confidence band.
Throughout, the protocol can read the resolved identity via ctx.identity(), check capabilities, record its own audit events, and recursively dispatch other protocols.
Metering and audit close the loop
MEAL::meter charges the call against three dimensions (tokens, seconds, watts). MAX::audit_log_entry records the decision and outcome. MOTET::emit writes traces. The response flows back through the gateway with the audit-chain head hash in the metadata so you can verify the record later.
Refusals carry reasons
If the request is denied at any stage, you receive a structured response — not a 500. Examples:
- Rate limit: HTTP 429 with
Retry-After, errorRateLimited, audit eventRateLimitExceeded - Capability missing: HTTP 403, error
CapabilityDeniedwith the requested capability name - Policy block: HTTP 422, error
ProtocolError(PolicyDenied { reason }) - Cross-org without treaty: HTTP 403, error
CapabilityDenied, audit event includes "no active MOAT treaty"
This is by design. The institution does not say "no" without saying why. Every refusal is a first-class record.