Skip to content

The escalation proof: an injected agent cannot act as a write-capable definition #376

Description

@jhrozek

An injected parent asks to act as a write-capable definition, the scope shrinks between two shown
token payloads, the gateway refuses the write, and the twin holds: the operator running that
same definition top-level still succeeds. Without the twin you cannot distinguish "escalation
blocked" from "the definition is broken".

mecatl and ToolHive. The north star: file first, complete last. Needs #371, plus the ToolHive work below.

One agent proves nothing — a single-agent run never requests a second definition, so the
interesting step never executes and the demo passes whether or not it is guarded.

It follows test-sidecar-delegation.sh, which checks real HTTP status codes — not
run-demo.sh Act 3, whose delegation matrix is a hardcoded printf.

Where it runs, and how often

Settled: task e2e, live, and the cost is accepted. Lightweight integration tests are fine alongside
it but are not the proof.

Cadence: on merge to main, path-filtered to the identity and delegation paths. Chosen for the
tightest feedback and because a failure is attributed to a specific PR rather than to "something last
week." Accepted cost: live, paid API calls sit in the merge path, and the path filter is what keeps that
off merges that cannot affect this.

The cadence is written down here on purpose. task e2e is not part of task test, and "it's e2e"
decays into "it hasn't run in three months" — this is the one test whose silent death would be
invisible.

What it pins

The escalation being tested is definition-based authorization working: code-reviewer registered
read-only, deployer registered to write, the injected parent trying to cross. So this test is also
the regression guard for that decision, and it should carry a comment saying so — if instance-based
external authorization ever comes back onto the table, this is where the cost of the flip shows up
first.

That decision is now better supported than when it was made. No IETF draft ties consent or
authorization to a running instance: McGuinness's agent_instance_id exists for attestation and audit
while his draft rules consent out of scope on purpose, the expired WSO2 draft names a
requested_actor which is a definition, and Liu et al.'s consent evidence keys on act.sub with
instance correlation as an optional audit add-on. Instance-based external authorization has no
counterparty specification today.

ToolHive dependencies

  • Scope intersection against the subject token is what the shrinking payloads are, and it is not
    filed there. File it before this proof can be demoed.
  • actor_token support (toolhive#5815) is a
    hard prerequisite: call 3 sends actor_token, and the server rejects it outright today
    (pkg/authserver/server/tokenexchange/handler.go:296-301, "not yet supported").
  • A consent model for external OIDC subject tokens
    (toolhive#5989) is how call 3 accepts a real IdP
    token at all.

Without the last two, the token payloads this proof shows shrinking between never get minted.

Reasoning

The design this comes from, both on main:

Every hazard above is stated in full here rather than linked, so this issue stands on its own.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions