Skip to main content

Governed Action Protocol (GAP)
draft-shovan-gap-00

Document Type Active Internet-Draft (individual)
Author Joshua Shovan
Last updated 2026-06-30
RFC stream (None)
Intended RFC status (None)
Formats
Stream Stream state (No stream defined)
Consensus boilerplate Unknown
RFC Editor Note (None)
IESG IESG state I-D Exists
Telechat date (None)
Responsible AD (None)
Send notices to (None)
draft-shovan-gap-00
Network Working Group                                          J. Shovan
Internet-Draft                                                 SynOI Inc
Intended status: Informational                              30 June 2026
Expires: 1 January 2027

                     Governed Action Protocol (GAP)
                          draft-shovan-gap-00

Abstract

   The Governed Action Protocol (GAP) is an open wire protocol for a
   Universal Action Coordination Fabric.  GAP defines a four-phase
   lifecycle (Declare, Grant, Invoke, Receipt) that governs every action
   an AI agent, smart device, industrial controller, or automated
   pipeline may take.  Every gate decision produces a content-addressed,
   immutable receipt; receipts are signed at L2+ (Ed25519) and L4
   (hybrid ML-DSA-65).  The protocol is designed to be language- and
   platform-neutral, applicable across enterprise AI agent pipelines,
   consumer smart home devices, medical equipment, industrial control
   systems, and game engines.

   This document specifies the wire format, object model, grant
   evaluation rules, HTTP API surface, workflow semantics, revocation
   mechanisms, and conformance tiers for GAP version 1.0.

Status of This Memo

   This Internet-Draft is submitted in full conformance with the
   provisions of BCP 78 and BCP 79.

   Internet-Drafts are working documents of the Internet Engineering
   Task Force (IETF).  Note that other groups may also distribute
   working documents as Internet-Drafts.  The list of current Internet-
   Drafts is at https://datatracker.ietf.org/drafts/current/.

   Internet-Drafts are draft documents valid for a maximum of six months
   and may be updated, replaced, or obsoleted by other documents at any
   time.  It is inappropriate to use Internet-Drafts as reference
   material or to cite them other than as "work in progress."

   This Internet-Draft will expire on 1 January 2027.

Copyright Notice

   Copyright (c) 2026 IETF Trust and the persons identified as the
   document authors.  All rights reserved.

Shovan                   Expires 1 January 2027                 [Page 1]
Internet-Draft                     GAP                         June 2026

   This document is subject to BCP 78 and the IETF Trust's Legal
   Provisions Relating to IETF Documents (https://trustee.ietf.org/
   license-info) in effect on the date of publication of this document.
   Please review these documents carefully, as they describe your rights
   and restrictions with respect to this document.

Table of Contents

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   4
     1.1.  Motivation  . . . . . . . . . . . . . . . . . . . . . . .   4
     1.2.  Scope . . . . . . . . . . . . . . . . . . . . . . . . . .   5
     1.3.  Conventions and Definitions . . . . . . . . . . . . . . .   5
   2.  Object Model  . . . . . . . . . . . . . . . . . . . . . . . .   6
     2.1.  CDRO Envelope . . . . . . . . . . . . . . . . . . . . . .   6
     2.2.  OID Computation . . . . . . . . . . . . . . . . . . . . .   8
     2.3.  Canonical JSON  . . . . . . . . . . . . . . . . . . . . .   9
   3.  Phase 1: Declare  . . . . . . . . . . . . . . . . . . . . . .   9
     3.1.  Purpose . . . . . . . . . . . . . . . . . . . . . . . . .   9
     3.2.  CapabilityDeclarationBody . . . . . . . . . . . . . . . .  10
     3.3.  Supersession  . . . . . . . . . . . . . . . . . . . . . .  12
     3.4.  Ephemeral Actors  . . . . . . . . . . . . . . . . . . . .  12
     3.5.  MCP Tool-Call Governance [DESIGN] . . . . . . . . . . . .  13
     3.6.  Identity Binding [DESIGN] . . . . . . . . . . . . . . . .  13
     3.7.  Compartment-Based Access Scoping [DESIGN] . . . . . . . .  15
   4.  Phase 2: Grant  . . . . . . . . . . . . . . . . . . . . . . .  15
     4.1.  Purpose . . . . . . . . . . . . . . . . . . . . . . . . .  16
     4.2.  CapabilityGrantBody . . . . . . . . . . . . . . . . . . .  16
       4.2.1.  Cross-Grant Aggregate Limit Groups  . . . . . . . . .  19
     4.3.  Precondition Kind Registry  . . . . . . . . . . . . . . .  20
       4.3.1.  Token Budget Governance [DESIGN]  . . . . . . . . . .  21
       4.3.2.  Signed PIP Response [DESIGN]  . . . . . . . . . . . .  22
     4.4.  Scope Narrowing Evaluation  . . . . . . . . . . . . . . .  23
     4.5.  Granted-By Verification . . . . . . . . . . . . . . . . .  24
     4.6.  Delegation  . . . . . . . . . . . . . . . . . . . . . . .  24
     4.7.  Capability Pattern Matching . . . . . . . . . . . . . . .  25
   5.  Phase 3: Invoke . . . . . . . . . . . . . . . . . . . . . . .  26
     5.1.  Purpose . . . . . . . . . . . . . . . . . . . . . . . . .  26
     5.2.  CapabilityInvocationBody  . . . . . . . . . . . . . . . .  26
     5.3.  Timestamp Validation  . . . . . . . . . . . . . . . . . .  26
     5.4.  Idempotency . . . . . . . . . . . . . . . . . . . . . . .  27
     5.5.  Grant Selection . . . . . . . . . . . . . . . . . . . . .  28
   6.  Phase 4: Receipt  . . . . . . . . . . . . . . . . . . . . . .  28
     6.1.  GapDecisionReceiptBody  . . . . . . . . . . . . . . . . .  28
     6.2.  GDPR Erasure  . . . . . . . . . . . . . . . . . . . . . .  28
     6.3.  Token Consumption on Receipt [DESIGN] . . . . . . . . . .  31
     6.4.  Compliance Tags . . . . . . . . . . . . . . . . . . . . .  31
   7.  Agent Delegation Chain [DESIGN] . . . . . . . . . . . . . . .  32
     7.1.  Structure . . . . . . . . . . . . . . . . . . . . . . . .  32

Shovan                   Expires 1 January 2027                 [Page 2]
Internet-Draft                     GAP                         June 2026

     7.2.  Constraints . . . . . . . . . . . . . . . . . . . . . . .  34
   8.  Consent Version Chain [DESIGN]  . . . . . . . . . . . . . . .  35
     8.1.  gap:consent_record  . . . . . . . . . . . . . . . . . . .  35
     8.2.  Precondition: consent_current . . . . . . . . . . . . . .  35
     8.3.  Relationship to GDPR  . . . . . . . . . . . . . . . . . .  36
   9.  Offline Execution Profile . . . . . . . . . . . . . . . . . .  36
     9.1.  OEP Bundle  . . . . . . . . . . . . . . . . . . . . . . .  36
     9.2.  Offline Evaluation  . . . . . . . . . . . . . . . . . . .  37
     9.3.  Reconciliation  . . . . . . . . . . . . . . . . . . . . .  38
     9.4.  Sovereign Mode  . . . . . . . . . . . . . . . . . . . . .  38
   10. Workflows . . . . . . . . . . . . . . . . . . . . . . . . . .  38
     10.1.  Purpose  . . . . . . . . . . . . . . . . . . . . . . . .  38
     10.2.  Workflow Lifecycle . . . . . . . . . . . . . . . . . . .  38
     10.3.  Signal Sender Validation . . . . . . . . . . . . . . . .  39
       10.3.1.  Channel Adapters . . . . . . . . . . . . . . . . . .  39
     10.4.  Two-Person Integrity . . . . . . . . . . . . . . . . . .  41
     10.5.  Safety Constraints on Workflow Definitions . . . . . . .  42
   11. Revocation  . . . . . . . . . . . . . . . . . . . . . . . . .  42
     11.1.  Revocation Kinds . . . . . . . . . . . . . . . . . . . .  42
     11.2.  Provisional Block Policy . . . . . . . . . . . . . . . .  42
     11.3.  Offline Revocation Bundle  . . . . . . . . . . . . . . .  43
     11.4.  Break-Glass Grants . . . . . . . . . . . . . . . . . . .  45
       11.4.1.  Break-Glass Grant Fields . . . . . . . . . . . . . .  45
       11.4.2.  Break-Glass Token  . . . . . . . . . . . . . . . . .  46
       11.4.3.  Break-Glass Invocation . . . . . . . . . . . . . . .  46
       11.4.4.  Local Override Credential  . . . . . . . . . . . . .  47
     11.5.  L3 Quorum Approval . . . . . . . . . . . . . . . . . . .  48
   12. HTTP API Surface  . . . . . . . . . . . . . . . . . . . . . .  48
     12.1.  Core Endpoints . . . . . . . . . . . . . . . . . . . . .  48
     12.2.  Revocation Endpoints . . . . . . . . . . . . . . . . . .  49
     12.3.  Workflow Endpoints . . . . . . . . . . . . . . . . . . .  49
     12.4.  Key Endpoints  . . . . . . . . . . . . . . . . . . . . .  50
     12.5.  Offline Endpoints  . . . . . . . . . . . . . . . . . . .  50
   13. Conformance . . . . . . . . . . . . . . . . . . . . . . . . .  50
     13.1.  Tier L1: Object Layer  . . . . . . . . . . . . . . . . .  51
     13.2.  Tier L2: Grant Evaluation Layer  . . . . . . . . . . . .  51
     13.3.  Tier L3: Workflow Layer  . . . . . . . . . . . . . . . .  51
     13.4.  Tier L4: Federation Layer  . . . . . . . . . . . . . . .  52
       13.4.1.  L3-PQ and L4-PQ (Post-Quantum Only)  . . . . . . . .  52
     13.5.  Conformance by Sector  . . . . . . . . . . . . . . . . .  52
   14. Security Considerations . . . . . . . . . . . . . . . . . . .  53
     14.1.  OID Integrity  . . . . . . . . . . . . . . . . . . . . .  53
     14.2.  Signature Verification . . . . . . . . . . . . . . . . .  53
       14.2.1.  When signing is required . . . . . . . . . . . . . .  53
       14.2.2.  Verifier obligations . . . . . . . . . . . . . . . .  54
       14.2.3.  Offline Key Distribution . . . . . . . . . . . . . .  55
     14.3.  Tenant Isolation . . . . . . . . . . . . . . . . . . . .  56
     14.4.  Delegation Chain Security  . . . . . . . . . . . . . . .  56

Shovan                   Expires 1 January 2027                 [Page 3]
Internet-Draft                     GAP                         June 2026

     14.5.  Provisional Block Fail-Closed  . . . . . . . . . . . . .  56
     14.6.  Workflow Signal Injection  . . . . . . . . . . . . . . .  57
     14.7.  Idempotency Cache Staleness  . . . . . . . . . . . . . .  57
     14.8.  Negative Numeric Scope Values  . . . . . . . . . . . . .  57
     14.9.  Bearer Token Security  . . . . . . . . . . . . . . . . .  57
       14.9.1.  Self-Sovereign Credential Mode . . . . . . . . . . .  57
   15. IANA Considerations . . . . . . . . . . . . . . . . . . . . .  58
   16. Normative References  . . . . . . . . . . . . . . . . . . . .  58
   17. Informative References  . . . . . . . . . . . . . . . . . . .  58
   Appendix A.  Normative References . . . . . . . . . . . . . . . .  59
   Appendix B.  Informative References . . . . . . . . . . . . . . .  59
   Appendix C.  Acknowledgments  . . . . . . . . . . . . . . . . . .  59
   Author's Address  . . . . . . . . . . . . . . . . . . . . . . . .  59

1.  Introduction

   As AI agents and automated systems proliferate, the absence of a
   common authorization and auditability layer creates compounding risk.
   Each environment (AI agent frameworks, smart home hubs, industrial
   SCADA systems, game engines, medical devices) builds its own
   integration layer with its own permission model, its own audit log
   (if any), and its own revocation mechanism.  None share an audit
   trail.  None speak to each other.  None produce verifiable, portable
   evidence of what was authorized.

   GAP is the fabric underneath all of them.  An actor declares what it
   can do.  An operator grants what it may do, under what conditions.
   Every invocation is evaluated against an active grant.  Every gate
   decision (allow, deny, defer, timeout) produces a content-addressed,
   immutable receipt (signed at L2+).  The same protocol that governs an
   AI agent's tool calls also governs an industrial valve controller, a
   door lock, and a medication infusion pump.

1.1.  Motivation

   Several properties motivate a unified open protocol:

   Portability:  Authorization evidence must be independently verifiable
      by any party with access to the gateway's public key, without
      contacting the gateway.

   Non-repudiation:  A signed, content-addressed receipt cannot be
      silently modified after issuance.

   Composability:  Delegation chains, multi-party approvals, and
      workflow orchestration must compose from the same primitive
      objects.

Shovan                   Expires 1 January 2027                 [Page 4]
Internet-Draft                     GAP                         June 2026

   Physical-safety applicability:  The protocol must carry safety
      classification metadata and define fail-closed semantics for
      capabilities that affect physical systems.

1.2.  Scope

   This document defines:

   *  The CDRO (Content-addressed, Deterministic, Replayable Object)
      wire format

   *  OID computation (canonical JSON + SHA-256)

   *  The four lifecycle phases: Declare, Grant, Invoke, Receipt

   *  Grant evaluation algorithm including scope narrowing and
      delegation

   *  Workflow orchestration and Human-in-the-Loop (HITL) signaling

   *  Revocation (immediate, scheduled, provisional block, and L3
      quorum)

   *  The HTTP API surface for GAP-conformant gateways

   *  Conformance tiers L1 through L4

   Security considerations for implementing a GAP gateway are specified
   in {{security-considerations}}. The protocol specification is
   dedicated to the public domain under CC0 1.0 Universal.
   Implementations of this specification may be licensed independently.

1.3.  Conventions and Definitions

   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 [RFC2119] [RFC8174] when, and only when, they appear in all
   capitals, as shown here.

   The following terms are used throughout this document:

   Actor:  An entity (AI agent, service, device, or human user) that can
      declare capabilities and invoke them subject to grants.

   Operator:  The human or organizational entity that issues grants.  An
      operator holds the root of trust for a tenant.

Shovan                   Expires 1 January 2027                 [Page 5]
Internet-Draft                     GAP                         June 2026

   Tenant:  An isolated authorization domain.  CDROs never implicitly
      cross tenant boundaries.

   CDRO:  Content-addressed, Deterministic, Replayable Object.  The base
      unit of the GAP object model.

   OID:  Object Identifier.  A content-addressed string of the form
      sha256:<hex> computed over the canonical JSON serialization of a
      CDRO's body fields.

   Gateway:  A GAP-conformant server that evaluates invocations against
      active grants and produces signed decision receipts.

   Physical-safety capability:  A capability whose invocation can cause
      irreversible physical consequences.  Identified by
      physical_safety: true in the capability declaration.

2.  Object Model

2.1.  CDRO Envelope

   Every GAP record is a CDRO.  A CDRO is a JSON object with the
   following fields:

Shovan                   Expires 1 January 2027                 [Page 6]
Internet-Draft                     GAP                         June 2026

    +=====================+==========+================================+
    | Field               | Required | Description                    |
    +=====================+==========+================================+
    | oid                 | yes      | sha256:<64 hex chars>,         |
    |                     |          | content-addressed identifier   |
    +---------------------+----------+--------------------------------+
    | type                | yes      | One of the gap:* type strings  |
    |                     |          | defined in this document       |
    +---------------------+----------+--------------------------------+
    | gap_version         | yes      | Always "1.0" for this          |
    |                     |          | specification                  |
    +---------------------+----------+--------------------------------+
    | tenant_id           | yes      | The owning tenant identifier   |
    +---------------------+----------+--------------------------------+
    | created_at_ms       | yes      | Unix epoch milliseconds        |
    +---------------------+----------+--------------------------------+
    | created_by          | yes      | OID of the actor creating this |
    |                     |          | record                         |
    +---------------------+----------+--------------------------------+
    | body                | yes      | Type-specific payload          |
    +---------------------+----------+--------------------------------+
    | signature           | no       | Ed25519 signature over the     |
    |                     |          | canonical envelope, base64url  |
    +---------------------+----------+--------------------------------+
    | signature_key_id    | no       | Identifies the signing key     |
    +---------------------+----------+--------------------------------+
    | signature_algorithm | no       | Signature algorithm: Ed25519,  |
    |                     |          | ML-DSA-65, or Ed25519+ML-DSA-  |
    |                     |          | 65.  Verifiers MUST NOT guess  |
    |                     |          | the algorithm from key length. |
    +---------------------+----------+--------------------------------+
    | supersedes          | no       | OID of the CDRO this record    |
    |                     |          | replaces                       |
    +---------------------+----------+--------------------------------+

                                  Table 1

   The defined type values are:

Shovan                   Expires 1 January 2027                 [Page 7]
Internet-Draft                     GAP                         June 2026

    +===============================+=================================+
    | Type string                   | Body type                       |
    +===============================+=================================+
    | gap:capability_declaration    | CapabilityDeclarationBody       |
    +-------------------------------+---------------------------------+
    | gap:capability_grant          | CapabilityGrantBody             |
    +-------------------------------+---------------------------------+
    | gap:capability_invocation     | CapabilityInvocationBody        |
    +-------------------------------+---------------------------------+
    | gap:decision_receipt          | GapDecisionReceiptBody          |
    +-------------------------------+---------------------------------+
    | gap:revocation_event          | RevocationEventBody             |
    +-------------------------------+---------------------------------+
    | gap:workflow_definition       | WorkflowDefinitionBody          |
    +-------------------------------+---------------------------------+
    | gap:workflow_instance         | WorkflowInstanceBody            |
    +-------------------------------+---------------------------------+
    | gap:stage_transition          | StageTransitionBody             |
    +-------------------------------+---------------------------------+
    | gap:channel_event             | ChannelEventBody                |
    +-------------------------------+---------------------------------+
    | gap:break_glass_token         | BreakGlassTokenBody             |
    +-------------------------------+---------------------------------+
    | gap:local_override_credential | LocalOverrideCredentialBody     |
    +-------------------------------+---------------------------------+
    | gap:lca_root                  | LcaRootBody ({                  |
    |                               | root_public_key_base64: string, |
    |                               | algorithm: string, tenant_id:   |
    |                               | string, valid_from_ms: number,  |
    |                               | expires_at_ms: number })        |
    +-------------------------------+---------------------------------+
    | gap:erasure_event             | ErasureEventBody                |
    +-------------------------------+---------------------------------+
    | gap:orchestration_chain       | OrchestrationChainBody [DESIGN] |
    +-------------------------------+---------------------------------+
    | gap:consent_record            | ConsentRecordBody [DESIGN]      |
    +-------------------------------+---------------------------------+
    | gap:pip_response              | PipResponseBody [DESIGN]        |
    +-------------------------------+---------------------------------+

                                  Table 2

2.2.  OID Computation

   An OID is computed as follows:

   1.  Take the CDRO envelope object.

Shovan                   Expires 1 January 2027                 [Page 8]
Internet-Draft                     GAP                         June 2026

   2.  Remove the fields oid, gap_version, signature, signature_key_id,
       and supersedes.

   3.  Canonicalize the resulting object per {{canonical-json}}.

   4.  Compute SHA-256 over the UTF-8 encoding of the canonical string.

   5.  Hex-encode the digest (lowercase).

   6.  Prepend sha256:.

   The result is the OID for this CDRO.  The same input MUST always
   produce the same OID.  Implementations MUST compute and store the OID
   before signing so that the signature covers the canonical content.

2.3.  Canonical JSON

   The canonical JSON form of a value is defined recursively:

   *  *string*: standard JSON string encoding

   *  *number*: standard JSON number encoding (no trailing zeros beyond
      the decimal point)

   *  *boolean*: true or false

   *  *null*: excluded; null values MUST be omitted from canonical form

   *  *array*: [ + elements joined by , (order preserved, null elements
      omitted) + ]

   *  *object*: { + key-value pairs joined by ,, keys sorted
      lexicographically (Unicode code-point order), pairs with null
      values omitted, keys encoded as JSON strings + }

   Note: GAP canonical JSON is NOT identical to JSON Canonicalization
   Scheme (JCS) [RFC8785].  The primary difference is that GAP omits
   null values while JCS preserves them.  Implementors MUST NOT use a
   JCS library for OID computation without first stripping null values.

3.  Phase 1: Declare

3.1.  Purpose

   Before any capability can be granted or invoked, the actor offering
   it MUST publish a CapabilityDeclaration.  The declaration is an
   immutable record of what the actor can do, the safety classification
   of each capability, and optional scope constraints.

Shovan                   Expires 1 January 2027                 [Page 9]
Internet-Draft                     GAP                         June 2026

3.2.  CapabilityDeclarationBody

       +===============+==========+================================+
       | Field         | Required | Description                    |
       +===============+==========+================================+
       | actor_type    | yes      | One of: service, device,       |
       |               |          | agent, human_user, mcp_server, |
       |               |          | gateway_subsystem, skill       |
       +---------------+----------+--------------------------------+
       | actor_id      | yes      | Stable identifier for the      |
       |               |          | declaring actor                |
       +---------------+----------+--------------------------------+
       | actor_name    | yes      | Human-readable name            |
       +---------------+----------+--------------------------------+
       | actor_version | yes      | Semantic version string        |
       +---------------+----------+--------------------------------+
       | capabilities  | yes      | Array of CapabilitySpec        |
       +---------------+----------+--------------------------------+

                                  Table 3

   Each CapabilitySpec:

   +======================+========+==============================================+
   |Field                 |Required|Description                                   |
   +======================+========+==============================================+
   |capability            |yes     |Dotted-taxonomy capability name (e.g.         |
   |                      |        |home.lock.engage)                             |
   +----------------------+--------+----------------------------------------------+
   |safety_class          |yes     |A (reversible), B (state-changing), or C      |
   |                      |        |(irreversible/critical)                       |
   +----------------------+--------+----------------------------------------------+
   |physical_safety       |no      |Boolean. true if invocation can cause physical|
   |                      |        |harm                                          |
   +----------------------+--------+----------------------------------------------+
   |description           |no      |Human-readable description                    |
   +----------------------+--------+----------------------------------------------+
   |scope_narrowing_schema|no      |JSON Schema describing allowed scope_narrowing|
   |                      |        |keys                                          |
   +----------------------+--------+----------------------------------------------+
   |privacy_classification|no      |none, pii, phi, financial, or privileged      |
   |                      |        |(legally privileged content: attorney-client, |
   |                      |        |work product, common-interest; the gateway    |
   |                      |        |routes receipts to a privilege-isolated store |
   |                      |        |and suppresses them from standard list        |
   |                      |        |endpoints)                                    |
   +----------------------+--------+----------------------------------------------+
   |require_signed_receipt|no      |Boolean.  When true, the gateway MUST attach a|

Shovan                   Expires 1 January 2027                [Page 10]
Internet-Draft                     GAP                         June 2026

   |                      |        |cryptographic signature to every decision     |
   |                      |        |receipt for this capability.  When false, the |
   |                      |        |gateway SHOULD omit signing even on a server  |
   |                      |        |that signs by default (useful for high-       |
   |                      |        |frequency low-risk capabilities where signing |
   |                      |        |cost outweighs benefit).  When absent, the    |
   |                      |        |gateway applies its configured default signing|
   |                      |        |policy.  The operator MAY override this per-  |
   |                      |        |grant via                                     |
   |                      |        |GrantedCapabilityScope.require_signed_receipt.|
   +----------------------+--------+----------------------------------------------+
   |pii_args              |no      |Array of arg key strings whose values contain |
   |                      |        |PII, PHI, or NPI requiring tokenization before|
   |                      |        |storage.  The gateway MUST replace each listed|
   |                      |        |key's value with a keyed HMAC token (one-way, |
   |                      |        |using a per-tenant key) before constructing   |
   |                      |        |the invocation CDRO and receipt body.  The    |
   |                      |        |original value is used for capability         |
   |                      |        |execution by the adapter but MUST NOT be      |
   |                      |        |stored in any CDRO.                           |
   +----------------------+--------+----------------------------------------------+
   |privilege_protected   |no      |Boolean.  When true, the gateway: routes      |
   |                      |        |receipts to a privilege-isolated store;       |
   |                      |        |suppresses the receipt body from GET /receipts|
   |                      |        |list endpoint; requires an explicit attorney- |
   |                      |        |assertion header on fetch by OID; excludes the|
   |                      |        |receipt from automated compliance exports.    |
   |                      |        |Controls access routing, not deletion.  Add   |
   |                      |        |privilege_asserted to the compliance_tags     |
   |                      |        |vocabulary when this field is true.           |
   +----------------------+--------+----------------------------------------------+

                                  Table 4

   When privilege_protected: true is set on a CapabilitySpec, the
   gateway: routes receipts to a privilege-isolated store; suppresses
   the receipt body from GET /receipts list endpoint; requires an
   explicit attorney-assertion header on fetch by OID; excludes the
   receipt from automated compliance exports. privilege_protected
   controls access routing, not deletion.

   For capabilities with privacy_classification: phi or matching
   medical.* or financial.*, any arg key whose name is listed in
   pii_args MUST be tokenized before CDRO construction.  The canonical
   JSON used for OID computation is taken from the unencrypted args
   before tokenization so OID integrity is maintained.  A GET /v1/gap/
   receipts/:oid?include_pii=true path with elevated authorization
   serves authorized reviewers.

Shovan                   Expires 1 January 2027                [Page 11]
Internet-Draft                     GAP                         June 2026

3.3.  Supersession

   An actor MAY publish a new declaration superseding an existing one by
   setting supersedes to the OID of the prior declaration.  A gateway
   MUST treat the prior declaration as inactive once the superseding
   declaration is accepted.  A gateway MUST NOT accept a new declaration
   with the same actor_id unless it includes a valid supersedes pointer
   to the currently active declaration for that actor_id within the
   tenant.

3.4.  Ephemeral Actors

   Some actors have a lifecycle tied to a session, job, or deployment
   instance rather than a persistent identity.

   Add actor_lifecycle to CapabilityDeclarationBody:

   +=======================+==========+===============================+
   | Field                 | Required | Description                   |
   +=======================+==========+===============================+
   | actor_lifecycle       | no       | persistent (default) or       |
   |                       |          | ephemeral.  Ephemeral         |
   |                       |          | declarations are exempt from  |
   |                       |          | the supersession uniqueness   |
   |                       |          | rule; each declaration gets a |
   |                       |          | fresh OID and is valid for    |
   |                       |          | its session only.             |
   +-----------------------+----------+-------------------------------+
   | actor_instance_id     | no       | UUID or job ID distinguishing |
   |                       |          | this instance from others     |
   |                       |          | with the same actor_id.  When |
   |                       |          | present, two declarations     |
   |                       |          | with the same actor_id but    |
   |                       |          | different actor_instance_id   |
   |                       |          | MUST NOT be treated as        |
   |                       |          | superseding each other.       |
   +-----------------------+----------+-------------------------------+
   | session_expires_at_ms | no       | For ephemeral actors: when    |
   |                       |          | this session ends.  The       |
   |                       |          | gateway MUST auto-revoke all  |
   |                       |          | grants scoped to this         |
   |                       |          | actor_instance_id at this     |
   |                       |          | time.                         |
   +-----------------------+----------+-------------------------------+

                                 Table 5

Shovan                   Expires 1 January 2027                [Page 12]
Internet-Draft                     GAP                         June 2026

   Grants issued to an actor_id without actor_instance_id apply to all
   instances of that actor_id.  Grants scoped to a specific
   actor_instance_id expire with that instance.

3.5.  MCP Tool-Call Governance [DESIGN]

   Actors of type mcp_server expose tools via the Model Context Protocol
   (MCP) tools/list response.  A GAP gateway auto-generates
   CapabilityDeclarations from those responses.

   Capability names for MCP tools follow the pattern:
   mcp.<server_id>.<tool_name>.

   MUST: a gateway MUST reject any auto-generated capability name that
   starts with gap: or matches any normative capability name defined in
   this specification.  This prevents namespace pollution from attacker-
   controlled MCP servers that return malicious tools/list responses.

   An invocation for an MCP-originated capability MAY carry
   mcp_tool_call on the CapabilityInvocationBody:

     +==================+==========+=================================+
     | Field            | Required | Description                     |
     +==================+==========+=================================+
     | server_id        | yes      | Stable identifier for the MCP   |
     |                  |          | server                          |
     +------------------+----------+---------------------------------+
     | tool_name        | yes      | Name of the tool as returned by |
     |                  |          | tools/list                      |
     +------------------+----------+---------------------------------+
     | tool_schema_hash | no       | SHA-256 hash of the tool's JSON |
     |                  |          | schema, for drift detection     |
     +------------------+----------+---------------------------------+

                                  Table 6

3.6.  Identity Binding [DESIGN]

   A CapabilityDeclarationBody MAY carry an identity_binding object that
   ties the actor_oid to a real-world credential using a hardware-backed
   signature.

   The canonical binding payload is domain-separated:

"gap-identity-binding-v1" + ":" + actor_oid + ":" + tenant_id + ":" + credential_identifier

   The binding_signature field carries the credential holder's signature
   over the canonical binding payload.

Shovan                   Expires 1 January 2027                [Page 13]
Internet-Draft                     GAP                         June 2026

   IdentityBinding fields:

    +=======================+==========+==============================+
    | Field                 | Required | Description                  |
    +=======================+==========+==============================+
    | credential_kind       | yes      | One of the normative values  |
    |                       |          | below                        |
    +-----------------------+----------+------------------------------+
    | credential_identifier | yes      | Stable identifier within the |
    |                       |          | credential_kind namespace    |
    +-----------------------+----------+------------------------------+
    | binding_signature     | yes      | Signature over canonical     |
    |                       |          | binding payload, base64url   |
    +-----------------------+----------+------------------------------+
    | binding_alg           | yes      | Signature algorithm (e.g.    |
    |                       |          | Ed25519, ES256, RS256)       |
    +-----------------------+----------+------------------------------+
    | bound_at_ms           | yes      | Unix epoch ms when binding   |
    |                       |          | was established              |
    +-----------------------+----------+------------------------------+
    | issuer                | no       | Issuer identifier (CA DN,    |
    |                       |          | OIDC issuer URL, etc.)       |
    +-----------------------+----------+------------------------------+
    | expires_at_ms         | no       | Expiry of the binding;       |
    |                       |          | absent means no expiry       |
    +-----------------------+----------+------------------------------+

                                  Table 7

   Normative credential_kind values:

Shovan                   Expires 1 January 2027                [Page 14]
Internet-Draft                     GAP                         June 2026

     +======================+=======================================+
     | Value                | Credential type                       |
     +======================+=======================================+
     | piv_cac              | US Federal PIV or Common Access Card  |
     +----------------------+---------------------------------------+
     | x509                 | X.509 certificate                     |
     +----------------------+---------------------------------------+
     | fido2                | FIDO2 / WebAuthn credential           |
     +----------------------+---------------------------------------+
     | tpm_attestation      | TPM 2.0 attestation key               |
     +----------------------+---------------------------------------+
     | oidc_sub             | OIDC subject claim from a trusted IdP |
     +----------------------+---------------------------------------+
     | spiffe_svid          | SPIFFE SVID (X.509 or JWT)            |
     +----------------------+---------------------------------------+
     | wallet_address       | Cryptographic wallet address          |
     +----------------------+---------------------------------------+
     | professional_license | Licensed professional credential      |
     |                      | (e.g. medical, legal)                 |
     +----------------------+---------------------------------------+

                                 Table 8

3.7.  Compartment-Based Access Scoping [DESIGN]

   A compartment field MAY be added to CapabilityDeclarationBody,
   CapabilityGrantBody, and CapabilityInvocationBody.

   Accepted values: UNCLASS, CUI, or any reverse-domain operator-defined
   label (e.g. com.acme.project-alpha).

   MUST: at invocation time, if the grant carries a compartment, the
   invocation compartment MUST exactly match.  A compartment mismatch
   MUST be treated as a denial.

   MUST: a gateway MUST return HTTP 404 (not HTTP 403) when the invoking
   actor's compartment level is insufficient to know that a resource
   exists.  OID existence MUST NOT leak across compartment boundaries.

   Cross-compartment access requires a bridge grant issued through a
   TPI-gated HITL workflow.  Bridge grants MUST be issued by an operator
   whose declaration carries both compartment labels and MUST produce a
   compliance_tag of cross_compartment_bridge on the receipt.

4.  Phase 2: Grant

Shovan                   Expires 1 January 2027                [Page 15]
Internet-Draft                     GAP                         June 2026

4.1.  Purpose

   A CapabilityGrant authorizes a specific actor (the grantee) to invoke
   one or more capabilities declared by a specific actor (the declarer),
   subject to optional scope constraints, time limits, and
   preconditions.

4.2.  CapabilityGrantBody

   +==============================+==========+========================+
   | Field                        | Required | Description            |
   +==============================+==========+========================+
   | grantee                      | yes      | ActorRef identifying   |
   |                              |          | the authorized actor   |
   +------------------------------+----------+------------------------+
   | capability_scopes            | yes      | Array of               |
   |                              |          | GrantedCapabilityScope |
   +------------------------------+----------+------------------------+
   | granted_at_ms                | yes      | Unix epoch             |
   |                              |          | milliseconds           |
   +------------------------------+----------+------------------------+
   | granted_by                   | yes      | OID of the operator    |
   |                              |          | issuing the grant      |
   +------------------------------+----------+------------------------+
   | expires_at_ms                | no       | Expiry timestamp in    |
   |                              |          | milliseconds           |
   +------------------------------+----------+------------------------+
   | parent_grant_oid             | no       | OID of the parent      |
   |                              |          | grant (delegation      |
   |                              |          | chains)                |
   +------------------------------+----------+------------------------+
   | limits                       | no       | InvocationLimits       |
   +------------------------------+----------+------------------------+
   | additional_preconditions     | no       | Array of Precondition  |
   +------------------------------+----------+------------------------+
   | timestamp_window_seconds     | no       | For safety_class C     |
   |                              |          | without                |
   |                              |          | physical_safety:       |
   |                              |          | override for the       |
   |                              |          | default timestamp      |
   |                              |          | validation window in   |
   |                              |          | seconds.  Gateway      |
   |                              |          | applies its default if |
   |                              |          | absent.                |
   +------------------------------+----------+------------------------+
   | offline_grace_seconds        | no       | Additional seconds     |
   |                              |          | beyond grant expiry    |
   |                              |          | during which offline   |

Shovan                   Expires 1 January 2027                [Page 16]
Internet-Draft                     GAP                         June 2026

   |                              |          | provisional receipts   |
   |                              |          | are accepted at        |
   |                              |          | reconciliation.        |
   |                              |          | Defaults to 0.         |
   +------------------------------+----------+------------------------+
   | max_grant_offline_ttl_ms     | no       | Maximum duration any   |
   |                              |          | device may use this    |
   |                              |          | grant without syncing  |
   |                              |          | to the gateway.  After |
   |                              |          | this window, further   |
   |                              |          | invocations MUST be    |
   |                              |          | denied until the       |
   |                              |          | device reconnects.     |
   +------------------------------+----------+------------------------+
   | max_revocation_bundle_age_ms | no       | Maximum acceptable age |
   |                              |          | of a revocation bundle |
   |                              |          | for this grant.        |
   |                              |          | Devices MUST deny      |
   |                              |          | physical_safety/class  |
   |                              |          | C invocations if the   |
   |                              |          | bundle is older than   |
   |                              |          | this value.            |
   +------------------------------+----------+------------------------+

                                 Table 9

   Each GrantedCapabilityScope:

Shovan                   Expires 1 January 2027                [Page 17]
Internet-Draft                     GAP                         June 2026

   +============================+==========+==========================+
   | Field                      | Required | Description              |
   +============================+==========+==========================+
   | capability                 | yes      | Capability name or       |
   |                            |          | wildcard pattern         |
   +----------------------------+----------+--------------------------+
   | capability_declaration_oid | yes*     | OID of the               |
   |                            |          | CapabilityDeclaration.   |
   |                            |          | REQUIRED when            |
   |                            |          | safety_class is C or     |
   |                            |          | physical_safety is true  |
   +----------------------------+----------+--------------------------+
   | scope_narrowing            | no       | Object constraining      |
   |                            |          | invocation arguments     |
   |                            |          | (see Scope Narrowing     |
   |                            |          | Evaluation)              |
   +----------------------------+----------+--------------------------+
   | additional_preconditions   | no       | Array of Precondition    |
   |                            |          | evaluated at invocation  |
   |                            |          | time for this scope (in  |
   |                            |          | addition to grant-level  |
   |                            |          | preconditions)           |
   +----------------------------+----------+--------------------------+
   | require_signed_receipt     | no       | Operator override for    |
   |                            |          | receipt signing.  When   |
   |                            |          | set, takes precedence    |
   |                            |          | over the capability      |
   |                            |          | declaration's            |
   |                            |          | require_signed_receipt.  |
   |                            |          | Allows an operator to    |
   |                            |          | require signing for a    |
   |                            |          | capability the actor     |
   |                            |          | declared unsigned, or    |
   |                            |          | suppress signing for a   |
   |                            |          | high-frequency           |
   |                            |          | capability the actor     |
   |                            |          | flagged as requiring it. |
   +----------------------------+----------+--------------------------+

                                 Table 10

   InvocationLimits:

Shovan                   Expires 1 January 2027                [Page 18]
Internet-Draft                     GAP                         June 2026

           +==================+================================+
           | Field            | Description                    |
           +==================+================================+
           | max_invocations  | Maximum total invocation count |
           +------------------+--------------------------------+
           | max_per_window   | Maximum invocations within a   |
           |                  | rolling time window            |
           +------------------+--------------------------------+
           | window_seconds   | Window duration in seconds     |
           +------------------+--------------------------------+
           | aggregate_limits | Array of AggregateLimitEntry   |
           +------------------+--------------------------------+

                                  Table 11

   AggregateLimitEntry:

          +================+====================================+
          | Field          | Description                        |
          +================+====================================+
          | key            | The invocation argument key to sum |
          +----------------+------------------------------------+
          | max            | Maximum rolling sum (non-negative) |
          +----------------+------------------------------------+
          | window_seconds | Rolling window duration in seconds |
          +----------------+------------------------------------+

                                  Table 12

4.2.1.  Cross-Grant Aggregate Limit Groups

   An aggregate_limit_group field on InvocationLimits references a named
   shared limit pool at the tenant level.  Multiple grants referencing
   the same pool share a single rolling counter enforced atomically by
   the gateway.

    +=======================+==========+=============================+
    | Field                 | Required | Description                 |
    +=======================+==========+=============================+
    | aggregate_limit_group | no       | Named pool identifier.  All |
    |                       |          | grants with the same        |
    |                       |          | aggregate_limit_group share |
    |                       |          | rolling aggregate counters  |
    |                       |          | defined in the tenant's     |
    |                       |          | pool configuration.         |
    +-----------------------+----------+-----------------------------+

                                 Table 13

Shovan                   Expires 1 January 2027                [Page 19]
Internet-Draft                     GAP                         June 2026

   The gateway MUST maintain atomic counters per pool and MUST deny any
   invocation from any grant in the pool that would exceed the pool
   ceiling.  Pool configuration is out of scope for this specification
   and is implementation-defined.

4.3.  Precondition Kind Registry

   A normative precondition_kind registry defines semantics for standard
   kinds.  Custom kinds use reverse-domain prefixes (e.g.
   com.example.custom_check).

   +=====================+=====================+=======================+
   | Kind                | Required args       | Description           |
   +=====================+=====================+=======================+
   | time_window         | days_of_week,       | Invocation only       |
   |                     | start_hour_utc,     | permitted in the      |
   |                     | end_hour_utc        | specified UTC time    |
   |                     |                     | window                |
   +---------------------+---------------------+-----------------------+
   | rate_limit          | max_count,          | Maximum invocations   |
   |                     | window_seconds      | per rolling window    |
   |                     |                     | (cross-invocation,    |
   |                     |                     | tracked by gateway)   |
   +---------------------+---------------------+-----------------------+
   | sanctions_screening | list_version,       | Screens named arg     |
   |                     | screening_provider, | keys against a        |
   |                     | subject_fields      | sanctions list.       |
   |                     |                     | Gateway MUST record   |
   |                     |                     | screening result OID  |
   |                     |                     | in the receipt.       |
   |                     |                     | Gateway MUST NOT      |
   |                     |                     | proceed to execution  |
   |                     |                     | if result is denied.  |
   +---------------------+---------------------+-----------------------+
   | external_pip        | endpoint_url,       | POSTs invocation      |
   |                     | cache_ttl_seconds,  | args to an external   |
   |                     | subject_fields,     | Policy Information    |
   |                     | pip_response_oid    | Point and evaluates   |
   |                     | (optional)          | the boolean allowed   |
   |                     |                     | response.  Result is  |
   |                     |                     | cached per (tenant,   |
   |                     |                     | capability, args-     |
   |                     |                     | hash) for             |
   |                     |                     | cache_ttl_seconds.    |
   |                     |                     | When                  |
   |                     |                     | pip_response_oid is   |
   |                     |                     | present the response  |
   |                     |                     | is ENFORCING (see     |

Shovan                   Expires 1 January 2027                [Page 20]
Internet-Draft                     GAP                         June 2026

   |                     |                     | Signed PIP Response   |
   |                     |                     | below).               |
   +---------------------+---------------------+-----------------------+
   | inventory_check     | resource_key,       | Verifies the named    |
   |                     | min_available       | resource has          |
   |                     |                     | sufficient            |
   |                     |                     | availability before   |
   |                     |                     | execution             |
   +---------------------+---------------------+-----------------------+
   | token_budget        | model_scope,        | [DESIGN] Rolling      |
   |                     | max_input_tokens,   | token-budget cap      |
   |                     | max_output_tokens,  | evaluated             |
   |                     | max_cost_usd,       | post_invoke.  See     |
   |                     | window_seconds      | Token Budget          |
   |                     |                     | Governance below.     |
   +---------------------+---------------------+-----------------------+
   | consent_current     | (none)              | [DESIGN] Evaluates    |
   |                     |                     | at invocation time    |
   |                     |                     | whether the actor's   |
   |                     |                     | most recent           |
   |                     |                     | gap:consent_record    |
   |                     |                     | for the capability's  |
   |                     |                     | context has           |
   |                     |                     | consented: true.      |
   |                     |                     | The gateway MUST NOT  |
   |                     |                     | use the idempotency   |
   |                     |                     | cache for this        |
   |                     |                     | evaluation.  See      |
   |                     |                     | Consent Version       |
   |                     |                     | Chain below.          |
   +---------------------+---------------------+-----------------------+

                                  Table 14

4.3.1.  Token Budget Governance [DESIGN]

   The token_budget precondition kind governs token consumption across
   invocations that call an LLM.  Evaluation timing: post_invoke
   (settled after execution).  The gateway writes the actual consumption
   to token_consumption on the decision receipt.

   TokenBudgetArgs:

Shovan                   Expires 1 January 2027                [Page 21]
Internet-Draft                     GAP                         June 2026

      +===================+==========+=============================+
      | Field             | Required | Description                 |
      +===================+==========+=============================+
      | model_scope       | yes      | Shell-glob pattern matching |
      |                   |          | model IDs (e.g. anthropic/  |
      |                   |          | claude-*)                   |
      +-------------------+----------+-----------------------------+
      | max_input_tokens  | no       | Maximum input tokens within |
      |                   |          | window_seconds              |
      +-------------------+----------+-----------------------------+
      | max_output_tokens | no       | Maximum output tokens       |
      |                   |          | within window_seconds       |
      +-------------------+----------+-----------------------------+
      | max_cost_usd      | no       | Maximum cost in USD within  |
      |                   |          | window_seconds.  [MODELED]; |
      |                   |          | not authoritative until a   |
      |                   |          | conformance vector exists.  |
      +-------------------+----------+-----------------------------+
      | window_seconds    | yes      | Rolling window length in    |
      |                   |          | seconds                     |
      +-------------------+----------+-----------------------------+

                                 Table 15

   The gateway settles actual consumption onto the receipt via the
   token_consumption field (see Phase 4: Receipt).  Uses the
   aggregate_limit_group counter mechanism for cross-grant budgeting.

4.3.2.  Signed PIP Response [DESIGN]

   When an external_pip precondition carries pip_response_oid, the
   referenced CDRO is a gap:pip_response that the external PIP emitted
   and the gateway re-signed.

   Distinction:

   *  Unsigned external reads (no pip_response_oid): ADVISORY.  The
      response influences the gate decision but MUST NOT be the sole
      basis for an allow outcome.

   *  Signed gap:pip_response (with pip_response_oid): ENFORCING.  The
      gateway MAY use it as the sole basis for allow or deny.

   MUST: if pip_response_oid is present, the gateway MUST verify the
   CDRO signature before treating the response as enforcing.

   PipResponseBody:

Shovan                   Expires 1 January 2027                [Page 22]
Internet-Draft                     GAP                         June 2026

   +====================+==========+==================================+
   | Field              | Required | Description                      |
   +====================+==========+==================================+
   | pip_endpoint       | yes      | URL of the external PIP endpoint |
   +--------------------+----------+----------------------------------+
   | request_args_hash  | yes      | SHA-256 hash of the canonical    |
   |                    |          | request args sent to the PIP     |
   +--------------------+----------+----------------------------------+
   | response_body_hash | yes      | SHA-256 hash of the raw response |
   |                    |          | body from the PIP                |
   +--------------------+----------+----------------------------------+
   | response_summary   | no       | Human-readable summary (not      |
   |                    |          | authoritative)                   |
   +--------------------+----------+----------------------------------+
   | evaluated_at_ms    | yes      | Unix epoch ms when the PIP was   |
   |                    |          | queried                          |
   +--------------------+----------+----------------------------------+
   | cache_ttl_ms       | yes      | How long (ms) this response may  |
   |                    |          | be cached by the gateway         |
   +--------------------+----------+----------------------------------+
   | pip_signature      | no       | Optional signature from the PIP  |
   |                    |          | itself, base64url                |
   +--------------------+----------+----------------------------------+
   | pip_signature_alg  | no       | Algorithm used for pip_signature |
   +--------------------+----------+----------------------------------+

                                 Table 16

   For any invocation of financial.wire.initiate or
   financial.payment.initiate, a gateway MUST enforce
   sanctions_screening precondition evaluation even when absent from the
   grant's additional_preconditions.  The gateway MUST reject the
   invocation and produce a denial receipt if the screening result is
   denied.

4.4.  Scope Narrowing Evaluation

   When a grant contains scope_narrowing, a gateway MUST enforce the
   following rules at invocation time:

   1.  For each key K in scope_narrowing:

       *  The invocation arguments MUST contain key K.  If absent, the
          invocation MUST be denied.

       *  If the scope value is a string: args[K] MUST equal the scope
          value (exact match, case-sensitive).  Key names are exact and
          singular/plural variants are distinct keys.

Shovan                   Expires 1 January 2027                [Page 23]
Internet-Draft                     GAP                         June 2026

       *  If the scope value is a boolean: args[K] MUST equal the scope
          value.

       *  If the scope value is a number and the key name has the prefix
          min_: args[K] MUST be greater than or equal to the scope value
          (lower bound).

       *  If the scope value is a number and the key name does not have
          the prefix min_: args[K] MUST be less than or equal to the
          scope value (upper bound).  For physical_safety: true
          capabilities, negative values for args[K] MUST be rejected
          even when they would satisfy the upper-bound check.

       *  If the scope value is a string array: args[K] MUST be a member
          of the array.

   Dot-path expansion: A scope_narrowing key containing a dot (e.g.
   position.x) evaluates against the nested arg path args.position.x.
   This enables constraint of nested arg structures without requiring
   flat arg schemas.

   Envelope constraint (optional): when a scope_narrowing value is an
   object with $constraint_oid, the gateway evaluates the named
   constraint CDRO (a pre-registered function) against the full args
   object, returning boolean.  This enables correlated multi-axis
   constraints (e.g. motor speed AND torque within a 2D operating
   envelope).

4.5.  Granted-By Verification

   A gateway MUST verify at grant acceptance time that body.granted_by
   OID matches the actor OID associated with the authenticated Bearer
   token or SSC credential.  A grant where these do not match MUST be
   rejected with HTTP 403.

4.6.  Delegation

   A grant MAY delegate authority to a sub-grantee by setting
   parent_grant_oid.  A gateway MUST enforce all of the following when
   evaluating a delegated grant:

   1.  The granted_by field of the child grant MUST equal the
       grantee.actor_oid of the parent grant.

   2.  For every capability scope in the child grant, a matching scope
       MUST exist in the parent grant (using capability pattern matching
       per {{patterns}}).

Shovan                   Expires 1 January 2027                [Page 24]
Internet-Draft                     GAP                         June 2026

   3.  For every key in the child grant's scope_narrowing, the child
       value MUST satisfy the subset rule against the parent value for
       the same key:

       *  String: child value MUST equal parent value.

       *  String array: every element of the child array MUST appear in
          the parent array.

       *  Number (upper bound, no min_ prefix): child value MUST be less
          than or equal to the parent value.

       *  Number (lower bound, min_ prefix): child value MUST be greater
          than or equal to the parent value.

       *  Key present in parent but absent in child: MUST be denied (a
          child cannot drop a parent constraint).

   4.  The child grant MUST NOT increase max_delegation_depth beyond the
       parent's value.  For physical_safety: true grants,
       max_delegation_depth MUST default to 0 (no further delegation)
       when absent.

   5.  For physical_safety: true grants, the child MUST inherit all
       additional_preconditions from all ancestors in the chain.

   6.  At invocation time, the gateway MUST re-validate that all grants
       in the delegation chain are non-expired and non-revoked.

4.7.  Capability Pattern Matching

   A capability pattern matches a capability name as follows:

   *  * matches any capability name (match-all).

   *  prefix.* matches direct children only (single path segment).
      game.* matches game.session but NOT game.admin.delete.users.

   *  prefix.** matches all descendants recursively. game.** matches all
      nested paths under game, including game.session,
      game.admin.delete.users, etc.  The prefix itself also matches
      (game.** matches game).

   *  An exact string matches only that exact name (no wildcard).

   Two wildcard levels are defined: prefix.* matches direct children
   only (single path segment); prefix.** matches all descendants
   recursively.  Implementations MUST support both levels.

Shovan                   Expires 1 January 2027                [Page 25]
Internet-Draft                     GAP                         June 2026

5.  Phase 3: Invoke

5.1.  Purpose

   A CapabilityInvocation is the actor's request to exercise a granted
   capability.  The gateway evaluates the invocation against active
   grants and produces a GapDecisionReceipt.

5.2.  CapabilityInvocationBody

   +=================+==========+=====================================+
   | Field           | Required | Description                         |
   +=================+==========+=====================================+
   | caller          | yes      | CallerRef (actor_type, actor_oid,   |
   |                 |          | grant_oid)                          |
   +-----------------+----------+-------------------------------------+
   | capability      | yes      | The capability being invoked        |
   +-----------------+----------+-------------------------------------+
   | args            | yes      | Invocation arguments (flat JSON     |
   |                 |          | object)                             |
   +-----------------+----------+-------------------------------------+
   | invoked_at_ms   | yes      | Client-supplied or server-stamped   |
   |                 |          | timestamp                           |
   +-----------------+----------+-------------------------------------+
   | idempotency_key | no       | Client-provided deduplication key   |
   +-----------------+----------+-------------------------------------+
   | client_event_ms | no       | Unix epoch ms when the action       |
   |                 |          | originally occurred in the caller's |
   |                 |          | reference frame (game-world time,   |
   |                 |          | clinical queue time, SCADA scan     |
   |                 |          | cycle).  Populated by the client;   |
   |                 |          | not used for replay prevention.     |
   |                 |          | Stored in receipt for audit.        |
   +-----------------+----------+-------------------------------------+
   | queued_at_ms    | no       | Unix epoch ms when the invocation   |
   |                 |          | was enqueued for submission (e.g.   |
   |                 |          | at reconnect after offline period). |
   |                 |          | Optional; aids debugging of         |
   |                 |          | delivery latency.                   |
   +-----------------+----------+-------------------------------------+

                                 Table 17

5.3.  Timestamp Validation

   Timestamp validation is per safety_class:

Shovan                   Expires 1 January 2027                [Page 26]
Internet-Draft                     GAP                         June 2026

   +============+===============+================+========================+
   |safety_class|physical_safety|Max             |Gateway behavior        |
   |            |               |client_timestamp|                        |
   |            |               |age             |                        |
   +============+===============+================+========================+
   |A           |any            |5 minutes       |Reject if invoked_at_ms |
   |            |               |                |(client-supplied) is    |
   |            |               |                |more than 5 minutes in  |
   |            |               |                |the past                |
   +------------+---------------+----------------+------------------------+
   |B           |any            |120 seconds     |Reject if more than 120 |
   |            |               |                |seconds in the past     |
   +------------+---------------+----------------+------------------------+
   |C           |false          |operator-       |Grant field             |
   |            |               |configurable    |timestamp_window_seconds|
   |            |               |                |sets the window; gateway|
   |            |               |                |default if absent       |
   +------------+---------------+----------------+------------------------+
   |C           |true           |server-stamp    |Gateway MUST ignore     |
   |            |               |only            |client-supplied         |
   |            |               |                |invoked_at_ms; stamps   |
   |            |               |                |receipt time            |
   |            |               |                |authoritatively         |
   +------------+---------------+----------------+------------------------+

                                  Table 18

   When rejecting due to timestamp, the denial receipt MUST include: -
   detail: 'timestamp_rejected' - server_time_ms field containing the
   gateway's current Unix epoch ms so clients can resync clocks

   For physical_safety: true capabilities:

   *  The gateway MUST server-stamp invoked_at_ms and ignore any client-
      supplied value.  The client-supplied value, if present, MUST be
      stored in client_claimed_at_ms on the receipt for audit purposes.

5.4.  Idempotency

   A gateway SHOULD implement idempotency keyed by (tenant_id,
   capability, idempotency_key).  On a cache hit:

   *  The gateway MUST re-validate that the grant is still non-revoked
      and non-expired.  If the grant has been revoked since the original
      execution, the gateway MUST return HTTP 410 with a new denial
      receipt.

Shovan                   Expires 1 January 2027                [Page 27]
Internet-Draft                     GAP                         June 2026

   *  The gateway MUST verify that the arguments in the new request
      exactly match the stored arguments.  If they differ, the gateway
      MUST return HTTP 409.

   *  The receipt MUST have is_idempotency_replay: true.

   *  For physical_safety: true capabilities, the maximum idempotency
      window MUST NOT exceed 60 seconds.

5.5.  Grant Selection

   When multiple active grants match an invocation, a gateway MUST use
   the following deterministic selection order:

   1.  Prefer the grant with more scope_narrowing keys (more specific).

   2.  Among grants with equal key count, prefer lower numeric upper-
       bound values.

   3.  Among grants with equal numeric bounds, prefer smaller string-
       array cardinality.

6.  Phase 4: Receipt

6.1.  GapDecisionReceiptBody

   A GapDecisionReceipt is produced for every gate decision.  It is
   immutable.

6.2.  GDPR Erasure

   GDPR Article 17 (right to erasure) requires a mechanism to handle
   erasure requests for receipts containing personal data.  The GAP
   erasure mechanism preserves OID integrity while satisfying erasure
   obligations.

   A gap:erasure_event CDRO replaces the body of a targeted receipt with
   a fixed erasure sentinel while preserving envelope metadata.

   ErasureEventBody:

Shovan                   Expires 1 January 2027                [Page 28]
Internet-Draft                     GAP                         June 2026

       +================+==========+==============================+
       | Field          | Required | Description                  |
       +================+==========+==============================+
       | target_oid     | yes      | OID of the CDRO being erased |
       +----------------+----------+------------------------------+
       | erasure_reason | yes      | gdpr_article_17, ccpa, or    |
       |                |          | operator_policy              |
       +----------------+----------+------------------------------+
       | erased_at_ms   | yes      | Unix epoch ms of erasure     |
       +----------------+----------+------------------------------+
       | erased_by      | yes      | Actor OID issuing the        |
       |                |          | erasure                      |
       +----------------+----------+------------------------------+
       | fields_erased  | yes      | Array of field paths erased  |
       |                |          | from the target CDRO body    |
       +----------------+----------+------------------------------+

                                 Table 19

   The erasure event's OID anchors to the original CDRO OID and is
   itself a signed CDRO, making it non-repudiable.  Verifiers MUST treat
   an erasure event as authoritative over the prior OID body.  Add
   gdpr_erasure to the compliance_tags vocabulary.

   For privacy_classification: pii capabilities, gateways operating
   under GDPR SHOULD use pii_args tokenization (see Phase 1: Declare) to
   minimize PII stored in receipts before erasure is required.

   +=======================+==========+================================+
   | Field                 | Required | Description                    |
   +=======================+==========+================================+
   | subject_kind          | yes      | capability_invocation,         |
   |                       |          | workflow_stage,                |
   |                       |          | provisional_block, or          |
   |                       |          | grant_evaluation               |
   +-----------------------+----------+--------------------------------+
   | subject_oid           | yes      | OID of the evaluated           |
   |                       |          | object                         |
   +-----------------------+----------+--------------------------------+
   | status                | yes      | DecisionStatus (see below)     |
   +-----------------------+----------+--------------------------------+
   | capability_grant_oids | yes      | Array of grant OIDs that       |
   |                       |          | were evaluated                 |
   +-----------------------+----------+--------------------------------+
   | decided_at_ms         | yes      | Gateway-stamped decision       |
   |                       |          | timestamp                      |
   +-----------------------+----------+--------------------------------+
   | detail                | no       | Error code string (from        |

Shovan                   Expires 1 January 2027                [Page 29]
Internet-Draft                     GAP                         June 2026

   |                       |          | the error code registry)       |
   +-----------------------+----------+--------------------------------+
   | compliance_tags       | no       | Array of compliance tag        |
   |                       |          | strings (gateway-              |
   |                       |          | populated, not in OID          |
   |                       |          | hash)                          |
   +-----------------------+----------+--------------------------------+
   | is_idempotency_replay | no       | Boolean. true if this          |
   |                       |          | receipt is a cached replay     |
   +-----------------------+----------+--------------------------------+
   | client_claimed_at_ms  | no       | Client-supplied                |
   |                       |          | invoked_at_ms for              |
   |                       |          | physical_safety                |
   |                       |          | invocations                    |
   +-----------------------+----------+--------------------------------+
   | max_offline_ttl_ms    | no       | Maximum duration a             |
   |                       |          | verifier MAY cache this        |
   |                       |          | receipt offline                |
   +-----------------------+----------+--------------------------------+
   | signer_identity       | no       | For 21 CFR Part 11             |
   |                       |          | contexts: display name,        |
   |                       |          | role, and credential           |
   |                       |          | identifier of the              |
   |                       |          | authorizing human.  The        |
   |                       |          | granted_by actor OID           |
   |                       |          | SHOULD resolve to this         |
   |                       |          | identity.                      |
   +-----------------------+----------+--------------------------------+
   | sequence_number       | no       | Monotonically increasing       |
   |                       |          | integer within the tenant,     |
   |                       |          | incremented per receipt,       |
   |                       |          | gapless.  Gaps in the          |
   |                       |          | sequence indicate dropped      |
   |                       |          | receipts.  Provides            |
   |                       |          | determinable ordering          |
   |                       |          | within a millisecond for       |
   |                       |          | high-frequency deployments     |
   |                       |          | (MiFID II RTS 25).             |
   +-----------------------+----------+--------------------------------+
   | decided_at_ns         | no       | Optional nanoseconds since     |
   |                       |          | Unix epoch for sub-            |
   |                       |          | millisecond precision.         |
   |                       |          | RECOMMENDED for                |
   |                       |          | financial.* capabilities.      |
   +-----------------------+----------+--------------------------------+

                                  Table 20

Shovan                   Expires 1 January 2027                [Page 30]
Internet-Draft                     GAP                         June 2026

   A gateway MUST guarantee strict monotonicity of sequence_number
   within a tenant.  For financial.* capabilities, the gateway SHOULD
   populate decided_at_ns.

   DecisionStatus values: ok, denied, failed, deferred, timed_out,
   pending, rate_limited.

6.3.  Token Consumption on Receipt [DESIGN]

   When a token_budget precondition is active, the gateway settles
   actual consumption onto the receipt after execution via the
   token_consumption field.

   TokenConsumption:

       +===============+==========+================================+
       | Field         | Required | Description                    |
       +===============+==========+================================+
       | input_tokens  | yes      | Input (prompt) tokens          |
       |               |          | consumed; non-negative integer |
       +---------------+----------+--------------------------------+
       | output_tokens | yes      | Output (completion) tokens     |
       |               |          | consumed; non-negative integer |
       +---------------+----------+--------------------------------+
       | model         | yes      | Model identifier               |
       +---------------+----------+--------------------------------+
       | cost_usd      | no       | Estimated cost in USD.         |
       |               |          | [MODELED]                      |
       +---------------+----------+--------------------------------+
       | settled_at_ms | yes      | Unix epoch ms when consumption |
       |               |          | was settled                    |
       +---------------+----------+--------------------------------+

                                  Table 21

6.4.  Compliance Tags

   A gateway MUST populate compliance_tags on every receipt.  The
   minimum tag set:

Shovan                   Expires 1 January 2027                [Page 31]
Internet-Draft                     GAP                         June 2026

     +================================+=============================+
     | Tag                            | When set                    |
     +================================+=============================+
     | safety_class:A,                | Capability safety class     |
     | safety_class:B, safety_class:C |                             |
     +--------------------------------+-----------------------------+
     | physical_safety                | physical_safety: true       |
     |                                | capability                  |
     +--------------------------------+-----------------------------+
     | hitl_approved                  | Workflow reached            |
     |                                | terminal_outcome: approved  |
     +--------------------------------+-----------------------------+
     | hitl_denied                    | Workflow reached            |
     |                                | terminal_outcome: denied    |
     +--------------------------------+-----------------------------+
     | idempotency_replay             | is_idempotency_replay: true |
     +--------------------------------+-----------------------------+
     | rate_limited                   | status: rate_limited        |
     +--------------------------------+-----------------------------+
     | phi                            | Declaration has             |
     |                                | privacy_classification: phi |
     +--------------------------------+-----------------------------+

                                 Table 22

   compliance_tags are gateway-populated.  They MUST NOT be included in
   the OID hash computation.  A verifier MUST NOT trust compliance_tags
   as normative; they are informational audit annotations.

7.  Agent Delegation Chain [DESIGN]

   A gap:orchestration_chain CDRO captures the ordered sequence of
   delegation hops that authorized a terminal capability invocation.  It
   consolidates multi-agent pipelines into a single auditable object.

7.1.  Structure

   An OrchestrationChainBody:

Shovan                   Expires 1 January 2027                [Page 32]
Internet-Draft                     GAP                         June 2026

     +======================+==========+=============================+
     | Field                | Required | Description                 |
     +======================+==========+=============================+
     | root_actor_oid       | yes      | Actor OID that initiated    |
     |                      |          | the chain                   |
     +----------------------+----------+-----------------------------+
     | steps                | yes      | Ordered array of            |
     |                      |          | DelegationStep, maximum 10  |
     +----------------------+----------+-----------------------------+
     | capability_name      | yes      | The capability name being   |
     |                      |          | delegated through the chain |
     +----------------------+----------+-----------------------------+
     | final_invocation_oid | yes      | OID of the terminal         |
     |                      |          | CapabilityInvocation CDRO   |
     +----------------------+----------+-----------------------------+

                                  Table 23

   Each DelegationStep:

Shovan                   Expires 1 January 2027                [Page 33]
Internet-Draft                     GAP                         June 2026

    +=====================+==========+===============================+
    | Field               | Required | Description                   |
    +=====================+==========+===============================+
    | step_index          | yes      | Zero-based position of this   |
    |                     |          | hop in the chain              |
    +---------------------+----------+-------------------------------+
    | delegator_actor_oid | yes      | Actor OID performing the      |
    |                     |          | delegation                    |
    +---------------------+----------+-------------------------------+
    | delegatee_actor_oid | yes      | Actor OID receiving authority |
    +---------------------+----------+-------------------------------+
    | grant_oid           | yes      | OID of the grant authorizing  |
    |                     |          | this hop                      |
    +---------------------+----------+-------------------------------+
    | prior_receipt_oid   | no       | OID of the receipt from the   |
    |                     |          | prior hop (absent for         |
    |                     |          | step_index 0)                 |
    +---------------------+----------+-------------------------------+
    | delegated_at_ms     | yes      | Unix epoch ms of delegation   |
    +---------------------+----------+-------------------------------+
    | step_signature      | yes      | Signature over                |
    |                     |          | canonical(prior_receipt_oid + |
    |                     |          | invocation_body), base64url   |
    +---------------------+----------+-------------------------------+
    | step_signature_alg  | yes      | Algorithm used for            |
    |                     |          | step_signature                |
    +---------------------+----------+-------------------------------+

                                 Table 24

7.2.  Constraints

   MUST: the steps array MUST NOT exceed 10 entries.  A gateway MUST
   return HTTP 400 with error code delegation_depth_exceeded when this
   limit is breached.

   MUST: signing keys for each hop MUST be declared at grant issuance.
   The gateway MUST verify each step's signature before allowing the
   terminal invocation.

   MUST: at invocation time, the gateway MUST re-validate that all
   grants referenced in the chain are non-expired and non-revoked.
   Partial chain validation is insufficient.

   A CapabilityInvocationBody MAY carry delegation_chain (array of
   DelegationStep) as a shorthand when the full gap:orchestration_chain
   CDRO is not yet materialized.  The same maximum-10-hop and per-step
   signature verification rules apply.

Shovan                   Expires 1 January 2027                [Page 34]
Internet-Draft                     GAP                         June 2026

8.  Consent Version Chain [DESIGN]

   GAP consent records form an append-only chain.  Each record
   references prior_consent_oid, making consent history non-repudiable
   and auditable.

8.1.  gap:consent_record

   ConsentRecordBody:

     +===================+==========+================================+
     | Field             | Required | Description                    |
     +===================+==========+================================+
     | actor_oid         | yes      | Actor OID whose consent this   |
     |                   |          | record captures                |
     +-------------------+----------+--------------------------------+
     | tenant_id         | yes      | Tenant scope                   |
     +-------------------+----------+--------------------------------+
     | context           | yes      | Free-form context string (e.g. |
     |                   |          | hiring.background_check,       |
     |                   |          | clinical.data_sharing)         |
     +-------------------+----------+--------------------------------+
     | consented         | yes      | Boolean: true = consent        |
     |                   |          | granted; false = consent       |
     |                   |          | withdrawn                      |
     +-------------------+----------+--------------------------------+
     | prior_consent_oid | no       | OID of the prior record for    |
     |                   |          | this actor + context           |
     +-------------------+----------+--------------------------------+
     | consented_at_ms   | yes      | Unix epoch ms of consent event |
     +-------------------+----------+--------------------------------+
     | expires_at_ms     | no       | Optional expiry; the gateway   |
     |                   |          | MUST treat expired records as  |
     |                   |          | consented: false               |
     +-------------------+----------+--------------------------------+
     | consent_text_hash | no       | SHA-256 hash of the disclosure |
     |                   |          | text shown to the actor        |
     +-------------------+----------+--------------------------------+

                                  Table 25

8.2.  Precondition: consent_current

   The consent_current precondition evaluates at invocation time whether
   the actor's most recent consent record for the capability's context
   has consented: true.

Shovan                   Expires 1 January 2027                [Page 35]
Internet-Draft                     GAP                         June 2026

   MUST: the gateway MUST NOT use the idempotency cache for
   consent_current evaluation.  Every invocation MUST re-read the most
   recent record.

   MUST: withdrawal of consent (a new record with consented: false) MUST
   take effect within 5 seconds across all gateway replicas.

   This single primitive subsumes all sector-specific consent patterns:
   hiring consent, learner consent, and clinical consent all use
   gap:consent_record with context-specific values in the context field.

8.3.  Relationship to GDPR

   The consent chain is a complement to the erasure mechanism (see GDPR
   Erasure).  Erasure removes PII from receipts; the consent chain
   records the authority under which invocations were permitted.  Both
   chains SHOULD be linked in receipt compliance_tags when applicable.

9.  Offline Execution Profile

   An Offline Execution Profile (OEP) enables a device or agent to
   evaluate grants and issue provisional receipts without network
   connectivity to the gateway.

9.1.  OEP Bundle

   A gap:offline_bundle is a gateway-signed CDRO containing everything
   needed for offline grant evaluation:

      +=====================+==========+============================+
      | Field               | Required | Description                |
      +=====================+==========+============================+
      | grant               | yes      | Full CapabilityGrant CDRO  |
      |                     |          | (inline, not by reference) |
      +---------------------+----------+----------------------------+
      | declaration         | yes      | Full CapabilityDeclaration |
      |                     |          | CDRO for the granted actor |
      +---------------------+----------+----------------------------+
      | keyring             | yes      | KeyringExport CDRO (see    |
      |                     |          | Offline Key Distribution)  |
      +---------------------+----------+----------------------------+
      | revocation_snapshot | yes      | RevocationSnapshot CDRO    |
      |                     |          | (see Offline Revocation)   |
      +---------------------+----------+----------------------------+
      | offline_policy      | yes      | OfflinePolicy block        |
      +---------------------+----------+----------------------------+

                                  Table 26

Shovan                   Expires 1 January 2027                [Page 36]
Internet-Draft                     GAP                         June 2026

   OfflinePolicy:

   +===========================+==========+============================+
   | Field                     | Required | Description                |
   +===========================+==========+============================+
   | max_offline_duration_ms   | yes      | Maximum duration the       |
   |                           |          | bundle is valid for        |
   |                           |          | offline use                |
   +---------------------------+----------+----------------------------+
   | max_offline_invocations   | yes      | Maximum invocations        |
   |                           |          | permitted during the       |
   |                           |          | offline period             |
   +---------------------------+----------+----------------------------+
   | offline_capability_filter | no       | Array of capability        |
   |                           |          | patterns permitted         |
   |                           |          | offline; absent = all      |
   |                           |          | granted capabilities       |
   |                           |          | permitted                  |
   +---------------------------+----------+----------------------------+
   | offline_allowed           | no       | Boolean.  For              |
   |                           |          | safety_class C with        |
   |                           |          | physical_safety: true,     |
   |                           |          | offline operation MUST     |
   |                           |          | NOT proceed unless this    |
   |                           |          | is explicitly true         |
   +---------------------------+----------+----------------------------+

                                  Table 27

   OEP bundles are fetched via GET /v1/gap/offline-
   bundle?grant_oid=<oid> and expire at expires_at_ms.

9.2.  Offline Evaluation

   When operating offline, the device:

   1.  Verifies the OEP bundle signature against the locally-held root
       public key

   2.  Checks grant.expires_at_ms against local clock

   3.  Checks offline_policy.max_offline_duration_ms from bundle
       issuance

   4.  Evaluates scope_narrowing against invocation args

   5.  Increments local invocation counter; denies if
       max_offline_invocations is reached

Shovan                   Expires 1 January 2027                [Page 37]
Internet-Draft                     GAP                         June 2026

   6.  Issues a provisional receipt signed with the device-local key
       pre-provisioned at enrollment

   Provisional receipts carry status: 'ok:offline' and include
   oep_bundle_oid referencing the OEP bundle.

   For safety_class C capabilities with physical_safety: true, offline
   operation MUST NOT proceed unless the OEP bundle's
   offline_policy.offline_allowed is explicitly true.

9.3.  Reconciliation

   On reconnection, the device submits accumulated provisional receipts
   via POST /v1/gap/offline-receipts (array of provisional receipt
   CDROs).

   The gateway: 1.  Validates each provisional receipt against the
   referenced OEP bundle 2.  Issues an authoritative receipt (status:
   ok) or rejection receipt (status: denied) 3.  Rejection does not
   retroactively void the physical action but establishes the audit
   record

9.4.  Sovereign Mode

   For fully self-hosted deployments with no external connectivity,
   operators MAY run a locally-operated gateway with a locally-generated
   root-of-trust keypair.  In sovereign mode: - All CDRO types are valid
   with locally-generated OIDs - The root public key is distributed at
   device provisioning time - No SynOI infrastructure is required at any
   point in the lifecycle

10.  Workflows

10.1.  Purpose

   A WorkflowDefinition describes a multi-stage Human-in-the-Loop (HITL)
   process triggered by a specific capability invocation.  When a
   grant's pending_workflow field references a workflow definition, the
   gateway instantiates a WorkflowInstance instead of immediately
   producing a terminal receipt.

10.2.  Workflow Lifecycle

   1.  On invocation, the gateway emits a receipt with status: pending.

   2.  The workflow proceeds through stages.  Each stage may listen for
       channel events (SMS, push notification, webhook, overlay
       approval).

Shovan                   Expires 1 January 2027                [Page 38]
Internet-Draft                     GAP                         June 2026

   3.  When the workflow reaches a terminal stage, the gateway emits a
       NEW receipt with the terminal status.  The pending receipt MUST
       NOT be modified in place.

   4.  The terminal receipt's capability_grant_oids MUST include the
       grant OIDs from the triggering invocation.

10.3.  Signal Sender Validation

   A gateway MUST validate the sender of every channel event used as a
   workflow signal:

   *  If a WorkflowStage's StageListen specifies required_from_binding,
      the gateway MUST verify the event's from field matches before
      accepting the event as a valid stage signal.

   *  For every channel kind on physical_safety: true stages, the
      gateway MUST verify the channel event's authenticity using the
      mechanism appropriate to the channel adapter (e.g. webhook HMAC,
      push token binding, local credential verification).  No single
      channel is normatively required for this check; the Channel
      Adapter interface defines the verification contract.

   *  The same actor_oid MUST NOT be counted as two approvals for the
      same stage (no self-counting).

10.3.1.  Channel Adapters

   GAP workflow stages deliver signals and receive responses through
   Channel Adapters.  A Channel Adapter is an implementation of the
   following interface:

   *  kind: a ChannelKind string identifying the adapter

   *  performAction(stage, context): executes the stage action (sends
      alert, invokes tool, etc.)

   *  armListen(stage, context): arms the adapter to receive inbound
      signals (reply, button tap, etc.)

   *  health(): returns whether the adapter is currently operational

   The gateway MUST support at minimum the sms channel kind.  All other
   channel kinds are OPTIONAL but MUST conform to this interface when
   implemented.

Shovan                   Expires 1 January 2027                [Page 39]
Internet-Draft                     GAP                         June 2026

10.3.1.1.  Defined Channel Kinds

   +====================+===============================+==============+
   | Kind               | Description                   | Connectivity |
   +====================+===============================+==============+
   | sms                | SMS message via any           | Internet     |
   |                    | provider (Twilio, AWS         |              |
   |                    | SNS, etc.)                    |              |
   +--------------------+-------------------------------+--------------+
   | voice              | Outbound voice call with      | Internet     |
   |                    | IVR response                  |              |
   +--------------------+-------------------------------+--------------+
   | email              | Email with approve/deny       | Internet     |
   |                    | link                          |              |
   +--------------------+-------------------------------+--------------+
   | slack              | Slack message with Block      | Internet     |
   |                    | Kit interactive               |              |
   |                    | components                    |              |
   +--------------------+-------------------------------+--------------+
   | mobile_push        | APNs / FCM push               | Internet     |
   |                    | notification                  |              |
   +--------------------+-------------------------------+--------------+
   | sse                | Server-Sent Events to a       | LAN/Internet |
   |                    | connected dashboard           |              |
   +--------------------+-------------------------------+--------------+
   | webhook            | HTTP POST to a                | LAN/Internet |
   |                    | configured endpoint           |              |
   +--------------------+-------------------------------+--------------+
   | in_app             | In-app overlay or             | Local        |
   |                    | notification                  |              |
   +--------------------+-------------------------------+--------------+
   | game_engine        | Game engine event             | Local        |
   |                    | (Unity, Unreal, Godot         |              |
   |                    | hook)                         |              |
   +--------------------+-------------------------------+--------------+
   | local_terminal     | Operator console at the       | Local/Air-   |
   |                    | local enforcement point;      | gapped       |
   |                    | requires hardware token       |              |
   |                    | or biometric                  |              |
   +--------------------+-------------------------------+--------------+
   | hmi_panel          | HMI touchscreen or            | Local/Air-   |
   |                    | physical operator panel       | gapped       |
   |                    | at the device                 |              |
   +--------------------+-------------------------------+--------------+
   | opc_ua_ack         | OPC-UA operator               | Local/Air-   |
   |                    | acknowledgement signal        | gapped       |
   |                    | from a process historian      |              |
   |                    | or SCADA terminal             |              |

Shovan                   Expires 1 January 2027                [Page 40]
Internet-Draft                     GAP                         June 2026

   +--------------------+-------------------------------+--------------+
   | local_signed_token | Physical signed token         | Local/Air-   |
   |                    | (QR code, smart card,         | gapped       |
   |                    | NFC) scanned at the           |              |
   |                    | device                        |              |
   +--------------------+-------------------------------+--------------+

                                  Table 28

   Custom channel kinds MAY be used by implementing the Channel Adapter
   interface.  The kind field accepts any string value; non-standard
   kinds SHOULD use a reverse-domain prefix (e.g. com.example.pager).

10.3.1.2.  Air-Gapped HITL

   For deployments without external connectivity, air-gapped channel
   types (local_terminal, hmi_panel, opc_ua_ack, local_signed_token)
   produce stage transition CDROs signed by the local gateway instance.
   These are synchronized to the cloud gateway at next connectivity to
   establish the complete receipt chain.

   Local channel adapters authenticate the operator using locally-held
   credentials: - local_terminal: smart card, hardware token, or
   biometric; verified against locally-registered public key -
   hmi_panel: operator badge scan or PIN verified against local roster
   CDRO - opc_ua_ack: OPC-UA NodeId-scoped acknowledgement from an
   authorized operator session - local_signed_token: scanned CDRO (QR or
   NFC) verified against locally-held root public key

   The signal sender validation rule (required_from_binding) applies to
   all channel kinds.  For local channels, required_from_binding
   references the operator's local actor OID rather than a phone number
   or Slack user ID.

10.4.  Two-Person Integrity

   A stage MAY specify authorized_approvers (array of actor OIDs).  When
   set:

   *  The gateway MUST verify the signal sender resolves to one of the
      listed OIDs.

   *  The workflow actor (the actor who triggered the invocation) MUST
      NOT serve as an approver (no self-approval).

Shovan                   Expires 1 January 2027                [Page 41]
Internet-Draft                     GAP                         June 2026

10.5.  Safety Constraints on Workflow Definitions

   For any stage triggered by a physical_safety: true or safety_class: C
   capability, a gateway MUST reject the workflow definition
   registration if any on_timeout path leads to a terminal stage with
   terminal_outcome: approved.  Timeout MUST NOT produce approval for
   physical-safety capabilities.

   The minimum duration_seconds for any stage triggered by a
   safety_class: C capability is 30 seconds.

11.  Revocation

11.1.  Revocation Kinds

      +================+===========================+================+
      | Kind           | Effect                    | Use case       |
      +================+===========================+================+
      | Immediate (L1) | Grant denied from         | Contractor     |
      |                | effective_at_ms forward   | access removal |
      +----------------+---------------------------+----------------+
      | Scheduled (L2) | Grant denied after        | Expiry         |
      |                | effective_at_ms           | enforcement    |
      +----------------+---------------------------+----------------+
      | Provisional    | Capability temporarily    | Anomaly        |
      | block          | blocked pending L3 quorum | detection      |
      +----------------+---------------------------+----------------+
      | L3 quorum      | Block becomes permanent   | High-stakes    |
      |                | on quorum; may auto-renew | revocation     |
      +----------------+---------------------------+----------------+

                                  Table 29

11.2.  Provisional Block Policy

   A RevocationEvent with revocation_kind: provisional_block MAY carry
   provisional_block_policy:

Shovan                   Expires 1 January 2027                [Page 42]
Internet-Draft                     GAP                         June 2026

         +==========================+============================+
         | Field                    | Description                |
         +==========================+============================+
         | on_expiry_without_quorum | renew or revert            |
         +--------------------------+----------------------------+
         | min_approvers            | Minimum approver count for |
         |                          | quorum                     |
         +--------------------------+----------------------------+
         | provisional_block_ttl_ms | Operator override for the  |
         |                          | block TTL.  Defaults to 72 |
         |                          | hours.  Minimum: 1 hour.   |
         +--------------------------+----------------------------+

                                  Table 30

   The provisional block TTL defaults to 72 hours.  Operators MAY set
   provisional_block_ttl_ms on the RevocationEventBody to override.
   Minimum: 1 hour.  For safety_class C capabilities with
   on_expiry_without_quorum: renew, the renewal cycle period equals
   provisional_block_ttl_ms.

   When the provisional block TTL expires and quorum has not been
   reached:

   *  If on_expiry_without_quorum is renew (or the field is absent on a
      physical_safety: true grant): the block MUST auto-renew.  The
      renewal MUST produce a receipt with subject_kind:
      provisional_block and status: pending.

   *  If on_expiry_without_quorum is revert: the block is lifted
      (capability re-enabled).  The revert value MUST NOT be used for
      physical_safety: true capabilities regardless of the field value.

11.3.  Offline Revocation Bundle

   A gap:revocation_bundle enables offline devices to check grant
   revocation status without contacting the gateway.

   RevocationBundleBody:

Shovan                   Expires 1 January 2027                [Page 43]
Internet-Draft                     GAP                         June 2026

    +================+==========+=====================================+
    | Field          | Required | Description                         |
    +================+==========+=====================================+
    | revocations    | yes      | Array of RevocationEntry            |
    +----------------+----------+-------------------------------------+
    | snapshot_at_ms | yes      | Unix epoch ms when the snapshot was |
    |                |          | taken                               |
    +----------------+----------+-------------------------------------+
    | expires_at_ms  | yes      | Maximum age of this bundle; devices |
    |                |          | MUST NOT use it after this time     |
    +----------------+----------+-------------------------------------+
    | tenant_id      | yes      | Tenant this bundle applies to       |
    +----------------+----------+-------------------------------------+

                                  Table 31

   Each RevocationEntry:

   +=================+==========+======================================+
   | Field           | Required | Description                          |
   +=================+==========+======================================+
   | grant_oid       | yes      | OID of the revoked grant             |
   +-----------------+----------+--------------------------------------+
   | effective_at_ms | yes      | When the revocation                  |
   |                 |          | became effective                     |
   +-----------------+----------+--------------------------------------+
   | kind            | yes      | immediate, scheduled, or             |
   |                 |          | provisional_block                    |
   +-----------------+----------+--------------------------------------+

                                  Table 32

   Export via GET /v1/gap/revocations/bundle?since_ms=<ms>.  The bundle
   is gateway-signed.

   Fail-safe rules for offline devices:

   *  Devices operating with physical_safety: true capabilities MUST
      require a valid (not-expired) revocation bundle as a precondition
      for any invocation

   *  If the device cannot obtain a fresh-enough bundle before
      expires_at_ms, the fail-safe action for physical_safety: true and
      safety_class C capabilities MUST be DENY

Shovan                   Expires 1 January 2027                [Page 44]
Internet-Draft                     GAP                         June 2026

   *  The maximum acceptable bundle age is set by
      max_revocation_bundle_age_ms on the grant; if absent, the gateway
      default applies (RECOMMENDED: 24 hours for class C, 7 days for
      class A/B)

11.4.  Break-Glass Grants

   A break-glass grant pre-authorizes a defined set of emergency
   capabilities with an offline-verifiable signed token, for use when
   the gateway is unreachable and immediate action is required for
   safety or clinical reasons.

11.4.1.  Break-Glass Grant Fields

   A grant with break_glass: true additionally carries:

   +=============================+==========+=========================+
   | Field                       | Required | Description             |
   +=============================+==========+=========================+
   | break_glass                 | yes      | Boolean true.  Marks    |
   |                             |          | this grant as a break-  |
   |                             |          | glass grant.            |
   +-----------------------------+----------+-------------------------+
   | break_glass_token           | yes      | A signed CDRO of type   |
   |                             |          | gap:break_glass_token   |
   |                             |          | pre-issued by the       |
   |                             |          | gateway and stored      |
   |                             |          | securely on the device. |
   +-----------------------------+----------+-------------------------+
   | break_glass_ttl_ms          | yes      | TTL of the break-glass  |
   |                             |          | token in milliseconds   |
   |                             |          | from issuance.          |
   |                             |          | RECOMMENDED: 4 hours.   |
   +-----------------------------+----------+-------------------------+
   | break_glass_max_invocations | no       | Maximum invocations     |
   |                             |          | allowed under this      |
   |                             |          | token before it is      |
   |                             |          | exhausted.  Defaults to |
   |                             |          | 1 for safety_class C.   |
   +-----------------------------+----------+-------------------------+
   | break_glass_requires_reason | no       | Boolean.  When true,    |
   |                             |          | the invoker MUST supply |
   |                             |          | a break_glass_reason    |
   |                             |          | string in invocation    |
   |                             |          | args.                   |
   +-----------------------------+----------+-------------------------+

                                 Table 33

Shovan                   Expires 1 January 2027                [Page 45]
Internet-Draft                     GAP                         June 2026

11.4.2.  Break-Glass Token

   A gap:break_glass_token CDRO body:

   +========================+==========+==============================+
   | Field                  | Required | Description                  |
   +========================+==========+==============================+
   | grant_oid              | yes      | OID of the break-glass grant |
   |                        |          | this token activates         |
   +------------------------+----------+------------------------------+
   | actor_oid              | yes      | OID of the authorized        |
   |                        |          | invoker                      |
   +------------------------+----------+------------------------------+
   | valid_from_ms          | yes      | Token validity start (Unix   |
   |                        |          | epoch ms)                    |
   +------------------------+----------+------------------------------+
   | expires_at_ms          | yes      | Token validity end (Unix     |
   |                        |          | epoch ms)                    |
   +------------------------+----------+------------------------------+
   | permitted_capabilities | yes      | Array of capability patterns |
   |                        |          | this token authorizes        |
   |                        |          | (subset of the grant)        |
   +------------------------+----------+------------------------------+
   | max_invocations        | yes      | Maximum invocations before   |
   |                        |          | token is exhausted           |
   +------------------------+----------+------------------------------+

                                 Table 34

   The token is gateway-signed at provisioning time.  The device
   verifies the signature using the locally-held public key (from the
   key bundle, per Offline Key Distribution) before activating break-
   glass operation.

11.4.3.  Break-Glass Invocation

   A break-glass invocation MUST carry break_glass_token_oid in the
   invocation args.  The enforcement point verifies:

   1.  The token OID matches a locally-held break-glass token CDRO

   2.  The token signature is valid

   3.  The token has not expired (expires_at_ms > current local clock)

   4.  The invoked capability matches permitted_capabilities

   5.  The token invocation counter has not reached max_invocations

Shovan                   Expires 1 January 2027                [Page 46]
Internet-Draft                     GAP                         June 2026

   6.  If break_glass_requires_reason: true, break_glass_reason is non-
       empty

   Break-glass invocations produce a mandatory provisional receipt with
   compliance_tags: ['break_glass'].  The device MUST submit these
   receipts to POST /v1/gap/offline-receipts at next connectivity.

11.4.4.  Local Override Credential

   For lifting a provisional block when the gateway is unreachable, an
   operator MAY provision a Local Override Credential (LOC) at grant
   issuance time.

   A gap:local_override_credential CDRO body:

    +==========================+==========+===========================+
    | Field                    | Required | Description               |
    +==========================+==========+===========================+
    | grant_oid                | yes      | OID of the provisionally- |
    |                          |          | blocked grant             |
    +--------------------------+----------+---------------------------+
    | actor_oid                | yes      | OID of the authorized     |
    |                          |          | override operator         |
    +--------------------------+----------+---------------------------+
    | expires_at_ms            | yes      | Credential expiry         |
    +--------------------------+----------+---------------------------+
    | single_use               | yes      | Always true.  An LOC MUST |
    |                          |          | be invalidated after      |
    |                          |          | first use.                |
    +--------------------------+----------+---------------------------+
    | override_reason_required | yes      | Boolean.  When true,      |
    |                          |          | operator MUST supply a    |
    |                          |          | reason string.            |
    +--------------------------+----------+---------------------------+

                                  Table 35

   The LOC is signed by the operator's key at grant issuance time and
   stored physically at the installation site (QR code, USB token, or
   printed secure storage).  When presented at the local enforcement
   point, it lifts the provisional block exactly once and produces a
   local override receipt synchronized at next uplink.

   Neither break-glass nor LOC mechanism allows indefinite ungoverned
   operation.  Both produce mandatory audit trails submitted to the
   gateway at next connectivity.

Shovan                   Expires 1 January 2027                [Page 47]
Internet-Draft                     GAP                         June 2026

11.5.  L3 Quorum Approval

   A revocation event may collect approvals via POST /v1/gap/revoke/
   approve.  Each approval MUST:

   *  Identify a unique approver_actor_oid (no duplicate approvers).

   *  Not be made by the same actor who created the revocation event (no
      self-approval).

   When min_approvers is reached, the gateway MUST set effective_at_ms
   on the revocation event and enforce the revocation.

12.  HTTP API Surface

   A GAP-conformant gateway MUST expose the following endpoints under a
   base path (conventionally /v1/gap).  All endpoints require Bearer
   token authentication.  Tenant isolation MUST be enforced: every CDRO
   fetch endpoint MUST verify stored_object.tenant_id ===
   authenticated_tenant_id.  On mismatch, the gateway MUST return HTTP
   404 (never HTTP 403, which would confirm cross-tenant OID existence).

12.1.  Core Endpoints

     +========+====================+=================================+
     | Method | Path               | Description                     |
     +========+====================+=================================+
     | POST   | /declarations      | Register a                      |
     |        |                    | CapabilityDeclaration           |
     +--------+--------------------+---------------------------------+
     | GET    | /declarations/:oid | Fetch a declaration by OID      |
     +--------+--------------------+---------------------------------+
     | POST   | /grants            | Issue a CapabilityGrant         |
     +--------+--------------------+---------------------------------+
     | GET    | /grants            | List grants (query: actor_oid,  |
     |        |                    | capability, status)             |
     +--------+--------------------+---------------------------------+
     | GET    | /grants/:oid       | Fetch a grant by OID            |
     +--------+--------------------+---------------------------------+
     | POST   | /invoke            | Invoke a capability             |
     +--------+--------------------+---------------------------------+
     | POST   | /invoke/batch      | Submit up to 1000 invocations   |
     |        |                    | sharing a tenant in a single    |
     |        |                    | request (L2 OPTIONAL).  Returns |
     |        |                    | array of receipts in submission |
     |        |                    | order.  Grant evaluation is     |
     |        |                    | independent per invocation.     |
     +--------+--------------------+---------------------------------+

Shovan                   Expires 1 January 2027                [Page 48]
Internet-Draft                     GAP                         June 2026

     | GET    | /receipts          | List receipts (query:           |
     |        |                    | actor_oid, grant_oid,           |
     |        |                    | capability, from_ms, to_ms,     |
     |        |                    | status, limit, cursor)          |
     +--------+--------------------+---------------------------------+
     | GET    | /receipts/:oid     | Fetch a receipt by OID          |
     +--------+--------------------+---------------------------------+
     | GET    | /limits/:pool_id   | Returns current rolling sum and |
     |        |                    | remaining headroom for a named  |
     |        |                    | aggregate limit pool.           |
     +--------+--------------------+---------------------------------+

                                  Table 36

12.2.  Revocation Endpoints

    +========+===================+===================================+
    | Method | Path              | Description                       |
    +========+===================+===================================+
    | POST   | /revoke           | Immediate or scheduled revocation |
    +--------+-------------------+-----------------------------------+
    | POST   | /revoke/          | Provisional block (anomaly        |
    |        | provisional-block | response)                         |
    +--------+-------------------+-----------------------------------+
    | POST   | /revoke/approve   | Submit L3 quorum approval         |
    +--------+-------------------+-----------------------------------+
    | GET    | /revocations/:oid | Fetch a revocation event by OID   |
    +--------+-------------------+-----------------------------------+
    | GET    | /revocations      | List revocation events (query:    |
    |        |                   | grant_oid, target_kind, since_ms) |
    +--------+-------------------+-----------------------------------+

                                 Table 37

12.3.  Workflow Endpoints

       +========+============================+====================+
       | Method | Path                       | Description        |
       +========+============================+====================+
       | POST   | /workflows/definitions     | Register a         |
       |        |                            | WorkflowDefinition |
       +--------+----------------------------+--------------------+
       | GET    | /workflows/                | Fetch a definition |
       |        | definitions/:oid           | by OID             |
       +--------+----------------------------+--------------------+
       | GET    | /workflows/instances/:oid  | Fetch a workflow   |
       |        |                            | instance           |
       +--------+----------------------------+--------------------+

Shovan                   Expires 1 January 2027                [Page 49]
Internet-Draft                     GAP                         June 2026

       | GET    | /workflows/instances/:oid/ | List stage         |
       |        | transitions                | transitions        |
       +--------+----------------------------+--------------------+
       | POST   | /workflows/signal          | Deliver a channel  |
       |        |                            | event signal       |
       +--------+----------------------------+--------------------+

                                 Table 38

12.4.  Key Endpoints

    +========+===============+========================================+
    | Method | Path          | Description                            |
    +========+===============+========================================+
    | GET    | /keys/current | Fetch the current signing key (public) |
    +--------+---------------+----------------------------------------+
    | GET    | /keys/:key_id | Fetch a signing key by ID (public)     |
    +--------+---------------+----------------------------------------+
    | GET    | /keys/bundle  | Fetch a signed keyring export bundle   |
    |        |               | for offline verifiers                  |
    +--------+---------------+----------------------------------------+

                                  Table 39

12.5.  Offline Endpoints

   +========+===================+=====================================+
   | Method | Path              | Description                         |
   +========+===================+=====================================+
   | GET    | /offline-bundle   | Fetch an OEP bundle for offline     |
   |        |                   | grant evaluation (query: grant_oid) |
   +--------+-------------------+-------------------------------------+
   | POST   | /offline-receipts | Submit accumulated provisional      |
   |        |                   | receipts for reconciliation         |
   +--------+-------------------+-------------------------------------+
   | GET    | /revocations/     | Fetch a signed revocation snapshot  |
   |        | bundle            | bundle (query: since_ms)            |
   +--------+-------------------+-------------------------------------+

                                 Table 40

13.  Conformance

   GAP implementations declare a conformance tier.  A higher tier is a
   superset of all lower tiers.

Shovan                   Expires 1 January 2027                [Page 50]
Internet-Draft                     GAP                         June 2026

13.1.  Tier L1: Object Layer

   An L1 implementation:

   *  MUST validate CDRO envelopes per {{object-model}}.

   *  MUST compute and verify content-addressed OIDs per {{oid-
      computation}}.

   *  MUST produce GapDecisionReceipts for every gate decision.

   *  MUST enforce tenant isolation on all CDRO fetch operations.

   *  Does NOT enforce scope_narrowing evaluation (steps 1-3 of grant
      evaluation only: expiry, revocation, grantee match).

   *  A grant containing any GrantedCapabilityScope with non-empty
      scope_narrowing MUST be rejected by an L1-conformant gateway at
      grant issuance time with HTTP 400 and error type
      tier_insufficient.

13.2.  Tier L2: Grant Evaluation Layer

   An L2 implementation satisfies L1 and additionally:

   *  MUST enforce scope_narrowing per {{scope-narrowing}}.

   *  MUST enforce delegation subset rules per {{delegation}}.

   *  MUST enforce invocation timestamp validation.

   *  MUST implement idempotency per {{idempotency}}.

   *  MUST enforce aggregate_limits rolling windows.

13.3.  Tier L3: Workflow Layer

   An L3 implementation satisfies L2 and additionally:

   *  MUST implement workflow instantiation, stage transitions, and HITL
      channel signaling.

   *  MUST implement optional_effects evaluation.

   *  MUST implement provisional block with quorum semantics.

   *  MUST emit the complete pending + terminal receipt chain for every
      workflow.

Shovan                   Expires 1 January 2027                [Page 51]
Internet-Draft                     GAP                         June 2026

13.4.  Tier L4: Federation Layer

   An L4 implementation satisfies L3 and additionally:

   *  MUST implement L3 quorum revocation with multi-party approval.

   *  MUST implement cross-tenant receipt verification.

   *  MUST implement federation handshakes.

13.4.1.  L3-PQ and L4-PQ (Post-Quantum Only)

   For deployments under CNSA 2.0 (NSS policy) or equivalent post-
   quantum mandates where Ed25519 is not an approved algorithm:

     +=======+==================+====================================+
     | Tier  | Signing          | Use case                           |
     +=======+==================+====================================+
     | L3-PQ | ML-DSA-65 only   | Classified / NSS deployments       |
     |       | (no Ed25519)     | requiring CNSA 2.0 compliance      |
     +-------+------------------+------------------------------------+
     | L4-PQ | ML-DSA-65 only + | Classified / regulated deployments |
     |       | authorized axis  | requiring PQ + full audit chain    |
     +-------+------------------+------------------------------------+

                                  Table 41

   The existing L4 hybrid mode (Ed25519 + ML-DSA-65) remains for
   interoperability with non-classified systems.  Pure ML-DSA-65
   operation without Ed25519 is the conformant path for CNSA 2.0
   deployments.  The signature_algorithm field in the CDRO envelope
   (value: ML-DSA-65) identifies PQ-only receipts.

   Security Considerations: Ed25519 is not approved for National
   Security Systems under CNSSP-15 and CNSA 2.0.  New NSS deployments
   MUST use L3-PQ or L4-PQ minimum.

13.5.  Conformance by Sector

     +==============+=========+=====================================+
     | Sector       | Minimum | Rationale                           |
     |              | Tier    |                                     |
     +==============+=========+=====================================+
     | MCP tool     | L2      | Scope enforcement mandatory         |
     | governance   |         |                                     |
     +--------------+---------+-------------------------------------+
     | AI agent     | L2      | Delegation chains required          |
     | pipelines    |         |                                     |

Shovan                   Expires 1 January 2027                [Page 52]
Internet-Draft                     GAP                         June 2026

     +--------------+---------+-------------------------------------+
     | Smart home   | L2      | User-issued grants with scope       |
     | (consumer)   |         |                                     |
     +--------------+---------+-------------------------------------+
     | Industrial / | L3      | HITL + provisional block required   |
     | OT           |         |                                     |
     +--------------+---------+-------------------------------------+
     | Medical      | L4      | HITL + signed receipts + authorized |
     | device /     | MINIMUM | axis required.  L3 without signed   |
     | clinical (21 |         | receipts does not satisfy 21 CFR    |
     | CFR Part 11) |         | Part 11 Section 11.50 electronic    |
     |              |         | signature requirements.  An L3      |
     |              |         | deployment for medical devices is   |
     |              |         | non-conformant to Part 11.          |
     +--------------+---------+-------------------------------------+
     | Physical     | L3      | HITL + two-person integrity         |
     | security     |         |                                     |
     +--------------+---------+-------------------------------------+
     | Cross-tenant | L4      | Federation semantics                |
     | / federated  |         |                                     |
     +--------------+---------+-------------------------------------+

                                 Table 42

14.  Security Considerations

14.1.  OID Integrity

   The security of GAP receipts depends on the integrity of OID
   computation.  An implementation MUST compute OIDs using the canonical
   JSON form defined in {{canonical-json}}. Divergence in canonical form
   between the issuing gateway and a verifier produces OID mismatch,
   which is detectable but MUST be investigated; it indicates either a
   bug or an active tampering attempt.

   Implementations across different languages MUST independently verify
   that their canonical JSON output matches the test vectors in the
   @synoi/gap TypeScript package and the Python synoi-gap package.  The
   pinned test vectors in test/conformance.test.ts serve as cross-
   implementation reference points.

14.2.  Signature Verification

14.2.1.  When signing is required

   A gateway MUST sign a receipt with Ed25519 [RFC8032] when any of the
   following are true:

Shovan                   Expires 1 January 2027                [Page 53]
Internet-Draft                     GAP                         June 2026

   1.  The effective require_signed_receipt for the capability is true.
       The effective value is determined by evaluating in precedence
       order: (a) GrantedCapabilityScope.require_signed_receipt if
       present; (b) CapabilitySpec.require_signed_receipt if present;
       (c) the gateway's configured default signing policy.

   2.  The gateway is configured to sign all receipts by default and
       neither the grant nor the declaration has suppressed signing with
       require_signed_receipt: false.

   3.  The capability's effective privacy_classification is financial or
       matches financial.*, OR the deployment asserts 21 CFR Part 11
       compliance.  For capabilities in these categories,
       require_signed_receipt MUST default to true and MUST NOT be
       overridable to false by operator grant override.

   A gateway MAY be configured with a safety_class_signing_floor that
   sets the minimum safety_class for which signatures are mandatory
   regardless of require_signed_receipt settings (e.g. B means all class
   B and C receipts are signed).  Gateway configuration SHOULD document
   whether safety_class_signing_floor is set, as it affects the signing
   behavior for every tenant on that deployment.

   A gateway MUST NOT sign a receipt when the effective
   require_signed_receipt is false, regardless of the server's default
   policy.  This allows actors to opt specific high-frequency
   capabilities out of signing cost.

   When signing is not required, the signature and signature_key_id
   fields MUST be omitted from the receipt envelope.

14.2.2.  Verifier obligations

   Verifiers MUST:

   *  Fetch the signing key from the gateway's /keys/:key_id endpoint
      using the signature_key_id from the receipt.

   *  Verify that the key's expires_at_ms has not passed.

   *  Verify the signature over the canonical envelope (with the same
      field exclusions used for OID computation).

   A verifier that receives a receipt without a signature field for a
   capability where require_signed_receipt is true MUST treat the
   receipt as invalid.

Shovan                   Expires 1 January 2027                [Page 54]
Internet-Draft                     GAP                         June 2026

   Key rotation: a gateway MUST publish a new key before retiring an old
   one.  Old keys remain valid for verifying receipts signed under them.
   The current key is always the one returned by GET /keys/current.

14.2.3.  Offline Key Distribution

   A gap:keyring_export CDRO enables verifiers to operate without a live
   /keys endpoint.

   KeyringExportBody:

          +================+==========+========================+
          | Field          | Required | Description            |
          +================+==========+========================+
          | keys           | yes      | Array of KeyEntry      |
          +----------------+----------+------------------------+
          | exported_at_ms | yes      | Unix epoch ms when the |
          |                |          | bundle was generated   |
          +----------------+----------+------------------------+
          | expires_at_ms  | yes      | Bundle validity window |
          +----------------+----------+------------------------+

                                 Table 43

   Each KeyEntry:

      +===================+==========+==============================+
      | Field             | Required | Description                  |
      +===================+==========+==============================+
      | key_id            | yes      | Key identifier matching      |
      |                   |          | signature_key_id on receipts |
      +-------------------+----------+------------------------------+
      | public_key_base64 | yes      | Base64url-encoded public key |
      |                   |          | bytes                        |
      +-------------------+----------+------------------------------+
      | algorithm         | yes      | Ed25519, ML-DSA-65, or       |
      |                   |          | Ed25519+ML-DSA-65            |
      +-------------------+----------+------------------------------+
      | valid_from_ms     | yes      | Key validity start           |
      +-------------------+----------+------------------------------+
      | expires_at_ms     | yes      | Key validity end             |
      +-------------------+----------+------------------------------+

                                  Table 44

Shovan                   Expires 1 January 2027                [Page 55]
Internet-Draft                     GAP                         June 2026

   The bundle is signed by the gateway's current root key so verifiers
   can authenticate it.  Export via GET /v1/gap/keys/bundle.  The bundle
   is distributable as a file, QR code, or USB for air-gapped
   deployments.

   Offline verifier rules: 1.  Load the bundle at provisioning time;
   verify bundle signature against locally-installed root public key 2.
   On receipt verification, look up key_id in local bundle only 3.  If
   key_id is not in the bundle, treat receipt as UNVERIFIABLE (not
   INVALID); the key may exist but has not been delivered yet 4.  If the
   bundle is expired, treat all receipts issued after expires_at_ms as
   UNVERIFIABLE until a fresh bundle is obtained

   A signature_algorithm field MUST be added to the CDRO envelope
   (alongside signature and signature_key_id): values: Ed25519, ML-DSA-
   65, Ed25519+ML-DSA-65.  Verifiers MUST NOT guess the algorithm from
   key length.

14.3.  Tenant Isolation

   A gateway MUST NOT return a CDRO belonging to one tenant in response
   to a request authenticated as another tenant.  The response for
   cross-tenant or not-found OIDs MUST be HTTP 404 to avoid confirming
   cross-tenant OID existence.

14.4.  Delegation Chain Security

   A gateway MUST evaluate the full delegation chain on every
   invocation.  A child grant whose parent has been revoked or expired
   MUST be denied, even if the child grant itself has not expired.
   Partial chain validation is insufficient.

14.5.  Provisional Block Fail-Closed

   For physical_safety: true grants, a gateway MUST treat the absence of
   on_expiry_without_quorum as renew.  A gateway MUST NOT allow revert
   semantics for physical-safety capabilities regardless of the field
   value.  This ensures that a gateway with misconfigured or absent
   provisional block policy defaults to the safer state (block
   maintained).

Shovan                   Expires 1 January 2027                [Page 56]
Internet-Draft                     GAP                         June 2026

14.6.  Workflow Signal Injection

   A gateway MUST validate the sender identity of every channel event
   before advancing a workflow stage.  Failure to validate enables
   unauthorized parties to advance or terminate HITL workflows by
   injecting signals.  The required_from_binding field provides the
   expected sender identity; its enforcement is REQUIRED for
   physical_safety: true stages.

14.7.  Idempotency Cache Staleness

   An idempotency cache hit MUST NOT return a cached approval receipt
   without re-validating that the underlying grant is still active.  A
   grant may be revoked after the original approval.  Returning a cached
   receipt for a revoked grant enables unauthorized capability exercise
   after revocation.

14.8.  Negative Numeric Scope Values

   For physical_safety: true capabilities, a gateway MUST reject
   invocation arguments that contain negative numeric values for scope-
   constrained keys, even when those values would satisfy an upper-bound
   check.  A negative value for a physical parameter (e.g.
   max_delta_units: -5.0) is almost always a sign of argument
   manipulation rather than a legitimate invocation.

14.9.  Bearer Token Security

   GAP does not define the authentication mechanism for the Bearer
   tokens used to authenticate gateway API calls.  Implementors MUST use
   short-lived tokens, rotate signing keys, and follow [RFC6750] for
   Bearer token handling.  Bearer token theft is not mitigated by this
   protocol; it is a deployment concern.

14.9.1.  Self-Sovereign Credential Mode

   For deployments without external connectivity or without a SynOI-
   operated token issuer, a Self-Sovereign Credential (SSC) mode
   provides an alternative authentication path.

   In SSC mode: 1.  The operator bootstraps a Local Credential Authority
   (LCA) by generating a local root signing keypair and publishing a
   gap:lca_root CDRO signed by the root key. 2.  Actor credentials are
   short-lived signed tokens bound to actor_oid and tenant_id, issued by
   the LCA and signed with ML-DSA-65 (or Ed25519 for non-NSS
   deployments). 3.  The gateway verifies actor credentials using the
   locally-held LCA public key only. 4.  Credential rotation requires
   only local key material.

Shovan                   Expires 1 January 2027                [Page 57]
Internet-Draft                     GAP                         June 2026

   The existing synoi-sk- Bearer token remains valid for cloud-connected
   deployments.  SSC mode is a normative alternative, not a deployment
   footnote.

   The gap:lca_root CDRO type is defined with body: {
   root_public_key_base64: string, algorithm: string, tenant_id: string,
   valid_from_ms: number, expires_at_ms: number }.

15.  IANA Considerations

   This document makes no requests to the Internet Assigned Numbers
   Authority (IANA) at this time.

   Future revisions of this specification MAY request registration of:

   *  A media type application/gap+json for GAP CDRO objects.

   *  A URI scheme gap: for portable CDRO references.

16.  Normative References

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119,
              DOI 10.17487/RFC2119, March 1997,
              <https://www.rfc-editor.org/info/rfc2119>.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
              May 2017, <https://www.rfc-editor.org/info/rfc8174>.

17.  Informative References

   [RFC6750]  Jones, M. and D. Hardt, "The OAuth 2.0 Authorization
              Framework: Bearer Token Usage", RFC 6750,
              DOI 10.17487/RFC6750, October 2012,
              <https://www.rfc-editor.org/info/rfc6750>.

   [RFC8259]  Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
              Interchange Format", STD 90, RFC 8259,
              DOI 10.17487/RFC8259, December 2017,
              <https://www.rfc-editor.org/info/rfc8259>.

   [RFC7517]  Jones, M., "JSON Web Key (JWK)", RFC 7517,
              DOI 10.17487/RFC7517, May 2015,
              <https://www.rfc-editor.org/info/rfc7517>.

Shovan                   Expires 1 January 2027                [Page 58]
Internet-Draft                     GAP                         June 2026

   [RFC8785]  Rundgren, A., Jordan, B., and S. Erdtman, "JSON
              Canonicalization Scheme (JCS)", RFC 8785,
              DOI 10.17487/RFC8785, June 2020,
              <https://www.rfc-editor.org/info/rfc8785>.

   [RFC8032]  Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital
              Signature Algorithm (EdDSA)", RFC 8032,
              DOI 10.17487/RFC8032, January 2017,
              <https://www.rfc-editor.org/info/rfc8032>.

Appendix A.  Normative References

   [RFC2119]:  Bradner, S., "Key words for use in RFCs to Indicate
      Requirement Levels", BCP 14, RFC 2119, March 1997.

   [RFC8174]:  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
      2119 Key Words", BCP 14, RFC 8174, May 2017.

   [RFC8032]:  Josefsson, S. and I.  Liusvaara, "Edwards-Curve Digital
      Signature Algorithm (EdDSA)", RFC 8032, January 2017.

   [RFC6750]:  Jones, M. and D.  Hardt, "The OAuth 2.0 Authorization
      Framework: Bearer Token Usage", RFC 6750, October 2012.

Appendix B.  Informative References

   [RFC8259]:  Bray, T., Ed., "The JavaScript Object Notation (JSON)
      Data Interchange Format", STD 90, RFC 8259, December 2017.

   [RFC8785]:  Rundgren, A., Jordan, B., and S.  Erdtman, "JSON
      Canonicalization Scheme (JCS)", RFC 8785, June 2020.

   [RFC7517]:  Jones, M., "JSON Web Key (JWK)", RFC 7517, May 2015.

Appendix C.  Acknowledgments

   The editors thank the SynOI protocol team and the early implementors
   who provided feedback on the wire format and grant evaluation
   algorithms during the cross-sector safety review that produced the
   ADR_006 safety protocol.

Author's Address

   Joshua Shovan
   SynOI Inc
   United States of America
   Email: joshua@foundationx.com
   URI:   https://github.com/synoi/synoi-gap/issues

Shovan                   Expires 1 January 2027                [Page 59]