Amr, Jijie,

Thanks. This helps, particularly the boundary definition.

On placement, I mean your reading (i), with one qualification.

For an in-process tool, if the host function table is the first boundary
that can actually admit or refuse the consequential invocation, then that
function-table boundary is the enforcement boundary. No network hop is
required.

I would phrase the general rule as:

The relevant boundary is the first point whose successful admission is
necessary for the effect to occur.

So if a host wrapper validates the AIC and the protected function cannot be
reached except through that wrapper, the wrapper can be the boundary. If
the same function can still be invoked through another local path that
bypasses the wrapper, then the wrapper is only upstream defence-in-depth
and does not satisfy the property by itself.

That is the placement point I was trying to express. “Boundary” should be
defined by effectuation dependency, not by whether a socket exists.

On actionClass, I think there is one distinction worth preserving.

I am happy to use r / w / x as the vocabulary for the action-class axis,
but I would not make it synonymous with the current AIC named-tool
allowlist.

For example:

AIC:
tools = ["read_file", "list_dir"]

establishes a tool-identity predicate.

Separately:

read_file -> r
list_dir  -> r
bash      -> x

establishes an action-class predicate.

And separately again:

read_file:
    path-prefix = /tmp/

would establish an argument-subset predicate if such a field were actually
bound in the certificate.

Those predicates answer different questions.

read_file("/etc/shadow") can therefore simultaneously be:

   - permitted by tool identity;
   - classed as r;
   - not rejectable on path subset under the present vectors, because no
   path constraint is bound.

That last point is what I meant by saying the current named-tool
authorization behaves as a class grant for that tool. I was not intending
to collapse tool membership into the r/w/x consequence classification.

So for the write-up I would now keep the predicates explicit:

   1. tool identity;
   2. action class;
   3. argument subset, where present; and
   4. enforcement placement / necessary effect boundary.

Then a denial can state exactly which predicate failed rather than using
“policy denial” for all four.

And yes, I agree that for a purely local tool the absence of a network
credential boundary should not mean that no admissible enforcement boundary
exists. If the protected local dispatch is where the operation becomes
executable, that can be the boundary.

That definition also makes the bypass test straightforward: if another path
can produce the same effect without crossing the verifier, the claimed
boundary is not yet load-bearing.

Best,

Sangam




On Wed, 2 Sep 2026 at 5:36 PM, Amr Hassan <[email protected]> wrote:

> Jijie, Sangam,
>
> Sangam's two predicates are, I think, the right decomposition, and I want
> to
> supply vocabulary for the first one and disagree slightly about the second.
>
> draft-hamr-oauth-agent-delegation-01 posted to the Datatracker on
> 2026-09-02.
> It occupies the delegation-chain-and-floors layer, not the per-call binding
> layer, and it cites draft-das-agentic-tool-binding as the latter. I have
> not
> run scenario-demo.py either, and I make no claim about the AIC
> implementation.
>
> 1. On "effectively a class grant for the named tool"
>
> That is exactly the object my draft calls an action class floor, and I
> would
> rather we converge on one word for it than each keep our own.
>
> Section "Action Class Floors" registers actionClass as an ordered
> enumeration
> r < w < x, where r is read-only, w is an idempotent write, and x is a
> consequential non-idempotent action. A link's value is a CEILING on the
> whole
> link, not a statement about any single operation — which is precisely
> Sangam's "class grant" property, including its weakness: read_file with
> path=/etc/shadow satisfies the clas path=/tmp/a
> does. The floor is evaluated once when a link is issued or verified. It
> does
> not narrow per invocation and it is
>
> Mapping your four-row matrix onto ieresting row
> is not bash:
>
>   read_file, list_dir   r
>   bash                  x
>   delete_file           w or x, depending on who is asked
>
> delete_file repeated has the same effect as performing it once, so the
> idempotence test in my draft classecall it x.
> That disagreement is the reason my draft carries a second axis,
> classSource,
> ranked method < declared: a verifies from the
> method (cheap, lossy) or require it to have been declared by the issuer
> (expensive, exact). Your certificats the
> declared case. A gateway inferring a class from the JSON-RPC method name is
> the method case. They are not inter a chain say
> which it permits.
>
> If that is useful, take the enumeration; it costs you nothing and it stops
> tool-identity denial and argument-sin the same
> words, which is the conflation Sangam asked to avoid.
>
> 2. On enforcement placement — where I do not yet agree
>
> Sangam: "only the first enforcement point moves earlier." Jijie: a host
> wrap
> before dispatch is the natural extees.
>
> My -01 subsection "Verifier Placemeears to. It
> says classification and admission are performed by the verifier at the
> resource or credential boundary, notion layer
> upstream of it; that a harness coordinating an agent's actions does not
> itself admit a request; and that ann action it
> expects to be refused does not, by that restraint, satisfy the section.
>
> I wrote that to close a specific failure: a system that reports compliance
> because the harness was told about e boundary
> would have permitted the request had it arrived. That failure is real and I
> am not withdrawing the requirement.
>
> But your case is not that case, andish them.
> Two readings, and I cannot tell from my own text which one it licenses:
>
>   (i) For an in-process tool with no gateway, the host's function table IS
>       the resource boundary. The ho and there is
>       no disagreement between us at all.
>
>   (ii) The host wrap is upstream of the boundary. It is then defense in
>        depth, welcome but not admisstill has to
>        happen — which for a purely local tool means it never happens.
>
> If (i), my section needs a sentence saying so, because a reader will assume
> "boundary" means "network boundary"strict for
> local dispatch and I would like to know that before -02.
>
> I lean toward (i): the boundary is defined by what refuses, not by whether
> a
> socket is involved. Tell me if thatthe
> credential is transport-identity anchored and a local path may present no
> AIC-JWT at all. That is the sharpesd I think
> it is Sangam's placement point restated as a definition problem rather than
> a deployment one.
>
> 3. What I would keep in the write-u
>
> Sangam's suggestion to keep the fouh predicates,
> plus one line naming which predicate each denial exercises. bash is a
> class denial. read_file path=/etc/sbset denial,
> and the current vectors cannot produce it — which is a property of the
> vectors, worth stating rather than
>
> Amr
> draft-hamr-oauth-agent-delegation-0
> On 9/2/26 7:44 AM, Sangam Das wrote:
>
> Hi Jijie,
>
> Thank you. I will treat those payloads as the normative std/mcp-v1
> vectors for this thread.
>
> Certificate parameters:
>
> {"tools":["read_file","list_dir"]}
>
> JSON-RPC decision Result
> tools/call read_file ALLOW (200)
> tools/call list_dir ALLOW (200)
> tools/call bash DENY (403)
> tools/call delete_file DENY (403)
> initialize / ping / tools/list No tools/call decision (200)
>
> This correctly exercises the first predicate: tool identity is bound by
> the AIC rather than by the model emission itself. bash is therefore not
> authorized simply because the model emits it; it falls outside the
> principal-signed allowlist. Fail-closed enforcement at gateway-http is
> the appropriate sink behaviour whenever the AIC-JWT is presented.
>
> Two additional predicates are not yet exercised by the current matrix and,
> in my view, should remain explicit.
>
>    1. *Argument subset within an allowlisted tool.*
>    Under the current std/mcp-v1 vectors, read_file with path=/etc/shadow
>    would still satisfy the tool-name predicate because the certificate
>    constrains the tool name, not its arguments. The wallet and database
>    examples in the same repository already demonstrate field-level
>    restrictions such as recipient, table, and limit. If MCP later binds path
>    prefixes or other argument fields in the same way, the enforcement model
>    remains unchanged; only the set of certificate-bound fields expands. Until
>    then, the tool allowlist is effectively a class grant for the named tool.
>    2. *Enforcement placement.*
>    The same matcher can be applied earlier in the execution path, for
>    example at host invoke(name, args) before dispatch reaches gateway-http.
>    This is not a separate policy model: the certificate, bounds, and
>    comparison semantics remain the same; only the first enforcement point
>    moves earlier.
>
> I have not yet run scenario-demo.py --scenario mcp on this host. I will
> report only if the observed behaviour diverges from the specified 200/403
> matrix.
>
> For the write-up, I suggest keeping the four-row decision table together
> with these two predicates so that tool-identity denial is not conflated
> with argument-subset denial.
>
> Best,
>
> Sangam
>
> On Wed, 2 Sept 2026 at 11:10, Jijie Wei <[email protected]> wrote:
>
>> Sangam,
>>
>> Thank you for the precise read -- and for framing the in-process path as
>> a placement question rather than a defect. I agree: gateway-http enforces
>> every call that crosses it; a host wrap that invokes the same bound-check
>> before dispatch is the natural extension for local function tables, and it
>> reuses the same certificate and bounds.
>>
>> On the mapping offer -- yes, please. A concrete test vector against
>> std/mcp-v1:
>>
>> Allowlist (from the AIC certificate parameters):
>> {"tools":["read_file","list_dir"]}
>>
>> Expected ALLOW:
>> {"jsonrpc":"2.0","id":1,"method":"tools/call",
>>  "params":{"name":"read_file","arguments":{"path":"/tmp/a.txt"}}}
>>
>> Expected DENY (fail-closed, tool not allowlisted):
>> {"jsonrpc":"2.0","id":2,"method":"tools/call",
>>  "params":{"name":"bash","arguments":{"cmd":"rm -rf /"}}}
>>
>> Protocol methods (initialize/ping/tools/list) pass without a tools/call
>> decision. The repo's scenario-demo.py --scenario mcp runs these against
>> the live gateway and reports the matrix (read_file 200, bash 403,
>> list_dir 200, initialize 200, delete_file 403).
>>
>> If you run it and your result differs, that is exactly the kind of
>> divergence worth pinning down. Happy to align on whatever format you
>> prefer.
>>
>> Best,
>> Jijie Wei
>>
>>
>> ------------------ Original ------------------
>> *From: * "Sangam Das"<[email protected]>;
>> *Date: * Wed, Sep 2, 2026 01:33 PM
>> *To: * "Jijie Wei"<[email protected]>;
>> *Cc: * "wimse"<[email protected]>; "oauth"<[email protected]>;
>> *Subject: * Re: [WIMSE] Re: Binding OAuth-authorized calls to specific
>> tool-call arguments in agentic/MCP flows
>>
>>
>> Dear Jijie,
>>
>> Thank you for the pointer to the demo repo, and for stating the boundary
>> so clearly.
>>
>> Your three points match what I see in aic-capability-demo:
>>
>>    1. Bounds come from the principal (and role intersection at the CA),
>>    not from the model’s emitted arguments.
>>    2. The HTTP/MCP gateway checks the concrete parameters against those
>>    bounds per request and fails closed.
>>    3. That enforcement is at the gateway / MCP endpoint, not inside the
>>    host’s first local dispatch.
>>
>> That is a real answer for every call that actually crosses gateway-http.
>> The wallet / database / deploy / MCP matrices in the README are the right
>> kind of evidence for this thread.
>>
>> I agree AIC composes with RAR and mission-bound authorization rather than
>> replacing them: the certificate is the offline-verifiable assertion; an
>> online decision can still narrow it.
>>
>> The remaining case is the one you already named — the in-process function
>> table (or any other path that never presents the AIC-JWT to the gateway).
>> On that path the host would need to invoke the same bound-check before
>> invoke(), or it is an alternate route around the sink you implemented.
>>
>> I do not read that as a defect in AIC. It is a placement question:
>> gateway for networked MCP/HTTP, host wrap for local dispatch.
>>
>> I will look through the repo in more detail. Happy to map a single MCP
>> tools/call against your std/mcp-v1 allowlist if that is useful.
>>
>> Thanks again, Sangam
>>
>> On Wed, 2 Sept 2026 at 10:56, Jijie Wei <[email protected]> wrote:
>>
>>> Sangam, you asked whether this gap is already solved somewhere. We have
>>> implemented an answer in the AIC work and it is running; details and test
>>> matrices are in the public demo repo below.
>>>
>>> The pattern, in the vocabulary this thread has converged on:
>>>
>>> 1. The policy assertion is certificate-bound and independent of the model
>>>    output. A principal signs a DelegationAuthorization carrying
>>> capability
>>>    declarations with precise parameters (database: tables, columns,
>>>    row_filter, limit; wallet: assets, per-transaction amount, recipient
>>>    allowlist; MCP: tool allowlist). These are encoded in an X.509 agent
>>>    certificate and its JWT carrier. The authorized bounds come from the
>>>    principal (optionally intersected with the operator role's policy at
>>>    issuance) -- never from the model's emitted arguments.
>>>
>>> 2. Enforcement happens at the sink boundary. An AIC-aware gateway
>>> verifies
>>>    the presented credential, then checks per request that the concrete
>>>    operation parameters fall within the certificate's declared bounds
>>>    before forwarding. For MCP, a tools/call must reference an allowlisted
>>>    tool; the structured operation (table, amount, recipient, environment,
>>>    tool) is matched against the bound parameters. Unknown or
>>> out-of-bounds
>>>    operations are denied (fail-closed).
>>>
>>> 3. One boundary I want to state explicitly: this enforces at the network
>>>    gateway / MCP endpoint, not inside the host process's first dispatch
>>>    (the function table). In-process enforcement would require the host to
>>>    invoke the same verifier at that earlier point; that is a different
>>>    layer.
>>>
>>> The credential is transport-identity anchored (X.509 presented over mTLS,
>>> or a short-lived JWT issued from the same CA). This composes with, rather
>>> than replaces, RAR / mission-bound dynamic authorization: the certificate
>>> supplies offline-verifiable bounds, and an online policy decision can
>>> further narrow them.
>>>
>>> Running implementation (capability schemes, gateway plugins, test
>>> matrices): https://github.com/varwof/aic-capability-demo
>>>
>>> Happy to map the details if useful.
>>>
>>> Best,
>>> Jijie Wei
>>> --
>>> WIMSE mailing list -- [email protected]
>>> To unsubscribe send an email to [email protected]
>>>
>> --
> Ciao, Amr
>
>
_______________________________________________
OAuth mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to