<?xml version='1.0' encoding='utf-8'?>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<rfc category="info" docName="draft-das-agentic-execution-finality-01"
     ipr="trust200902" submissionType="IETF" xml:lang="en" version="3"
     tocInclude="true" tocDepth="3" symRefs="true" sortRefs="true">
  <front>
    <title abbrev="Tool-Dispatch Finality">Tool Selection Is Not Execution: Finality for Agentic Tool Dispatch</title>
    <seriesInfo name="Internet-Draft" value="draft-das-agentic-execution-finality-01"/>
    <author fullname="Sangam Das" initials="S." surname="Das">
      <organization>Independent Inventor</organization>
      <address>
        <postal>
          <city>Balasore</city>
          <region>Odisha</region>
          <code>756001</code>
          <country>India</country>
        </postal>
        <email>info@sangamdas.com</email>
      </address>
    </author>
    <date year="2026" month="August" day="27"/>
    <area>Security</area>
    <keyword>agentic AI</keyword>
    <keyword>tool use</keyword>
    <keyword>MCP</keyword>
    <keyword>execution finality</keyword>
    <keyword>prompt injection</keyword>
    <abstract>
      <t>An agentic model can emit a tool call that today's runtimes treat
      as something to execute. Allowlists, OAuth tokens, MCP server
      auth, sandboxes, output filters, and human approval decide
      whether an agent may reach a tool. They do not decide whether
      this generated call, with this argument digest, from this
      instruction chain, at this delegation depth, may take effect
      now.</t>
      <t>That gap is the incident surface. Prompt-injected content,
      poisoned retrieval, a malicious tool response, or a delegated
      sub-agent can produce a call that looks like ordinary tool use.
      If the dispatcher executes whatever the model selected, policy
      that lived upstream becomes advisory.</t>
      <t>This document specifies a dispatch-time gate. The model may
      compute a call. The call remains a Candidate Act. A Protected
      Enforcement Domain binds agent, tool, arguments, purpose,
      destination, provenance, and policy epochs, then issues scoped
      non-bearer authority. A Tool-Dispatch Finality Sink verifies
      that authority against the actual invocation immediately before
      the tool runs, then consumes it. The same gate applies to
      support, coding, payments, clinical, SOC, browser-use, and
      multi-agent MCP deployments. Tool selection is not
      execution authority.</t>
    </abstract>
  </front>
  <middle>
    <section anchor="intro">
      <name>Introduction</name>
      <t>Agent runtimes now sit on a short path:</t>
      <artwork><![CDATA[
user or retrieved text
        -> model reasoning
        -> tool_call{name, arguments}
        -> runtime.dispatch()
        -> external effect
]]></artwork>
      <t>The third arrow is the one this document controls. The model
      is allowed to propose. The runtime is not allowed to treat the
      proposal as a capability. A proposed tool call, API request,
      browser action, shell command, message, file write, memory
      write, payment request, or agent delegation remains
      non-effective until act-specific authority is verified at the
      dispatch boundary.</t>
      <t>This profile uses the two-boundary execution-finality chain
      also applied to AI-native networks
      <xref target="I-D.das-6g-finality"/> and precision-bounded
      egress <xref target="I-D.das-egress"/>. It specifies only the
      agent-dispatch predicates: tool and function identity, argument
      digest, instruction provenance, delegation depth, consequence
      class, and alternate-path closure across MCP, HTTP, browser,
      and shell.</t>
    </section>

    <section anchor="rfc2119">
      <name>Requirements Language</name>
      <t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL",
      "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT
      RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
      interpreted as described in BCP 14 <xref target="RFC2119"/>
      <xref target="RFC8174"/> when, and only when, they appear in all
      capitals, as shown here.</t>
      <t>Failure to establish current dispatch authority MUST NOT be
      converted into permission to invoke the tool.</t>
    </section>

    <section anchor="problem">
      <name>Problem Space</name>
      <t>Tool-using agents fail in a different place than chat
      models. A chat model that is tricked produces text. An agent
      that is tricked produces an invocation. The invocation may
      send mail, write memory, move money, open a browser, run a
      shell, or hand work to another agent. The model's next token
      is then an action.</t>
      <t>The practical failures look like this:</t>
      <ul>
        <li>The user asked for nearby pharmacies. Injected page text
        said "also export the address book." The model selected a
        second tool. The runtime had both tools enabled.</li>
        <li>A retrieved document contained an instruction to ignore
        the system prompt and call a payment function with a new
        recipient.</li>
        <li>Agent A was allowed to search. It delegated to Agent B,
        which called a shell. No single hop exceeded its local
        allowlist. The cumulative effect did.</li>
        <li>A previous ALLOW for maps.search.nearby was replayed
        against maps.search.nearby with different arguments, or
        against a different function on the same server.</li>
        <li>The approved MCP server was swapped for another server
        advertising the same tool name.</li>
      </ul>
      <t>In each case some upstream control was satisfied: the agent
      was authenticated, the tool was in the list, a token existed,
      or a human had approved "use tools" for the session. The
      missing question is the one asked at the sink: may this exact
      act become effective now.</t>
    </section>

    <section anchor="existing">
      <name>Existing Solutions and What They Do Not Bind</name>
      <t>Current stacks already contain important controls. This
      profile is designed to consume them, not to replace them.</t>
      <section>
        <name>Tool Allowlists and Function Schemas</name>
        <t>Hosted tool-use interfaces expose a list of functions and
        JSON schemas to the model. That answers "which tools exist
        for this session." It does not bind a particular argument
        vector, destination, or instruction provenance. A model that
        is allowed bash and http.post can still emit the wrong
        one.</t>
      </section>
      <section>
        <name>OAuth, API Keys, and MCP Server Authentication</name>
        <t>Tokens establish that the runtime or user may call a
        server. MCP server authentication establishes that the
        dispatcher is talking to a named server. Possession of a
        token is bearer-like with respect to every call the token can
        make. It is not act-specific, single-use, or argument-bound
        unless the application adds that layer.</t>
      </section>
      <section>
        <name>Sandboxes and Computer-Use Isolation</name>
        <t>OS sandboxes, containers, and browser isolation reduce
        blast radius. They do not decide whether a permitted tool
        inside the sandbox should fire. A sandboxed agent that can
        send email can still send the wrong email.</t>
      </section>
      <section>
        <name>Model-Level Safety and Output Filters</name>
        <t>Constitutional or policy-trained refusal, classifiers, and
        tool-argument filters try to stop bad calls before dispatch.
        They are probabilistic. An attacker who wins the prompt
        still needs a second gate that does not trust the model. A
        filter that is uncertain MUST NOT be treated as ALLOW at the
        sink.</t>
      </section>
      <section>
        <name>Human Approval and Session Consent</name>
        <t>"Allow this agent to use tools" or a one-time confirm for
        a tool class is a session decision. It goes stale when
        parental policy, enterprise policy, destination, or
        delegation depth changes. It also does not hash arguments,
        so approval of a search is reused as approval of a
        transfer.</t>
      </section>
      <section>
        <name>Policy Engines and Agent Frameworks</name>
        <t>OPA-style engines, graph orchestrators, and guardrail
        middleware can deny a call. If the same effect can be
        reached by a second path — raw HTTP, a plugin, a shell pipe,
        another agent — a policy decision on the first path is not
        finality. A component is a Finality Sink only if bypassing
        it makes the effect technically non-completable.</t>
      </section>
      <section>
        <name>What This Profile Adds</name>
        <ul>
          <li>the tool call remains a Candidate Act after the model
          emits it;</li>
          <li>arguments are bound by a canonical digest, so approved
          arguments cannot be swapped;</li>
          <li>instruction provenance and delegation depth are
          predicates, not log fields;</li>
          <li>authority is non-bearer, sink-bound, epoch-bound, and
          consumed;</li>
          <li>the dispatcher verifies the live invocation, not the
          model's self-description;</li>
          <li>uncertainty, timeout, and unknown destination fail
          closed or escalate; and</li>
          <li>alternate invocation paths that can produce the same
          effect are in scope.</li>
        </ul>
      </section>
    </section>

    <section anchor="usecases">
      <name>Industrial Relevance and Use Cases</name>
      <t>The profile is meant to sit in a production dispatcher, not
      only in an architecture review. The cases below are the
      deployments where a model already has tools and where an
      allowlist is no longer the last control.</t>

      <section>
        <name>Customer Support and CRM</name>
        <t>A support agent is allowed to read tickets, search a
        knowledge base, and draft replies. The same runtime often
        also has refund, credit, account-close, and data-export
        tools. Session consent says "this bot may use CRM tools."
        Prompt-injected ticket text or a retrieved policy PDF can
        cause the model to select refund.create or
        customer.export_pii.</t>
        <t>The sink binds function_id, arguments_digest (amount,
        beneficiary, ticket id), consequence_class FINANCIAL or
        DATA_DISCLOSURE, and instruction provenance. A digest
        issued for refund.create at 12.00 MUST NOT authorize
        1,200.00. Export of a whole address book remains
        non-effective when provenance is retrieval rather than an
        authenticated supervisor instruction.</t>
      </section>

      <section>
        <name>Software Engineering and Production Change</name>
        <t>Coding agents already call repo.read, test.run,
        pull_request.open, and, in some fleets, deploy or
        cloud.cli. Sandboxing the workspace does not distinguish
        "open a PR on a feature branch" from "apply this Terraform
        to prod." A poisoned README or CI log can instruct the
        agent to raise IAM privileges or disable an alert.</t>
        <t>Consequence class PERSISTENT_STATE_CHANGE or
        NETWORK_CONTROL, destination (org/repo/account), and
        delegation_depth are the load-bearing fields. Authority
        for git.commit on branch feature/x MUST NOT authorize
        kubectl apply in another cluster. Computer-use agents that
        drive an IDE or a cloud console are the same profile with
        sink_type BROWSER_CONTROLLER or SHELL_GATEWAY.</t>
      </section>

      <section>
        <name>Payments, Treasury, and Commerce</name>
        <t>Checkout, payout, and bookkeeping agents hold tools that
        move value. PCI and bank APIs already authenticate the
        application. They do not hash the model's chosen
        recipient, amount, and memo. Indirect injection in an
        invoice PDF or a supplier email is enough to retarget a
        payment tool the agent was allowed to use.</t>
        <t>The Candidate Act MUST carry consequence_class FINANCIAL,
        a destination recipient_id, and a single-use authority
        bound to the arguments digest. Replay of yesterday's
        payout authority, or reuse at a second payment sink, MUST
        fail. Human approval, where required, is an input to the
        PED, not a substitute for sink verification.</t>
      </section>

      <section>
        <name>Clinical, Payer, and Life-Sciences Assistants</name>
        <t>Scheduling and chart-summary agents need lookup tools.
        The same session must not silently acquire
        record.disclose, prescription.send, or
        research.export_cohort. Existing EHR OAuth scopes are
        session-wide. A retrieved clinical note that contains
        "forward this chart to the address below" is an instruction
        provenance problem, not an authentication problem.</t>
        <t>DATA_DISCLOSURE acts SHOULD escalate when provenance is
        not user or clinician. Destination jurisdiction and
        recipient_id bind cross-border or consumer-app egress.
        This document does not define medical policy. It keeps
        unauthorized disclosure non-completable when that policy
        says no.</t>
      </section>

      <section>
        <name>Security Operations and IT Administration</name>
        <t>SOC and IT agents query SIEM, open tickets, isolate
        hosts, rotate credentials, and in some designs push
        firewall or identity changes. Speed is the reason they
        exist. The failure mode is an attacker-authored alert or
        ticket that the model treats as an instruction to
        disable_monitoring or add_allowlist_entry.</t>
        <t>NETWORK_CONTROL and PERSISTENT_STATE_CHANGE calls SHOULD
        leave the hot path. The sink binds tool, destination
        system, and arguments digest so that isolate(host=A) cannot
        be substituted with isolate(host=controller). Delegation
        from a triage agent to an enrichment agent MUST NOT inherit
        containment-tool authority.</t>
      </section>

      <section>
        <name>Email, Calendar, and Knowledge-Work Assistants</name>
        <t>Mail-send, calendar-accept, drive-share, and memory-write
        tools are already shipped in consumer and enterprise
        assistants. "You may send email on my behalf" is a session
        grant. Injected calendar notes and retrieved threads are
        the classic indirect-prompt path to
        message.send or file.share with a new recipient.</t>
        <t>COMMUNICATION and DATA_DISCLOSURE classes bind
        recipient_id and arguments digest. A draft authorized for
        to=alice@example.com MUST fail if the live call is
        to=attacker@example.net. Memory writes that persist
        injected instructions are themselves Candidate Acts of type
        MEMORY_WRITE; leaving them unconstrained trains the next
        turn's provenance failure.</t>
      </section>

      <section>
        <name>Browser and Computer-Use Agents</name>
        <t>Agents that click, type, and submit forms cross the same
        boundary as an API tool, except the effect is whatever the
        current page will accept. Site isolation and a visible
        browser do not bind "pay this merchant 40" versus "pay this
        merchant 4000," or prevent a page script from becoming an
        instruction.</t>
        <t>The sink is BROWSER_CONTROLLER. Each consequential
        submission is a Candidate Act. Navigation to a new origin
        is a destination change and MUST invalidate authority
        issued for the previous origin. Accessibility and
        screenshot tools that can reconstruct secrets are
        DATA_DISCLOSURE acts, not INFORMATIONAL ones.</t>
      </section>

      <section>
        <name>Contact Center, Voice, and Real-Time Tools</name>
        <t>Voice agents invoke the same CRM and payment tools under
        a latency budget. The hot path in
        <xref target="operation"/> exists for that reason. Cached
        envelopes MAY allow account.lookup and script.play.
        account.charge, number.port, and recording.export remain
        cold-path or human-gated classes. Timeout MUST NOT place
        the charge.</t>
      </section>

      <section>
        <name>Multi-Agent Marketplaces and MCP Fleets</name>
        <t>Enterprises are connecting many MCP servers and
        specialist agents. Discovery of a server, a tool name, or
        a marketplace card is not authority. A planner agent that
        may call search MUST NOT, by delegation, confer shell or
        payout tools on a worker. AGENT_DELEGATION is a Candidate
        Act. Depth, parent_agent_id, and permitted consequence
        class travel with the worker. MCP/server substitution
        (T7) is a sink-binding and endpoint-binding check, not a
        model check.</t>
      </section>

      <section>
        <name>What Operators Should Measure</name>
        <t>A deployment can treat the profile as live when these are
        true in production logs: (1) every high-risk tool invoke has
        a consumed authority_id; (2) argument-digest mismatches are
        denials, not retries that skip the sink; (3) delegation
        beyond policy.max_depth never reaches invoke(); (4) a second
        path (raw HTTP, shell, plugin) cannot complete the same
        effect without the same bindings. Those four tests are more
        useful than a slide that says the agent is "policy
        aligned."</t>
      </section>
    </section>

    <section anchor="terminology">
      <name>Terminology</name>
      <dl newline="true">
        <dt>Agent Candidate Act</dt>
        <dd>A model- or agent-generated operation that has been
        prepared as a tool call or equivalent but has not been
        permitted to execute.</dd>
        <dt>Tool-Dispatch Finality Sink</dt>
        <dd>The component that would actually invoke the tool, issue
        the HTTP request, start the browser action, exec the command,
        commit the write, or send the message. If that component
        can be skipped, it is not the sink.</dd>
        <dt>Arguments digest</dt>
        <dd>A hash over the canonical tool arguments. Changing any
        load-bearing argument MUST change the digest and invalidate
        previously issued authority.</dd>
        <dt>Instruction provenance</dt>
        <dd>Evidence about whether the act is attributable to a user
        or developer instruction versus retrieved text, tool output,
        memory, or another agent.</dd>
        <dt>Delegation depth</dt>
        <dd>The number of agent-to-agent hops from the initiating
        principal to the actor proposing the Candidate Act.</dd>
      </dl>
      <t>Candidate Act, Non-Effective State, Protected Enforcement
      Domain (PED), Protected Validation Evidence, scoped non-bearer
      finality authority, and Finality Sink are used as in
      <xref target="I-D.das-6g-finality"/>.</t>
    </section>

    <section anchor="architecture">
      <name>Architecture</name>
      <t>A Candidate Act MUST NOT become effective merely because the
      model selected a tool, the tool is in the allowlist, a token
      exists, or an upstream safety model returned ALLOW.</t>
      <figure>
        <name>Dispatch-time finality</name>
        <artwork><![CDATA[
model emits tool_call(name, args)
              |
              v
     AGENT CANDIDATE ACT
              |
              v
        Non-Effective State
              |
              v
   Protected Enforcement Domain
      principal, agent, model
      tool, function, args digest
      purpose, destination
      instruction provenance
      delegation depth
      policy / revocation epochs
      intended dispatch sink
              |
              v
   evidence + scoped authority
              |
              v
   TOOL-DISPATCH FINALITY SINK
              |
      +-- PASS -> invoke once, consume authority
      |
      `-- FAIL -> tool is not invoked
]]></artwork>
      </figure>
    </section>

    <section anchor="pseudocode">
      <name>Dispatch Pseudocode</name>
      <t>The following procedures are normative in behavior, not in
      programming language. An implementation MAY collapse PED and
      sink into one protected service if both roles remain
      distinguishable: evidence is committed before authority is
      usable, and the invocation is checked immediately before
      effect.</t>
      <section>
        <name>Runtime: Model Output to Candidate Act</name>
        <sourcecode type="pseudocode"><![CDATA[
function ON_MODEL_TOOL_CALL(model_out, ctx):
    act = AgentCandidateAct{
        candidate_act_id: fresh_id(),
        act_type: map_act_type(model_out),
        agent: ctx.agent,
        tool: {id: model_out.name,
               function_id: model_out.name,
               endpoint: resolve(model_out.name),
               protocol: ctx.protocol},
        arguments_digest: HASH(canonicalize(model_out.arguments)),
        purpose: ctx.declared_purpose,
        destination: resolved_destination(model_out),
        consequence_class: classify(model_out.name),
        instruction_provenance: ctx.provenance,
        delegation_depth: ctx.depth,
        policy_state: current_epochs(),
        freshness: {nonce: fresh_nonce()},
        finality_sink: ctx.dispatch_sink,
        expires_at: now() + short_ttl
    }
    HOLD_NON_EFFECTIVE(act)
    return PED_VALIDATE(act, model_out.arguments)
]]></sourcecode>
      </section>
      <section>
        <name>PED Validation</name>
        <sourcecode type="pseudocode"><![CDATA[
function PED_VALIDATE(act, raw_args):
    if malformed(act):
        return DENY(MALFORMED_ACT)
    if not fresh(act.freshness.nonce):
        return DENY(REPLAY_OR_STALE)
    if act.policy_state != current_epochs():
        return DENY(EPOCH_MISMATCH)
    if not allowlisted(act.agent, act.tool):
        return DENY(TOOL_NOT_AUTHORIZED)
    if act.destination unknown or jurisdiction unresolved:
        return ESCALATE_OR_DENY(DESTINATION_UNRESOLVED)
    if act.delegation_depth > policy.max_depth:
        return DENY(DELEGATION_EXCEEDED)
    if provenance_untrusted(act) and
       act.consequence_class in HIGH_RISK:
        return ESCALATE_OR_DENY(INSTRUCTION_PROVENANCE_FAILURE)
    if args_exceed_schema(act.tool, raw_args):
        return DENY(SCOPE_MISMATCH)

    evidence = COMMIT_PROTECTED_EVIDENCE(act)
    authority = ISSUE_SCOPED_AUTHORITY(act, evidence)
    # act remains non-effective
    return ALLOW(authority)
]]></sourcecode>
      </section>
      <section>
        <name>Sink: Verify Then Invoke</name>
        <sourcecode type="pseudocode"><![CDATA[
function DISPATCH_SINK_INVOKE(act, authority, raw_args):
    live_digest = HASH(canonicalize(raw_args))

    if authority missing or integrity_fail(authority):
        return DENY(NO_OR_INVALID_AUTHORITY)
    if HASH(act) != authority.candidate_act_digest:
        return DENY(ACT_MISMATCH)
    if live_digest != act.arguments_digest:
        return DENY(ARGS_SUBSTITUTION)
    if authority.sink_id != THIS_SINK:
        return DENY(SINK_MISMATCH)
    if authority.tool_id != act.tool.id or
       authority.function_id != act.tool.function_id:
        return DENY(TOOL_SUBSTITUTION)
    if expired(authority) or consumed(authority):
        return DENY(STALE_OR_USED)
    if authority.epochs != current_epochs():
        return DENY(EPOCH_MISMATCH)
    if act.destination != authority.permitted_destination:
        return DENY(DESTINATION_MISMATCH)

    ATOMICALLY:
        consume(authority)
        advance_replay_state(act.freshness.nonce)
        result = invoke(act.tool, raw_args)
    record_sink_evidence(act, authority, result)
    return result
]]></sourcecode>
        <t>The required property is that invoke() is unreachable when
        any check fails. Logging a denial and then calling the tool
        is non-conforming.</t>
      </section>
    </section>

    <section anchor="bindings">
      <name>Load-Bearing Bindings</name>
      <t>Authority issued for maps.search / nearby / digest D1 MUST
      NOT authorize maps.search / nearby / digest D2, a different
      function on the same server, a shell action, or the same call
      at another sink. DISPLAY-equivalent mistakes in this profile
      are tool substitution and argument substitution.</t>
      <t>An authority for TOOL_DISPATCH MUST NOT automatically
      authorize SHELL_GATEWAY or BROWSER_CONTROLLER. An authority
      for delegation_depth 0 MUST NOT automatically authorize depth
      2. An authority created under revocation epoch N MUST fail
      when the protected state is at epoch N+1.</t>
    </section>

    <section anchor="injection">
      <name>Prompt Injection and Delegation</name>
      <t>The protocol does not require the model to detect injection.
      It requires that a consequential call influenced by retrieved
      text, tool output, memory, or another agent still cannot
      execute unless provenance and consequence-class predicates
      pass. Where provenance is UNKNOWN and the class is FINANCIAL,
      PERSISTENT_STATE_CHANGE, PHYSICAL, or NETWORK_CONTROL, the PED
      SHOULD escalate or deny rather than allow on the hot path.</t>
      <t>Discovery of an MCP server, marketplace entry, or advertised
      tool MUST NOT by itself create dispatch authority. Delegation
      to a sub-agent is itself a Candidate Act of type
      AGENT_DELEGATION. Implementations SHOULD track cumulative
      depth, tool-call velocity, and consequence class across the
      chain and withhold further authority when the envelope is
      exceeded.</t>
    </section>

    <section anchor="paths">
      <name>Alternate-Path Closure</name>
      <t>If the same protected effect can be produced by MCP, a raw
      HTTP client, a browser controller, a shell, an IPC helper, or
      a memory/file write, each path MUST either enforce this
      profile or be unable to produce the effect. Moving the call
      from the tool dispatcher to curl MUST NOT remove the
      requirement.</t>
    </section>

    <section anchor="json">
      <name>JSON Interoperability Profile</name>
      <t>Objects are UTF-8 JSON. A transport MAY be local IPC, an
      OS broker, HTTPS, or an MCP middleware hook. Transport MUST
      preserve integrity, sink identity, freshness, and non-bearer
      semantics. Implementations MUST reject unknown load-bearing
      fields when additionalProperties is false.</t>

      <section>
        <name>AgentCandidateAct</name>
        <sourcecode type="json"><![CDATA[
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:ietf:params:json-schema:agent-finality:candidate-act:1",
  "title": "AgentCandidateAct",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "version", "object_type", "candidate_act_id", "act_type",
    "created_at", "expires_at", "initiating_principal", "agent",
    "tool", "purpose", "arguments_digest", "consequence_class",
    "policy_state", "freshness", "finality_sink"
  ],
  "properties": {
    "version": { "type": "string", "const": "1.0" },
    "object_type": {
      "type": "string",
      "const": "agent_candidate_act"
    },
    "candidate_act_id": {
      "type": "string",
      "minLength": 16,
      "maxLength": 128
    },
    "act_type": {
      "type": "string",
      "enum": [
        "TOOL_CALL", "FUNCTION_CALL", "API_REQUEST",
        "BROWSER_ACTION", "SHELL_ACTION", "MESSAGE_SEND",
        "FILE_WRITE", "MEMORY_WRITE", "AGENT_DELEGATION",
        "PAYMENT_REQUEST", "OTHER"
      ]
    },
    "created_at": { "type": "string", "format": "date-time" },
    "expires_at": { "type": "string", "format": "date-time" },
    "initiating_principal": {
      "type": "object",
      "required": ["principal_id", "principal_type"],
      "properties": {
        "principal_id": { "type": "string" },
        "principal_type": {
          "type": "string",
          "enum": ["user", "service", "enterprise", "device", "agent"]
        },
        "authorization_context_id": { "type": "string" }
      }
    },
    "agent": {
      "type": "object",
      "required": ["agent_id", "runtime_id"],
      "properties": {
        "agent_id": { "type": "string" },
        "runtime_id": { "type": "string" },
        "model_id": { "type": "string" },
        "model_version": { "type": "string" },
        "alf_digest": { "type": "string" },
        "delegation_depth": { "type": "integer", "minimum": 0 },
        "parent_agent_id": { "type": "string" }
      }
    },
    "tool": {
      "type": "object",
      "required": ["tool_id", "function_id"],
      "properties": {
        "tool_id": { "type": "string" },
        "function_id": { "type": "string" },
        "tool_endpoint": { "type": "string" },
        "tool_protocol": {
          "type": "string",
          "enum": [
            "MCP", "HTTP_API", "LOCAL_FUNCTION",
            "BROWSER", "SHELL", "A2A", "OTHER"
          ]
        }
      }
    },
    "purpose": {
      "type": "object",
      "required": ["purpose_id", "declared_purpose"],
      "properties": {
        "purpose_id": { "type": "string" },
        "declared_purpose": { "type": "string" },
        "user_intent_reference": { "type": "string" },
        "purpose_epoch": { "type": "integer", "minimum": 0 }
      }
    },
    "arguments_digest": {
      "type": "object",
      "required": ["algorithm", "value", "canonicalization"],
      "properties": {
        "algorithm": {
          "type": "string",
          "enum": ["SHA-256", "SHA-384", "SHA-512"]
        },
        "value": { "type": "string" },
        "canonicalization": {
          "type": "string",
          "enum": ["JCS", "implementation-defined"]
        }
      }
    },
    "destination": {
      "type": "object",
      "properties": {
        "destination_id": { "type": "string" },
        "endpoint": { "type": "string" },
        "jurisdiction": { "type": "string" },
        "recipient_id": { "type": "string" }
      }
    },
    "consequence_class": {
      "type": "string",
      "enum": [
        "INFORMATIONAL", "DATA_DISCLOSURE",
        "PERSISTENT_STATE_CHANGE", "FINANCIAL",
        "NETWORK_CONTROL", "PHYSICAL", "COMMUNICATION", "OTHER"
      ]
    },
    "policy_state": {
      "type": "object",
      "required": ["policy_epoch", "authority_epoch", "revocation_epoch"],
      "properties": {
        "policy_epoch": { "type": "integer", "minimum": 0 },
        "authority_epoch": { "type": "integer", "minimum": 0 },
        "revocation_epoch": { "type": "integer", "minimum": 0 },
        "policy_profile_id": { "type": "string" }
      }
    },
    "freshness": {
      "type": "object",
      "required": ["nonce"],
      "properties": {
        "nonce": { "type": "string", "minLength": 16 },
        "sequence": { "type": "integer", "minimum": 0 },
        "session_id": { "type": "string" }
      }
    },
    "instruction_provenance": {
      "type": "object",
      "properties": {
        "instruction_digest": { "type": "string" },
        "source_type": {
          "type": "string",
          "enum": [
            "user", "system", "developer", "tool",
            "retrieval", "memory", "agent"
          ]
        },
        "trusted_chain_digest": { "type": "string" }
      }
    },
    "finality_sink": {
      "type": "object",
      "required": ["sink_id", "sink_type"],
      "properties": {
        "sink_id": { "type": "string" },
        "sink_type": {
          "type": "string",
          "enum": [
            "TOOL_DISPATCH", "API_GATEWAY", "BROWSER_CONTROLLER",
            "SHELL_GATEWAY", "MESSAGE_GATEWAY", "FILE_COMMIT",
            "MEMORY_COMMIT", "OTHER"
          ]
        }
      }
    }
  }
}
]]></sourcecode>
      </section>

      <section>
        <name>Evidence, Authority, and Sink Verify</name>
        <sourcecode type="json"><![CDATA[
{
  "version": "1.0",
  "object_type": "protected_validation_evidence",
  "evidence_id": "pve-6d6ddc91",
  "candidate_act_id": "act-7f39b8b8",
  "decision": "ALLOW",
  "validated_predicates": {
    "principal_authorized": true,
    "agent_identity_valid": true,
    "tool_scope_valid": true,
    "purpose_valid": true,
    "instruction_provenance_valid": true,
    "destination_valid": true,
    "policy_epoch_valid": true,
    "revocation_state_valid": true,
    "freshness_valid": true,
    "sink_binding_valid": true
  },
  "protected_state": {
    "state_reference": "ped-state-901",
    "monotonic_counter": 8481
  },
  "issued_at": "2026-08-26T17:40:01Z",
  "protector": {
    "type": "TEE_SIGNATURE",
    "key_id": "ped-key-12",
    "signature": "base64url-signature"
  }
}
]]></sourcecode>
        <sourcecode type="json"><![CDATA[
{
  "version": "1.0",
  "object_type": "agent_finality_authority",
  "authority_id": "afa-c7d32d54",
  "candidate_act_id": "act-7f39b8b8",
  "evidence_id": "pve-6d6ddc91",
  "scope": {
    "act_type": "TOOL_CALL",
    "tool_id": "maps.search",
    "function_id": "nearby",
    "permitted_destination": "tool.example",
    "permitted_consequence_class": "DATA_DISCLOSURE"
  },
  "binding": {
    "candidate_act_digest": {
      "algorithm": "SHA-256",
      "value": "base64url-act-digest"
    },
    "nonce": "A81D4F42B779CC21",
    "policy_epoch": 42,
    "revocation_epoch": 7,
    "finality_sink_id": "dispatch-sink-01"
  },
  "lifetime": {
    "issued_at": "2026-08-26T17:40:01Z",
    "expires_at": "2026-08-26T17:40:10Z",
    "single_use": true
  },
  "issuer": {
    "ped_id": "ped-runtime-01",
    "key_id": "ped-key-12",
    "signature": "base64url-signature"
  }
}
]]></sourcecode>
        <sourcecode type="json"><![CDATA[
{
  "operation": "DispatchSinkVerify",
  "request_id": "req-10aa19",
  "decision": "ALLOW",
  "verification": {
    "authority_signature": "VALID",
    "candidate_act_binding": "MATCH",
    "tool_scope": "MATCH",
    "args_digest": "MATCH",
    "destination": "MATCH",
    "nonce": "FRESH",
    "policy_epoch": "CURRENT",
    "revocation_epoch": "CURRENT",
    "consumption_state": "UNUSED",
    "sink_binding": "MATCH"
  },
  "consumption": {
    "authority_id": "afa-c7d32d54",
    "status": "CONSUMED"
  },
  "effectuation": { "permitted": true, "effect_id": "effect-bf3b5a" }
}
]]></sourcecode>
        <sourcecode type="json"><![CDATA[
{
  "operation": "DispatchSinkVerify",
  "request_id": "req-10ab00",
  "decision": "DENY",
  "error": {
    "code": "EF_SCOPE_MISMATCH",
    "message": "Requested function is outside authorized tool scope.",
    "retryable": false
  },
  "effectuation": { "permitted": false }
}
]]></sourcecode>
      </section>

      <section>
        <name>Complete Transaction</name>
        <sourcecode type="json"><![CDATA[
{
  "step_1_model": {
    "emitted": "maps.search.nearby",
    "status": "NON_EFFECTIVE"
  },
  "step_2_candidate_act": {
    "candidate_act_id": "act-7f39b8b8",
    "act_type": "TOOL_CALL",
    "agent": {
      "agent_id": "assistant-23",
      "model_id": "model-X",
      "delegation_depth": 0
    },
    "tool": {
      "tool_id": "maps.search",
      "function_id": "nearby",
      "tool_protocol": "MCP"
    },
    "purpose": {
      "purpose_id": "nearby-service-discovery",
      "declared_purpose": "Find nearby pharmacies"
    },
    "arguments_digest": {
      "algorithm": "SHA-256",
      "value": "base64url-args-digest",
      "canonicalization": "JCS"
    },
    "destination": { "destination_id": "tool.example", "jurisdiction": "IN" },
    "consequence_class": "DATA_DISCLOSURE",
    "instruction_provenance": { "source_type": "user" },
    "finality_sink": {
      "sink_id": "dispatch-sink-01",
      "sink_type": "TOOL_DISPATCH"
    }
  },
  "step_3_authority": { "authority_id": "afa-c7d32d54", "single_use": true },
  "step_4_sink": { "decision": "ALLOW", "authority_consumed": true },
  "step_5_effect": "maps.search.nearby invoked once with digest-bound args"
}
]]></sourcecode>
      </section>
    </section>

    <section anchor="operation">
      <name>Hot Path, Escalation, and Failure</name>
      <t>Repeated calls inside a fixed envelope — same agent, tool,
      function, destination, consequence class, and provenance class
      — MAY use cached policy and short-lived authority. The sink
      check is still mandatory. New tools, unknown destinations,
      depth increases, FINANCIAL or PHYSICAL class, provenance
      UNKNOWN, or runtime anomaly SHOULD escalate. Timeout is not
      ALLOW.</t>
      <t>Illustrative codes, not IANA assignments: EF-002
      NO_FINALITY_AUTHORITY, EF-005 AUTHORITY_ALREADY_USED, EF-006
      REPLAY_DETECTED, EF-012 SCOPE_MISMATCH, EF-013 PURPOSE_MISMATCH,
      EF-020 DESTINATION_MISMATCH, EF-040 SINK_MISMATCH, EF-053
      INSTRUCTION_PROVENANCE_FAILURE, EF-070 ESCALATION_REQUIRED,
      EF-080 FAIL_CLOSED.</t>
    </section>

    <section anchor="security">
      <name>Security Considerations</name>
      <t>Threats the sink is intended to make non-completable without
      current authority include: T1 direct prompt injection, T2
      indirect prompt injection, T3 poisoned retrieval, T4 poisoned
      memory, T5 malicious tool response, T6 tool substitution, T7
      MCP/server substitution, T8 unauthorized delegation, T9
      recursive escalation, T10 stale user authority, T11 destination
      substitution, T14 authority replay, T15 cross-sink reuse, and
      T17 alternate-path effectuation.</t>
      <t>The model is not trusted to police these. The property is
      mechanical: invoke() does not run. Residual risk remains if
      an unclassified generated argument string is itself a program
      the tool will interpret, or if a path around the sink exists.
      Implementations MUST treat those as profile failures, not as
      acceptable best-effort modes.</t>
    </section>

    <section anchor="privacy">
      <name>Privacy Considerations</name>
      <t>Candidate Act descriptors can reveal user intent, tool
      choice, and destination. Implementations SHOULD hash or seal
      raw arguments and prompts on untrusted paths and SHOULD NOT log
      full tool arguments when the digest is sufficient for
      verification.</t>
    </section>

    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>This document requests no IANA actions.</t>
    </section>

    <section anchor="ipr-note">
      <name>Intellectual Property Note</name>
      <t>Certain concepts are associated with pending applications in
      the DAS Protocols family, including PCT/IB2026/054453,
      PCT/IB2026/055615, PCT/IB2026/055760, PCT/IB2026/055870,
      PCT/IB2026/056058, and PCT/IB2026/053385. IETF disclosure
      should follow BCP 79 <xref target="RFC8179"/>.</t>
    </section>

    <section anchor="conclusion">
      <name>Conclusion</name>
      <t>The model may select a tool. That selection is computation.
      External execution starts only after the Candidate Act is
      validated, evidence is committed, scoped authority is issued,
      and the dispatch sink verifies the live invocation. Tool
      selection is not execution authority.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>Normative References</name>
      <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
        <front>
          <title>Key words for use in RFCs to Indicate Requirement Levels</title>
          <author initials="S." surname="Bradner" fullname="S. Bradner"/>
          <date year="1997" month="March"/>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="2119"/>
      </reference>
      <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
        <front>
          <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
          <author initials="B." surname="Leiba" fullname="B. Leiba"/>
          <date year="2017" month="May"/>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="8174"/>
      </reference>
      <reference anchor="RFC8179" target="https://www.rfc-editor.org/info/rfc8179">
        <front>
          <title>Intellectual Property Rights in IETF Technology</title>
          <author initials="S." surname="Bradner" fullname="S. Bradner"/>
          <author initials="J." surname="Contreras" fullname="J. Contreras"/>
          <date year="2017" month="May"/>
        </front>
        <seriesInfo name="BCP" value="79"/>
        <seriesInfo name="RFC" value="8179"/>
      </reference>
    </references>
    <references>
      <name>Informative References</name>
      <reference anchor="I-D.das-6g-finality">
        <front>
          <title>Execution-Finality for AI-Native 5G/6G and O-RAN</title>
          <author fullname="Sangam Das" initials="S." surname="Das"/>
          <date year="2026" month="August"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-das-ai-native-6g-execution-finality-01"/>
      </reference>
      <reference anchor="I-D.das-egress">
        <front>
          <title>Access Is Not Egress: Precision-Bounded Location Release</title>
          <author fullname="Sangam Das" initials="S." surname="Das"/>
          <date year="2026" month="August"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-das-precision-bounded-egress-01"/>
      </reference>
    </references>
  </back>
</rfc>
