Shield Security

Shield sits between an agent and the things it can change, and answers one question per call: allow, refuse, or stop and ask a human. Every answer is recorded with an identifier you can quote later. This page is explanation and reference: what Shield is for, what each part of it does, what the API contract looks like, and, stated as plainly as the rest, what Shield does not do.

If you are wiring an integration, the contract you need is in §13. If you are deciding whether to turn enforcement on, read §3 and §15 first.

1. What Shield is for

An agent with tools is an agent that can act. The useful version of that sentence is "it can file the ticket for you"; the dangerous version is "it can revoke a key, publish to a shared corpus, or transfer ownership, because a web page it read told it to". The gap between the two is not intelligence. It is authorisation.

Shield addresses a specific threat, and it is worth naming precisely, because it is not the threat most security pages describe. It is not primarily about an attacker stealing a credential. It is about a legitimately authenticated agent being induced to take an action nobody authorised, by injected instructions in retrieved content, by a compromised or swapped third-party tool server, or simply by a model deciding on its own that a destructive step was the helpful one.

Conventional authorisation cannot see that. An API key is valid or it is not, and a valid key is valid for every call it can make. Shield adds three things a key cannot carry:

  • What class of thing is this call? Reading is not writing; writing to your own tenant is not publishing to a shared one.
  • Where did the instruction come from? Content pulled off the open web is not the same input as something a human explicitly confirmed.
  • Is this specific tool, key or server still trusted right now? Trust granted last month is a claim about last month.

Everything below is a mechanism for asking one of those three questions, plus a way for a human to answer when the machine should not.

2. Where Shield sits

Shield evaluates on two surfaces, with the same decision logic behind both.

SurfaceWhat is evaluatedHow a refusal arrives
MCP tool callsEvery tool invocation, before the tool runsA JSON-RPC error, see §13
HTTP mutation routesState-changing calls on the platform API, ingest, proof, economy, delegation, org and the restAn error envelope with an HTTP status, see §13

Two properties of that placement matter.

It is a pre-execution gate, not an audit after the fact. The decision is made before the tool is invoked or the mutation is applied. A refusal means nothing happened.

Read paths are largely untouched. Shield's job is actions with consequences. Retrieval and other read-only calls are governed by tenancy and capability scoping rather than by Shield's approval and kill-switch machinery, which is why turning enforcement on does not degrade your read latency story.

Shield administration itself is a set of tenant-scoped routes under /v1/shield/. A request whose credential carries no tenant gets 401 AuthMissing; a request naming a tenant that disagrees with its credential gets 403 Forbidden with Tenant mismatch for shield request, the mismatch is refused rather than reconciled. Mutating Shield configuration requires an owner or admin seat where the deployment requires seat-based authorisation; otherwise tenant-scoped API credentials are accepted as tenant-admin equivalents so that operational automation keeps working.

3. Observe and enforce

Shield has two modes, and the mode is the single most important thing to know about any given deployment.

ModeWhat happens on a policy hitWhat you get
observeThe decision is computed and recorded. The call proceeds.A complete picture of what would have been blocked, with zero risk of breaking a working integration
enforceThe decision is computed, recorded, and acted onActual refusals

GET /v1/shield/capabilities returns the current mode alongside the capability manifests, so a client can always discover which regime it is operating under rather than inferring it from behaviour.

Three consequences follow, and all three are the kind of thing that surprises people.

Observe mode is the default posture, and it blocks nothing. A deployment that has never been promoted is running a very good monitoring system and no enforcement at all. Do not read "Shield is enabled" as "Shield is stopping things".

Approval gating is a second, separate switch. A deployment can be in enforce mode with approval enforcement still off. In that configuration a call that requires human approval is recorded as requiring it, and then proceeds. If your control story depends on the approval gate, verify that specific switch, not just the mode.

When Shield is switched off entirely for a deployment, it still records. Every call is allowed, and the decision is written with a marker saying Shield was disabled, plus a corresponding event. The off state is legible in the record rather than an unexplained gap in it. That is deliberate: an evidence trail with a silent hole is worse than no evidence trail, because the hole looks like inactivity.

The intended promotion path is therefore: run in observe, read the recorded decisions for a representative window, fix the policies that would have produced false refusals, then promote. Shield is designed to make that first step free.

4. The capability model

Every tool Shield knows about has a manifest, a declaration of what kind of thing that tool is. Manifests are readable at GET /v1/shield/capabilities, and each one carries:

FieldMeaning
nameThe tool the manifest governs
summaryOne line of human-readable description
capability_classWhat category of action this is
risk_tierlow, med, high or extreme
requires_approvalWhether a human must approve before this runs
taint_policy.block_taintedWhether tainted input is refused for this tool
taint_policy.require_user_confirmedWhether this tool needs explicitly user-confirmed input

The capability classes are a published vocabulary. They are the coarse answer to "what kind of thing is this call":

ClassCovers
READ_ONLYReads that touch nothing sensitive
READ_PRIVATEReads of tenant-private material
WRITE_PRIVATEWrites into the tenant's own space
PROOFReceipt and verification operations
PUBLISH_COMMONSPublishing into shared, cross-tenant material
ECONOMY_MUTATEAnything that moves credits or billing state
DELEGATE_ACCESSGranting another principal the right to act
SELF_SIGNUPAgent self-registration
FEEDBACKFeedback and signal submission
DANGEROUS_LOCALActions that reach the operator's own machine

Two behaviours attach to this model that you should know before you rely on it.

Undeclared tools are refused under enforcement. A tool call with no manifest is denied in enforce mode and allowed in observe mode. Shield is deny-by-default for things it has never been told about, which also means an incomplete manifest set becomes an outage the moment you promote. Enumerate your tools during the observe window; that is what the window is for.

DANGEROUS_LOCAL is refused outright in a production deployment, in either mode. The hosted plane will not execute a tool declared as reaching the operator's local machine, and this refusal is not a policy you can configure away from the tenant side. It is a property of running hosted rather than local.

5. Taint

Taint is Shield's answer to "where did this instruction come from". Every evaluated call may carry one of four labels:

LabelMeans
TAINTEDDerived from untrusted content, retrieved documents, third-party output, anything an attacker could have written
USER_CONFIRMEDA human explicitly confirmed this specific action
SYSTEM_GENERATEDOriginated inside the platform, not from external content
TRUSTED_SERVERCame from a tool server on the trust registry

A manifest declares how its tool reacts. block_tainted refuses tainted input outright. require_user_confirmed demands an explicit human confirmation regardless of how plausible the request looks. The combination is what stops the classic prompt-injection chain: a document says "now revoke the admin key", the agent believes it, the call arrives labelled TAINTED, and the tool that revokes keys declares that it does not accept tainted input.

Be clear about the boundary here. Shield enforces the label; it does not compute it. The caller supplies the taint label. Shield's guarantee is that a declared label is honoured consistently, recorded, and cannot be overridden by the tool. It is not a guarantee that untrusted content is detected for you. An integration that labels everything SYSTEM_GENERATED has, in effect, turned taint policy off, and Shield cannot tell.

6. The trust registry

The trust registry answers "is this tool server the one I approved". It has two levels, and the separation is the point.

Publishers are the entities you trust. Each carries a display_name, an identity_type, sigstore, gpg, org_domain or manual, an identity_ref for that identity, a trust_level of low, medium or high, and a status of active, suspended or revoked. Managed at GET and POST /v1/shield/trust/publishers.

Digests are the specific builds you trust. A trusted digest binds a server_name and a content digest to a publisher, and carries its own risk_tier, the list of capabilities that build is permitted, whether sampling_allowed and ui_apps_allowed, a status of active, quarantined or revoked, and a free-form provenance record. Managed at GET and POST /v1/shield/trust/digests.

Digest pinning is what makes the registry mean anything. Trusting a publisher is trusting a name; a name can ship a new build tomorrow. Trusting a digest is trusting an exact artefact. When a server presents a digest that is not on the list, Shield refuses with DigestUntrusted, a supply-chain swap therefore fails closed rather than inheriting yesterday's trust.

Revocation is immediate and is its own answer. POST /v1/shield/trust/digests/revoke takes the digest, a reason and a revokedBy, and from that moment a call presenting that digest is refused with TrustRevoked and HTTP 451, a deliberately distinct status from a generic policy block, so a client can tell "this used to be allowed and has been withdrawn" from "this was never allowed".

Alongside the registry, GET and POST /v1/shield/servers and POST /v1/shield/servers/{serverId}/status track the lifecycle of managed tool servers through installed, upgrading, quarantined, revoked and disabled. quarantined is the useful middle state: the server stays known and its history stays intact while it stops being usable.

7. The approval workflow

Some actions should not be decided by a machine at all. A manifest with requires_approval set routes those to a human, and this is what actually happens.

1. The call is attempted and stopped. The agent's call returns a refusal carrying an approvalRequestId and a decisionId. Nothing has been executed.

2. An approval request exists and is reviewable. GET /v1/shield/approvals/{approvalRequestId} returns the record: the tool_name, the capability_class, the risk_tier, the taint_label the call arrived with, the proposer, the payload that was proposed, and the current state. That is what a human is actually looking at, not "an agent wants to do something", but this tool, this classification, this input, from this source, with this provenance. A request can also be raised ahead of time with POST /v1/shield/approvals/propose.

3. A human resolves it. POST /v1/shield/approvals/{approvalRequestId}/resolve takes a decision of approved, denied or expired, plus an optional resolver and reason. The reason is worth supplying; it is the part of the record that explains itself six months later.

4. Approval becomes a token, and the token is spent. An approved request yields an approval token. The caller replays the original action presenting it, as the x-shield-approval-token header on HTTP, or as an approval_token parameter on MCP, and the action proceeds.

The token's properties are the important part:

  • It is single-use. Once redeemed the request moves to consumed and the token will not work again. An approval authorises an action, not a standing permission.
  • It expires. The approval record carries a token_expires_at; read it rather than assuming a duration. Lifetimes are measured in minutes, are deployment-configurable, and are not a guarantee.
  • The lifecycle is a closed enum: requested, approved, denied, expired, consumed. There is no state in which an approval quietly lapses into permission.

Approval requests and their resolutions are both emitted as events, so "who approved what, when, and why" is answerable from the event stream rather than from a screenshot.

8. Sandboxing

Sandbox policy applies to tools in the high and extreme risk tiers. For those, Shield evaluates the declared execution context against the policy recorded for that server: which filesystem roots the server may touch and whether each is read-only or read-write, and whether the network egress it is attempting is on the allowed list. A breach produces a SandboxViolation refusal carrying a violationId, and emits a sandbox-violation event.

Say plainly what this is. It is a policy check against a declared context, evaluated before execution. It is not a container, a seccomp profile or a kernel confinement, and it does not itself constrain a process that is already running. Its value is that a high-risk tool must declare what it intends to touch, and a declaration outside the agreed envelope is refused. Its limit is that it is only as good as the declaration; it constrains what the platform will invoke on your behalf, not what a compromised process could attempt on its own.

Lower-risk tiers are not sandbox-evaluated at all. If you want a tool sandbox-checked, its manifest has to place it in a tier that gets checked.

9. Kill switches

A kill switch is the control you reach for when you do not yet know what is wrong, only that something is. It is deliberately blunt.

POST /v1/shield/kill-switches takes a targetType, a targetRef, a reason, an actor and an optional expiresAt. Five target types are supported:

targetTypeStops
agent_keyEvery action by one agent credential
toolOne named tool, for every agent in the tenant
server_digestEvery call attributed to one specific server build
sampling_globalAll model-sampling by third-party servers, tenant-wide
cross_tenant_purchaseCross-tenant purchase flows

GET /v1/shield/kill-switches lists what is active. DELETE /v1/shield/kill-switches/{killSwitchId} lifts one, taking a reason and an actor so that the lift is as attributable as the activation.

How fast it takes effect: on the next evaluated call. There is no cached allow-list to expire and no propagation delay to wait out, the switch is consulted during evaluation, so the first call after you create it is already refused, with KillSwitchActive and HTTP 412. There is no restart, no redeploy and no configuration reload.

How fast it does not take effect: on work already in flight. A kill switch is a gate on new actions. A call that already passed evaluation is not recalled, and a long-running job started before the switch is not interrupted. It stops the next thing, not the current thing.

Both activation and lifting emit an event, and an expiresAt lets you set a containment window that closes itself rather than being forgotten.

10. Sampling and UI integrity

Two narrower controls, included because their absence would be a gap in the story.

Sampling policies govern third-party tool servers asking the platform to run model inference on their behalf, a capability worth controlling, because it spends your budget on someone else's prompt. GET and POST /v1/shield/sampling-policies manage a per-digest policy carrying enabled, a tokenBudget, requiresApproval and a redactionPolicy. Consumption is metered against the budget and audited per request. The sampling_global kill switch is the emergency stop for the whole category.

UI integrity sessions bind a rendered surface to what it claims to be showing. GET and POST /v1/shield/ui-integrity-sessions manage a session carrying a requestId, a payloadHash, a serverDigest and an expiresAt. The purpose is to make an interface supplied by a third-party server accountable for the payload it displayed, so that "the user approved it" can be tied to what the user was actually shown.

11. Incident drills

GET and POST /v1/shield/incident-drills record a rehearsal: a drillType, a status of planned, running, completed or failed, a summary, findings, and start and completion timestamps.

This is a record, not an engine. It runs nothing and simulates nothing. Its value is that "we have a kill switch" is a claim, while "we exercised the kill switch on this date, it took effect on the next call, here is the finding we wrote up and here are the decisions recorded during the window" is evidence. A reviewer asking whether your containment works is asking for the second thing, and drills are where you keep it.

The dry-run companion is POST /v1/shield/evaluate-hypothetical, which evaluates a tool call, optionally with a given serverDigest, publisherId, taintLabel and isMutation, and returns the decision without executing anything and without side effects. Use it to test a policy change before promoting a mode, or to answer "would this be blocked" without finding out the expensive way.

12. The decision record

Every evaluation produces a decision, whether it allowed or refused, and every decision carries:

FieldMeaning
decision_idThe identifier quoted back on refusal, and the join key for everything else
actionallow, deny or approval_required
reason_codeWhich policy family produced the outcome
modeobserve or enforce at the time of the decision
tool_nameWhat was being attempted
capability_class, risk_tier, taint_labelThe classification the decision was made on
requires_approvalWhether the manifest demanded a human
approval_request_idPresent when an approval was raised
detailsPolicy-specific context, including the signals a decision in observe mode would have acted on

Decisions are also emitted as events, alongside events for trust changes, approval requests and resolutions, taint blocks, sandbox violations and kill-switch toggles. Combined with the platform's receipt surface, that gives two independent records with different properties: the Shield decision record says what was permitted and why; a receipt is a verifiable record of what was stored and retrieved. They answer different questions and neither substitutes for the other.

Recording is unconditional. Allowed calls are recorded. Calls evaluated in observe mode are recorded, with the policies they tripped. Calls made while Shield was switched off are recorded as such. The volume is the price of not having gaps.

13. The error contract

Publish this to your integrators; it is the part they will need at three in the morning. All responses use the standard envelope, { ok: false, error: { type, message }, meta }, and every Shield refusal carries a decisionId you can quote when asking why.

13.1 HTTP

SituationStatuserror.typeExtra fields
A human must approve first409ApprovalRequiredapprovalRequestId, decisionId
A kill switch matches412KillSwitchActivedecisionId
Sandbox policy breach412SandboxViolationviolationId, decisionId
Taint policy refusal412DeniedTaintdecisionId
Trust for this digest was revoked451TrustRevokeddecisionId
Digest is not on the trust registry412DigestUntrusteddecisionId
Capability policy refusal412DeniedCapabilitydecisionId
Any other policy refusal412PolicyBlockedpolicy

13.2 MCP

SituationJSON-RPC codedata
Denied-32003type (one of the seven above), decisionId, reasonCode
Approval required-32010type: "ApprovalRequired", decisionId, approvalRequestId, reasonCode

13.3 How a client should behave

  • None of these are retryable. A Shield refusal is a decision, not a transient failure. Retrying the identical call produces the identical refusal and one more decision record. Only RateLimited and upstream failures are recoverable error types on this API.
  • 409 ApprovalRequired is the one to handle properly. Surface the approvalRequestId to a human, wait, then replay the original call with the approval token. Do not poll the original endpoint.
  • 451 TrustRevoked deserves an alert, not a log line. It means something you previously trusted has been withdrawn while you were using it.
  • Log the decisionId. It is the only handle that ties your failed call to the record explaining it.

14. Operating it

The sequence, in order:

  1. Enumerate your tools. Anything without a manifest is invisible in observe and refused in enforce.
  2. Register publishers, then pin digests. Publisher-level trust alone leaves you accepting whatever that publisher ships next.
  3. Run in observe for a representative window. A week that contains your month-end job is worth more than a month that does not.
  4. Read the decisions. Every one that would have been denied is either a policy bug or a genuine finding. Resolve each before promoting.
  5. Promote to enforce, and check separately whether approval gating is on, because it is a different switch.
  6. Drill the kill switch and record the drill. An untested containment control is a claim.
  7. Keep evaluate-hypothetical in your change process so policy edits are tested before they are live.

15. What Shield does NOT do

Stated as plainly as everything above, because a control plane whose limits are unstated is worse than one with fewer features.

  • It does not enforce anything in observe mode. This is the default. Nothing is blocked; everything is recorded.
  • It does not enforce approvals just because it is in enforce mode. Approval gating is a separate switch and can be off while the mode is on.
  • It does not enforce at all when switched off for the deployment. Calls are allowed and marked as such in the record.
  • It does not detect taint. It enforces the label the caller supplies. Mislabel your inputs and taint policy is decorative.
  • It does not verify a signature or attest a build. The trust registry records what you declared you trust, an identity reference and a digest. Establishing that the identity is genuine and that the digest matches the artefact you audited happens before the record is written, and Shield takes your word for it.
  • It does not confine a running process. Sandbox policy is a pre-execution check on a declared execution context, and only for high and extreme risk tiers. It is not a container boundary.
  • It does not interrupt work already in flight. A kill switch stops the next evaluated call; it does not recall a call that already passed, or terminate a job already running.
  • It does not govern read paths. Retrieval and read-only calls are governed by tenancy and capability scoping, not by Shield's approval and kill-switch machinery.
  • It does not prove that an agent did or did not do something. A decision record shows what Shield permitted and why. What the agent then did with the permission is a separate question, answered, partially, by the receipt surface.
  • It does not carry a latency or availability commitment. Nothing on this page is an SLA. Evaluation happens inline with your call; treat it as work your request pays for.
  • It does not run your incident drills. It records them.
  • It is not a substitute for least privilege. Shield refuses calls a credential was technically allowed to make. Not issuing the credential in the first place remains strictly better.