Skip to main content

Agents as a service

An agent that only your own systems can call is an internal tool. The agent-to-agent surface is how an agent becomes something another party can discover, authenticate against, and delegate work to, under governance that holds across the boundary.

It runs in both directions, and the two halves are deliberately asymmetric.

DirectionWhat it is
InboundAnother party's agent discovers yours, authenticates, and submits a task
OutboundYour agent delegates to a registered external agent

The protocol​

The surface implements the open agent-to-agent specification over JSON-RPC, so a conformant client needs no System9-specific adapter.

EndpointPurposeAuthentication
The agent cardDiscovery. What this agent is, what it can do, and how to authenticateNone. Discovery is open
The key setThe public half of the card-signing key, so a peer can verifyNone
The identity bundleWorkload-identity trust materialNone
The task endpointEverything elseRequired

Methods served on the task endpoint:

MethodWhat it does
message/sendSubmit a task
message/streamSubmit and stream the result
tasks/getRead a task's state
tasks/cancelCancel a task
tasks/pushNotificationConfig/*Set, get, list and delete push-notification configuration
structures/query and structures/execA declared extension. See below

Errors use the specification's own codes: a task not found, a task not cancelable, push notifications unsupported, and a governance refusal in the server-defined range.

The card says only what is true​

An agent card advertises capabilities, and the temptation is to advertise generously. The implementation does the opposite: every capability flag defaults to off, and the deployment injects only what is actually wired.

The card is signed, with a detached signature, and the public half is published at the key-set endpoint so a peer can verify it independently. One signing identity backs both, which is what keeps the card and the verification material from ever disagreeing.

Until that identity resolves, the node serves an unsigned card and an empty key set rather than a card signed with something improvised.

The security schemes, and the one deliberately left out​

The card declares mutual TLS, an identity-provider flow, and an API key as accepted authentication. It also declares a per-message signature scheme, and deliberately leaves it out of the required list.

That is not an oversight. A per-message signature is defence in depth on top of the transport, not a transport requirement, and listing it as required would tell a peer it must do something the connection does not actually depend on.

Authenticating an inbound caller​

Discovery is open. Everything else requires a credential, and four verifiers are available:

CredentialResolved against
API keyThe platform's own key store
Identity-provider tokenThe identity providers an operator has actually enabled. None configured means the scheme stays off, fail-closed
Workload identity, in-nodeThis node's own issuer, so there is one key set and no split brain
Workload identity, peer bundleA peer trust domain whose key set an operator has pinned
Mutual TLSThe client certificate

Two trust modes:

  • Verified and permissive (the default). Any valid credential is accepted, and every task is still value-gated individually.
  • Allowlist only. The resolved foreign agent must be pre-registered. This is the sovereign posture.

The authenticated caller is mapped to a directory entity of agent kind, which is the move that makes everything downstream work: a foreign caller becomes a first-class actor on the audit chain rather than an anonymous request.

If the verifier chain cannot be built, the node serves a fail-closed refusal, not an open door.

What happens to an inbound task​

Figure AS1: an inbound task. The property to notice is at the first gate: the foreign caller is the actor the value gate evaluates, and that decision lands on the tamper-evident record before any work is admitted. Not after, and not attributed to the local node.

Four details worth drawing out.

The task state is the row. An admitted task becomes a job row, and reading the task projects that row. There is no separate status that could drift from what is actually happening.

Verification is against the published key set, not against whatever key signed the envelope. Checking a signature against its own signer is a tautology; checking it against what the node publishes is a real check. Only the published agent resolves; anything else resolves to nothing and is refused.

Tenancy is checked twice, and the second is not redundant. The receiver refuses a scope that is not resident on this cell. The handler then additionally requires the backing row's workspace to equal the resolved scope. The first answers "may this cell run work in that tenancy"; the second answers "is this the row that tenancy owns." An envelope naming a resident workspace but pointing at another tenant's task passes the first and must not pass the second.

A refusal is terminal. A refused task reaches a final state with a reason, so a caller is never left waiting on something that will never proceed.

Acceptance and concurrency are different quantities​

Inbound acceptance is governed as imposed resistance: how much externally originated work this node absorbs. Outbound concurrency is governed as utilisation, with back-pressure rather than refusal as it climbs.

Those are not the same thing and are deliberately not flattened onto one ceiling. Accepting work from outside is a decision about exposure; running your own work in parallel is a decision about capacity.

Metering​

Every inbound task is metered against the calling entity, on the same hash-chained record as the rest of the estate. Because the foreign caller was resolved to a directory entity at authentication, the consumption attributes to a principal rather than to a connection.

The data extension​

Beyond running a task, the surface declares one extension: governed access to the cell's data structures.

The mechanism matters more than the feature. The bridge forwards to the existing internal data surface as the foreign entity, over the system-token channel. The caller's identity is honoured only on a valid system token, so a self-asserted identity header is ignored.

The result is that the engine boundary evaluates permissions, data grants and classification as the external caller, not as the node. A missing caller identity is a hard refusal rather than falling back to system authority, which would be an escalation. The bridge reads grants and mints nothing.

Reaching other parties' agents​

The outbound half is a per-workspace registry of external agents you may delegate to.

Registering one fetches the peer's signed card, verifies it, and pins it on first use. Credentials are sealed by reference rather than stored. Registration and every mutation pass the value gate and are audited.

Card drift is detected. If a peer's card changes after pinning, the registration is marked drifted rather than silently followed. A peer that re-advertises itself with different capabilities or a different endpoint is a thing you should be told about.

Delegation itself is an agent tool declared consequential, so each call is value-gated at the moment it is made, not only when the peer was registered. Around the call: the pin is re-verified, credentials are unsealed, the result is checked for taint, egress leaves from a residency-correct cell, and the whole exchange lands on a symmetric hash-chained ledger.

How this differs from the tool surface​

These are two planes and it is worth being precise about which is which:

Serving toolsServing agents
ProtocolModel Context ProtocolAgent-to-agent
What the caller getsCapabilities to invokeA task someone else's agent performs
Who actsThe caller's own agentYour agent
SurfaceThe tool serverThe task endpoint

Serving tools means another agent uses your capabilities with its own reasoning. Serving agents means your agent does the reasoning. The governance differs accordingly: a tool call is gated per invocation, while an inbound task is gated once at admission and then runs under your agent's own authority and budget.

One agent per cell today

A cell publishes exactly one representative agent: one card, one endpoint, one signing identity. There is no per-agent address and no agent identifier in the request.

Multi-tenant agent hosting, where one cell serves several independently addressable agents to outside callers, is not the current shape. Plan a deployment around one served agent per cell.