From c166a0c482cbd67ef3d868e15d77ea41410cd56f Mon Sep 17 00:00:00 2001 From: brian_chuang Date: Wed, 16 Sep 2026 23:08:26 +0800 Subject: [PATCH 1/4] rfc: propose enterprise security extensions strategy (RFC 0006) - Propose RFC 0006 defining an opt-in architectural strategy and recommended extension profiles for enterprise-grade security capabilities (wire signing, tamper-evident audit ledger, asynchronous HITL suspension, TOCTOU integrity, and emergency administrative revocation). - Emphasize non-normative, opt-in profile design that preserves Core 0.1 minimalism and allows complete implementer flexibility over namespaces and algorithms. - Update rfcs/README.md with the RFC 0006 proposal summary. - Add test fixtures validating that Core 0.1 schema cleanly accepts events and responses carrying enterprise extensions. --- .../valid/pre-tool-use-wire-signing.json | 30 ++ .../valid/ask-with-hitl-suspension.json | 15 + ...enterprise-security-extensions-strategy.md | 424 ++++++++++++++++++ rfcs/README.md | 11 + 4 files changed, 480 insertions(+) create mode 100644 fixtures/hook-event/valid/pre-tool-use-wire-signing.json create mode 100644 fixtures/hook-response/valid/ask-with-hitl-suspension.json create mode 100644 rfcs/0006-enterprise-security-extensions-strategy.md diff --git a/fixtures/hook-event/valid/pre-tool-use-wire-signing.json b/fixtures/hook-event/valid/pre-tool-use-wire-signing.json new file mode 100644 index 0000000..9b2766a --- /dev/null +++ b/fixtures/hook-event/valid/pre-tool-use-wire-signing.json @@ -0,0 +1,30 @@ +{ + "spec": "agent-hooks/0.1", + "event_id": "8f2ab3e1-4c5d-4e6f-8a9b-0c1d2e3f4a5b", + "hook_event_name": "PreToolUse", + "session_id": "session-42", + "timestamp": "2026-09-17T02:30:00Z", + "sequence": 4, + "cwd": "/workspace/project", + "prompt_id": "prompt-17", + "tool_name": "Bash", + "tool_input": { + "command": "git status --short" + }, + "tool_use_id": "toolu-17", + "extensions": { + "sec.enterprise.crypto": { + "key_id": "key_enclave_prod_01", + "algorithm": "ed25519", + "canonical_algorithm": "RFC8785_JCS", + "canonical_hash": "sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", + "signature": "MEQCIE3b2r19xL0...signature_base64..." + }, + "sec.enterprise.audit": { + "sequence": 4, + "prev_record_hash": "a1b2c3d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0", + "record_hash": "f8e7d6c5b4a3928170efdcba9876543210fedcba0987654321abcdef01234567", + "tamper_evident_status": "verified" + } + } +} diff --git a/fixtures/hook-response/valid/ask-with-hitl-suspension.json b/fixtures/hook-response/valid/ask-with-hitl-suspension.json new file mode 100644 index 0000000..7633346 --- /dev/null +++ b/fixtures/hook-response/valid/ask-with-hitl-suspension.json @@ -0,0 +1,15 @@ +{ + "spec": "agent-hooks/0.1", + "event_id": "36c2b982-1d4c-4dc2-ae5b-a65139601741", + "decision": "ask", + "reason": "Administrative privileges required to access production database.", + "extensions": { + "sec.enterprise.hitl": { + "mode": "async_suspended", + "challenge_id": "ch_slack_prod_99182", + "resumption_channel": "slack://security-operations", + "expires_at": 1773729900, + "escalation_policy": "require_manager_approval" + } + } +} diff --git a/rfcs/0006-enterprise-security-extensions-strategy.md b/rfcs/0006-enterprise-security-extensions-strategy.md new file mode 100644 index 0000000..a9f9b3c --- /dev/null +++ b/rfcs/0006-enterprise-security-extensions-strategy.md @@ -0,0 +1,424 @@ +--- +title: "RFC 0006: Enterprise Security Extensions Strategy & Non-Core Capabilities" +status: Draft +discussion: "Pending — repository Discussions are not enabled" +review-start: "Not started" +review-end: "Not scheduled" +maintainer-votes: [] +decision: "Pending" +supersedes: [] +superseded-by: [] +--- + +# RFC 0006: Enterprise Security Extensions Strategy & Non-Core Capabilities + +## Summary + +This RFC proposes a standardized architectural strategy and recommended extension profiles for enterprise-grade security capabilities within the Agent Hook ecosystem. + +To preserve the minimalism, zero-dependency, and lightweight nature of the **Agent Hook Core 0.1 Specification**, heavy enterprise defense features—such as cryptographic wire signing (Ed25519/TPM), tamper-evident audit ledgers (hash-chaining), asynchronous Human-in-the-Loop (HITL) suspension, Time-of-Check to Time-of-Use (TOCTOU) payload verification, and out-of-band administrative session revocation—are explicitly designated as **optional, non-core extension profiles**. + +These profiles leverage the existing standard `extensions` container defined in [`spec/0.1/extensions.md`](../spec/0.1/extensions.md). Conforming implementations are free to adopt, customize, or omit these extensions without breaking Core 0.1 interoperability. + +--- + +## Motivation + +### The Architectural Dilemma + +When designing runtime security governance standards for autonomous AI agents, two distinct sets of requirements emerge: + +1. **Open Source & Lightweight Developers (OSS / Consumer Agents)**: + - Demand zero heavy dependencies, minimal overhead, and absolute ease of adoption. + - A single-file Python script or simple TypeScript agent should run without needing C cryptography bindings, hardware TPM drivers, or asynchronous webhook suspension queues. +2. **Enterprise, FinTech, & Regulated Sectors (Enterprise / GovTech / SEC Compliance)**: + - Demand non-repudiation, tamper-evident audit trails for forensic admissibility, cryptographic hardware identity, asynchronous human approval across corporate chat tools (Slack/Teams), and immediate administrative kill-switches. + +Forcing heavy enterprise armor into the **Core 0.1 normative specification** would alienate open-source developers and slow down adoption. Conversely, providing no standard for enterprise capabilities leads to fragmentation, proprietary vendor lock-in, and incompatible custom forks. + +### Guiding Philosophy + +> **"Core does subtraction (protecting a universal minimal baseline); Extensions do addition (mounting modular enterprise armor on demand)."** + +By establishing a standardized yet strictly optional **Enterprise Security Extension Profile**, this RFC provides a common blueprint for high-security implementations (such as PEP proxies like NVIDIA NeMo Guardrails/Relay and Policy Decision Points like Trend Micro Vision One) while guaranteeing 100% interoperability with lightweight Core 0.1 agents. + +--- + +## Proposal + +### 1. Scope & Core vs. Non-Core Boundary + +The following capabilities are classified as **Tier 2 (Enterprise Extension Profiles)** and **Tier 3 (Control-Plane Operations)**: + +``` ++---------------------------------------------------------------------------------------------------+ +| Agent Hook Security Governance Hierarchy | ++---------------------------------------------------------------------------------------------------+ +| | +| [ TIER 1: Core 0.1 Protocol ] (Normative Baseline) | +| - Flat JSON Envelopes, Top-Level Decision & Reason, 18 Core Lifecycle Events | +| | ++---------------------------------------------------------------------------------------------------+ +| | +| [ TIER 2: Enterprise Extension Profiles ] (Optional Profiles via extensions[...]) | +| | +| 1. Zero-Trust Wire Signing 2. Tamper-Evident Ledger 3. Asynchronous HITL 4. TOCTOU Guard | +| (sec.enterprise.crypto) (sec.enterprise.audit) (sec.enterprise.hitl) (sec.enterprise.integrity) | +| | ++---------------------------------------------------------------------------------------------------+ +| | +| [ TIER 3: Control-Plane Management ] (Out-of-band Administrative Channel) | +| | +| 5. Emergency Administrative Kill Switch (x-nemo/SessionRevoke or POST /sessions/{id}/revoke) | +| | ++---------------------------------------------------------------------------------------------------+ +``` + +#### Gap Analysis of Non-Core Capabilities + +| Capability | Primary Value | Why Kept Out of Core 0.1 | Recommended Disposition | +| :--- | :--- | :--- | :--- | +| **1. Cryptographic Wire Signing** | Hardware-grade authenticity (Ed25519/TPM); prevents spoofed agent events. | Requires crypto dependencies (`cryptography`, libsodium) and key management infrastructure. | Optional profile under `extensions["sec.enterprise.crypto"]` or HTTP header `Hook-Signature`. | +| **2. Tamper-Evident Audit Ledger** | Hash-chained records (`prev_record_hash`) providing forensic non-repudiation. | Imposes sequencing and storage overhead unsuitable for stateless lambdas/microservices. | Optional profile under `extensions["sec.enterprise.audit"]`. | +| **3. Asynchronous HITL Suspension** | Async suspension with signed resumption tokens (`ApprovalGrantToken`) via Slack/Teams. | Involves long-lived state queues and callback channels beyond Core synchronous request/response. | Optional profile under `extensions["sec.enterprise.hitl"]`. | +| **4. TOCTOU Integrity Verification** | Compares payload hash between approval time and execution time. | Application-level invariant check rather than lifecycle dispatch primitive. | Optional profile under `extensions["sec.enterprise.integrity"]`. | +| **5. Emergency Session Revocation** | Out-of-band administrative command to immediately sever agent network & revoke grants. | Control-plane operation, fundamentally distinct from inside-out agent data-plane lifecycle events. | Out-of-band control endpoint (`POST /sessions/{id}/revoke`) or namespaced event `x-nemo/SessionRevoke`. | + +--- + +### 2. Normative Rules for Extensions + +All extensions proposed in this RFC adhere strictly to [`spec/0.1/extensions.md`](../spec/0.1/extensions.md): + +1. **Non-Mandatory (Opt-In)**: No agent, PEP, or PDP is required to implement any extension defined herein to claim Core 0.1 compliance. +2. **Safe to Ignore**: A consumer that does not understand an extension namespace MUST ignore it without failing validation. +3. **Namespace Autonomy & Customization**: + - The namespaces defined in this document (e.g., `sec.enterprise.*` or reverse-DNS `com.trendmicro.security.*`) represent **recommended public profiles**. + - Conforming to Core 0.1 `spec/0.1/extensions.md`, property names under `extensions` MUST be reverse-DNS namespaces using dot notation (`^(?:[a-z][a-z0-9-]*\.)+[a-z][a-z0-9-]*$`). + - Implementers are free to define proprietary namespaces (e.g., `com.mycompany.security.crypto`) or customize property keys according to their internal architecture. +4. **Parameterized & Open Algorithms**: + - Cryptographic and hashing algorithms specified in example payloads are **parameterized**. Implementers MAY choose alternative algorithms (e.g., `rsa-pss`, `ecdsa-p256`, post-quantum algorithms like `dilithium`, or alternative hashes like `blake3` and `sha3-512`). + +--- + +### 3. Recommended Profile Specifications + +#### 3.1 Profile: Cryptographic Wire Signing (`sec.enterprise.crypto`) + +Used to guarantee message authenticity and provenance between Agent, PEP (Relay), and PDP. + +##### Request / Response Example +```json +{ + "spec": "agent-hooks/0.1", + "event_id": "01J8ABCDEF1234567890abcdef", + "hook_event_name": "PreToolUse", + "session_id": "sess_production_9981", + "timestamp": "2026-09-17T02:30:00Z", + "tool_name": "bash", + "tool_input": { + "command": "uname -a" + }, + "extensions": { + "sec.enterprise.crypto": { + "key_id": "key_enclave_prod_01", + "algorithm": "ed25519", + "canonical_algorithm": "RFC8785_JCS", + "canonical_hash": "sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", + "signature": "MEQCIE...base64_encoded_signature..." + } + } +} +``` + +* **`key_id`** *(string, required)*: Identifier of the public key registered in the enterprise directory. +* **`algorithm`** *(string, optional, default: `"ed25519"`)*: Cryptographic signing algorithm. Implementations MAY specify `"rsa-pss"`, `"ecdsa-p256"`, or post-quantum variants. +* **`canonical_algorithm`** *(string, optional, default: `"RFC8785_JCS"`)*: Canonicalization method used before hashing. +* **`canonical_hash`** *(string, required)*: Hex-encoded digest (`:`). +* **`signature`** *(string, required)*: Base64-encoded signature over the canonical hash. + +Alternatively, transport-level implementations MAY transport this metadata via HTTP header: +```http +Hook-Signature: key_id="key_enclave_prod_01", alg="ed25519", sig="MEQCIE..." +``` + +--- + +#### 3.2 Profile: Tamper-Evident Audit Ledger (`sec.enterprise.audit`) + +Enables forensic verification of agent operation history using back-linked hash chains. + +##### Example Payload +```json +{ + "extensions": { + "sec.enterprise.audit": { + "sequence": 42, + "prev_record_hash": "a1b2c3d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0", + "record_hash": "f8e7d6c5b4a3928170efdcba9876543210fedcba0987654321abcdef01234567", + "hash_algorithm": "sha256", + "tamper_evident_status": "verified" + } + } +} +``` + +* **`sequence`** *(integer, required)*: Monotonically increasing event sequence index for the session. +* **`prev_record_hash`** *(string, required)*: Hex-encoded hash of the previous ledger record (or genesis seed string for `sequence: 0`). +* **`record_hash`** *(string, required)*: Hex-encoded hash of the current record including `prev_record_hash`. +* **`hash_algorithm`** *(string, optional, default: `"sha256"`)*: Hash algorithm used (`"sha256"`, `"sha3-512"`, `"blake3"`). +* **`tamper_evident_status`** *(string, optional)*: State evaluation by the verification point (`"verified"`, `"broken_chain"`, `"unverified"`). + +--- + +#### 3.3 Profile: Asynchronous HITL Suspension (`sec.enterprise.hitl`) + +Standardizes asynchronous human intervention when a Policy Decision Point returns `decision: "ask"`. + +##### PDP Response with Suspension Challenge +```json +{ + "spec": "agent-hooks/0.1", + "event_id": "01J8ABCDEF1234567891abcdef", + "decision": "ask", + "reason": "Execution of bash shell with root privilege requires administrator sign-off.", + "extensions": { + "sec.enterprise.hitl": { + "mode": "async_suspended", + "challenge_id": "ch_slack_prod_99182", + "resumption_channel": "slack://security-operations", + "expires_at": 1773729900, + "escalation_policy": "require_manager_approval" + } + } +} +``` + +##### Resumption Callback Request (Triggered by Slack/Teams Approval) +When approved by an authorized administrator, the enterprise PDP or callback service invokes the PEP resumption endpoint with an authorized grant token: +```json +{ + "spec": "agent-hooks/0.1", + "event_id": "01J8ABCDEF1234567892abcdef", + "decision": "allow", + "reason": "Approved by security administrator Alice.", + "extensions": { + "sec.enterprise.hitl": { + "challenge_id": "ch_slack_prod_99182", + "approval_grant_token": "agt_eyJhbGciOiJFZERTQ...", + "approved_by": "alice.security.lead@example.com", + "approved_at": 1773726500, + "bound_tool_hash": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + } +} +``` + +* **`mode`** *(string, optional)*: `"sync_prompt"` (synchronous user prompt) or `"async_suspended"` (long-lived asynchronous suspension). +* **`challenge_id`** *(string, required)*: Unique identifier for the human approval challenge. +* **`approval_grant_token`** *(string, optional)*: Cryptographically signed single-use grant token. +* **`expires_at`** *(integer, optional)*: Unix epoch timestamp indicating expiration of the approval challenge. + +--- + +#### 3.4 Profile: TOCTOU Content Fingerprint (`sec.enterprise.integrity`) + +Defends against Time-of-Check to Time-of-Use (TOCTOU) payload swapping attacks between policy verification and tool execution. + +##### Example Payload +```json +{ + "extensions": { + "sec.enterprise.integrity": { + "content_identity": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "enforce_toctou_pre_dispatch": true + } + } +} +``` + +* **`content_identity`** *(string, required)*: Cryptographic hash of the serialized tool input arguments (`tool_input`). +* **`enforce_toctou_pre_dispatch`** *(boolean, optional, default: `true`)*: Instructs the PEP/host to verify that the executed parameters match `content_identity` identically prior to invocation. + +--- + +### 4. Control-Plane Operation: SessionRevoke Emergency Kill Switch + +#### Distinction between Data Plane and Control Plane + +* **Data-Plane Lifecycle Events** ([`events.md`](../spec/0.1/events.md)): Fired inside-out by the Agent runtime as it progresses (e.g., `PreToolUse`, `AfterModelResponse`). +* **Control-Plane Management Commands**: Fired outside-in by administrative systems or SOC platforms to instruct the PEP/host to terminate execution immediately. + +#### Recommended Implementation Formats + +Implementations MAY support administrative revocation through either: + +1. **REST Management Endpoint**: + ```http + POST /v1/sessions/{session_id}/revoke HTTP/1.1 + Host: relay.enterprise.local + Authorization: Bearer + Content-Type: application/json + + { + "reason": "Compromised credentials detected on host machine.", + "revoked_by": "soc_incident_responder_42", + "terminate_subagents": true + } + ``` +2. **Namespaced Extension Event**: + On internal event buses, implementations MAY emit an extension event adhering to `spec/0.1/events.md`: + ```json + { + "spec": "agent-hooks/0.1", + "event_id": "01J8ABCDEF1234567893abcdef", + "hook_event_name": "x-nemo/SessionRevoke", + "session_id": "sess_production_9981", + "timestamp": "2026-09-17T02:35:00Z", + "extensions": { + "sec.enterprise.control": { + "action": "terminate", + "reason": "Administrative kill-switch invoked by SOC" + } + } + } + ``` + +Upon receiving a valid revocation command, the PEP/Host MUST: +- Invalidate all active tokens and standing authorizations associated with `session_id`. +- Terminate or cleanly interrupt running subagents and child tasks. +- Sever external network egress proxy connections for the session. +- Append a terminal record to the audit ledger. + +--- + +## Roles & Responsibilities + +``` ++----------------+ Core 0.1 Events +-------------------+ Enriched Extensions +----------------------+ +| AI Agent Host | ──────────────────────────> | NeMo Relay (PEP) | ─────────────────────────> | Security PDP (Trend) | +| (Lightweight) | <────────────────────────── | (Security Proxy) | <───────────────────────── | (Policy Engine) | ++----------------+ Standard Allow/Deny +-------------------+ UniversalDecision +----------------------+ + │ │ + │ Asynchronous Suspension (202) │ Slack/Teams + ▼ ▼ + [ Suspension Store ] [ Corporate HITL ] +``` + +1. **AI Agent Host (Lightweight)**: + - Needs only Core 0.1 compliance. + - Emits standard lifecycle events. + - Transparently retains `extensions` without modifying or stripping unrecognized fields. +2. **NeMo Relay / Interceptor Proxy (PEP)**: + - Bridges the lightweight Agent with heavy enterprise infrastructure. + - Offloads cryptographic signing, hash-chain ledger maintenance, and connection suspension from the Agent runtime. + - Enforces TOCTOU verification before dispatching tool executions. +3. **Security Vendor / PDP (e.g., Trend Micro Vision One)**: + - Evaluates incoming events against enterprise threat intelligence and security policies. + - Verifies wire signatures and ledger continuity. + - Returns top-level `decision: "ask"` and issues signed `ApprovalGrantToken` upon human authorization. + - Issues out-of-band `SessionRevoke` commands when high-severity incidents are detected. + +--- + +## Compatibility Impact + +- **Core 0.1 Compatibility**: **100% Compatible**. All mechanisms defined in this RFC reside inside the `extensions` dictionary or out-of-band endpoints. No Core schema fields or mandatory behaviors are altered. +- **Backward Compatibility**: Existing agents that do not understand these extensions continue to function normally. Gate decisions (`allow`, `deny`, `ask`, `defer`) remain in their canonical top-level format. + +--- + +## Security and Privacy Impact + +- **Enhanced Integrity**: Cryptographic wire signing and TOCTOU protection prevent adversarial injection and man-in-the-middle tampering. +- **Legal Non-Repudiation**: Hash-chained ledgers provide tamper-evident records suitable for enterprise compliance audits (SOC 2, ISO 27001, GDPR). +- **Privacy Considerations**: Extension payloads (such as audit hashes) SHOULD hash rather than log raw sensitive parameters (PII/secrets) unless explicitly intended for encrypted secure audit vaults. + +--- + +## Alternatives Considered + +1. **Mandating signing and hash chains in Core 0.1**: Rejected. Would impose C-extension dependencies and high storage overhead on open-source, CLI, and resource-constrained agents. +2. **Using proprietary vendor protocols outside Agent Hook**: Rejected. Would fragment the ecosystem and force enterprise agents into disparate non-interoperable silos. +3. **Encoding non-core decisions inside `hookSpecificOutput`**: Rejected. Top-level `decision` and `reason` cleanly separate the control plane from data mutations, keeping extensions strictly focused on auxiliary governance metadata. + +--- + +## Appendix: Reference JSON Schemas for Extension Profiles + +The following JSON Schemas illustrate how implementations may validate extension payloads independently of the core specification. + +### A.1 Wire Signing Profile (`sec.enterprise.crypto`) +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "EnterpriseCryptoExtension", + "type": "object", + "properties": { + "key_id": { "type": "string" }, + "algorithm": { "type": "string", "default": "ed25519" }, + "canonical_algorithm": { "type": "string", "default": "RFC8785_JCS" }, + "canonical_hash": { "type": "string", "pattern": "^[a-z0-9-]+:[a-f0-9]+$" }, + "signature": { "type": "string" } + }, + "required": ["key_id", "canonical_hash", "signature"], + "additionalProperties": true +} +``` + +### A.2 Audit Ledger Profile (`sec.enterprise.audit`) +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "EnterpriseAuditExtension", + "type": "object", + "properties": { + "sequence": { "type": "integer", "minimum": 0 }, + "prev_record_hash": { "type": "string" }, + "record_hash": { "type": "string" }, + "hash_algorithm": { "type": "string", "default": "sha256" }, + "tamper_evident_status": { "type": "string", "enum": ["verified", "broken_chain", "unverified"] } + }, + "required": ["sequence", "prev_record_hash", "record_hash"], + "additionalProperties": true +} +``` + +### A.3 Asynchronous HITL Profile (`sec.enterprise.hitl`) +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "EnterpriseHitlExtension", + "type": "object", + "properties": { + "mode": { "type": "string", "enum": ["sync_prompt", "async_suspended"] }, + "challenge_id": { "type": "string" }, + "approval_grant_token": { "type": "string" }, + "resumption_channel": { "type": "string" }, + "expires_at": { "type": "integer" }, + "approved_by": { "type": "string" }, + "approved_at": { "type": "integer" }, + "bound_tool_hash": { "type": "string" } + }, + "required": ["challenge_id"], + "additionalProperties": true +} +``` + +--- + +## References + +- [RFC 0001: Agent Hook 0.1 Core Event Contract](./0001-agent-hook-core-event-contract.md). +- [RFC 0004: Standard network, memory, and configuration lifecycle events](./0004-standard-lifecycle-events.md). +- [RFC 0005: Inspect and control response content with PostNetworkAccess](./0005-network-response-delivery-inspection.md). +- [Core protocol](../spec/0.1/core.md). +- [Event registry](../spec/0.1/events.md). +- [Extension policy](../spec/0.1/extensions.md). +- [Security considerations](../spec/0.1/security.md). + +--- + +## Decision record + +Pending. The prerequisite Discussion, public review window, and formal maintainer votes remain outstanding under repository governance. Core 0.1 remains the normative baseline. diff --git a/rfcs/README.md b/rfcs/README.md index 3ccf512..e3acc93 100644 --- a/rfcs/README.md +++ b/rfcs/README.md @@ -26,3 +26,14 @@ names, preserves Observe-only implementations and the fail-open default, and requires explicit configuration for the revised control semantics. The draft awaits the prerequisite Discussion and formal review; the current specification and schemas do not yet implement it. + +## Enterprise security extensions proposal + +[RFC 0006](./0006-enterprise-security-extensions-strategy.md) proposes an +opt-in architectural strategy and recommended extension profiles for +enterprise-grade security capabilities (cryptographic wire signing, +tamper-evident audit ledgers, asynchronous HITL suspension, TOCTOU payload +integrity, and administrative session revocation). It preserves Core 0.1 +minimalism and interoperability while offering standard integration blueprints +for enterprise PEPs (e.g. NeMo Relay) and PDPs (e.g. Trend Micro Vision One). + From ab06a97e141468d42c2cd2bda81dd5c06bb0ed83 Mon Sep 17 00:00:00 2001 From: brian_chuang Date: Thu, 17 Sep 2026 17:31:49 +0800 Subject: [PATCH 2/4] rfc: add failure and degradation enforcement profile to RFC 0006 --- .../valid/fail-closed-degradation-policy.json | 24 ++++ ...enterprise-security-extensions-strategy.md | 123 +++++++++++++++++- rfcs/README.md | 3 +- 3 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 fixtures/hook-response/valid/fail-closed-degradation-policy.json diff --git a/fixtures/hook-response/valid/fail-closed-degradation-policy.json b/fixtures/hook-response/valid/fail-closed-degradation-policy.json new file mode 100644 index 0000000..65fbb57 --- /dev/null +++ b/fixtures/hook-response/valid/fail-closed-degradation-policy.json @@ -0,0 +1,24 @@ +{ + "spec": "agent-hooks/0.1", + "event_id": "47d3c093-2e5d-4ed3-bf6c-b76240712852", + "decision": "deny", + "reason": "Security policy requires fail-closed enforcement on uninspected tool execution during PDP timeout.", + "extensions": { + "sec.enterprise.degradation": { + "profile_version": "1.0", + "mode": "strict_fail_closed", + "applicable_gates": [ + "PreToolUse", + "PreNetworkAccess", + "PreMemoryWrite" + ], + "applicable_failures": [ + "timeout", + "transport_error", + "http_server_error", + "native_validation_failure" + ], + "audit_alert": true + } + } +} diff --git a/rfcs/0006-enterprise-security-extensions-strategy.md b/rfcs/0006-enterprise-security-extensions-strategy.md index a9f9b3c..ed2d206 100644 --- a/rfcs/0006-enterprise-security-extensions-strategy.md +++ b/rfcs/0006-enterprise-security-extensions-strategy.md @@ -62,14 +62,17 @@ The following capabilities are classified as **Tier 2 (Enterprise Extension Prof | | | [ TIER 2: Enterprise Extension Profiles ] (Optional Profiles via extensions[...]) | | | -| 1. Zero-Trust Wire Signing 2. Tamper-Evident Ledger 3. Asynchronous HITL 4. TOCTOU Guard | -| (sec.enterprise.crypto) (sec.enterprise.audit) (sec.enterprise.hitl) (sec.enterprise.integrity) | +| 1. Zero-Trust Wire Signing 2. Tamper-Evident Ledger 3. Asynchronous HITL | +| (sec.enterprise.crypto) (sec.enterprise.audit) (sec.enterprise.hitl) | +| | +| 4. TOCTOU Integrity Guard 5. Failure & Degradation Enforcement | +| (sec.enterprise.integrity) (sec.enterprise.degradation) | | | +---------------------------------------------------------------------------------------------------+ | | | [ TIER 3: Control-Plane Management ] (Out-of-band Administrative Channel) | | | -| 5. Emergency Administrative Kill Switch (x-nemo/SessionRevoke or POST /sessions/{id}/revoke) | +| 6. Emergency Administrative Kill Switch (x-nemo/SessionRevoke or POST /sessions/{id}/revoke) | | | +---------------------------------------------------------------------------------------------------+ ``` @@ -82,7 +85,8 @@ The following capabilities are classified as **Tier 2 (Enterprise Extension Prof | **2. Tamper-Evident Audit Ledger** | Hash-chained records (`prev_record_hash`) providing forensic non-repudiation. | Imposes sequencing and storage overhead unsuitable for stateless lambdas/microservices. | Optional profile under `extensions["sec.enterprise.audit"]`. | | **3. Asynchronous HITL Suspension** | Async suspension with signed resumption tokens (`ApprovalGrantToken`) via Slack/Teams. | Involves long-lived state queues and callback channels beyond Core synchronous request/response. | Optional profile under `extensions["sec.enterprise.hitl"]`. | | **4. TOCTOU Integrity Verification** | Compares payload hash between approval time and execution time. | Application-level invariant check rather than lifecycle dispatch primitive. | Optional profile under `extensions["sec.enterprise.integrity"]`. | -| **5. Emergency Session Revocation** | Out-of-band administrative command to immediately sever agent network & revoke grants. | Control-plane operation, fundamentally distinct from inside-out agent data-plane lifecycle events. | Out-of-band control endpoint (`POST /sessions/{id}/revoke`) or namespaced event `x-nemo/SessionRevoke`. | +| **5. Failure & Degradation Enforcement** | Enforces fail-closed or bounded-open degradation under handler outage, timeout, or validation rejection. | Core 0.1 mandates baseline fail-open to preserve agent availability without defining complex failure state machines. | Optional profile under `extensions["sec.enterprise.degradation"]` or host capability metadata. | +| **6. Emergency Session Revocation** | Out-of-band administrative command to immediately sever agent network & revoke grants. | Control-plane operation, fundamentally distinct from inside-out agent data-plane lifecycle events. | Out-of-band control endpoint (`POST /sessions/{id}/revoke`) or namespaced event `x-nemo/SessionRevoke`. | --- @@ -242,7 +246,82 @@ Defends against Time-of-Check to Time-of-Use (TOCTOU) payload swapping attacks b --- -### 4. Control-Plane Operation: SessionRevoke Emergency Kill Switch +### Extension Profile 5: Failure & Degradation Enforcement (`sec.enterprise.degradation`) + +#### Motivation & Threat Model + +While Core 0.1 specifies a baseline **fail-open** policy under handler errors or timeouts (to ensure lightweight, local, or experimental agents do not break unexpectedly), enterprise environments and regulated deployments operate under a Zero-Trust threat model: +- If a security policy handler, network proxy, or credential vault times out or crashes, allowing an unvetted `PreToolUse`, `PreNetworkAccess`, or `PreMemoryWrite` operation to proceed creates severe breach and prompt injection exposure. +- Conversely, an unconditional fail-closed policy across non-critical events could cause unnecessary availability outages during brief network jitter. + +To resolve this conflict without breaking Core 0.1 minimalism, this profile establishes an opt-in degradation specification that defines: +1. **Precise Failure Classes** that trigger degradation. +2. **Deterministic Enforcement Modes** (`strict_fail_closed`, `bounded_open`, `fail_open_monitored`). +3. **Bounded-Open Circuit Breakers** with defined state transitions and exhaustion thresholds. +4. **Explicit Precedence** over the Core 0.1 fail-open default. + +#### Recommended Payload Structure + +This profile MAY be declared in the host's capability metadata or attached to hook responses to configure degradation rules per gate: + +```json +{ + "extensions": { + "sec.enterprise.degradation": { + "profile_version": "1.0", + "mode": "bounded_open", + "applicable_gates": [ + "PreToolUse", + "PreNetworkAccess", + "PreMemoryWrite", + "SubagentStart" + ], + "applicable_failures": [ + "timeout", + "transport_error", + "http_server_error", + "malformed_response", + "native_validation_failure" + ], + "bounded_open_policy": { + "max_consecutive_failures": 3, + "window_seconds": 60, + "cooldown_seconds": 300, + "on_exhausted": "fail_closed" + }, + "audit_alert": true + } + } +} +``` + +#### Specification of Parameters + +* **`mode`** *(string, required)*: + - `"strict_fail_closed"`: Any failure in `applicable_failures` for a covered Gate results in immediate operation denial (`decision: "deny"`) and aborts the pending turn. + - `"bounded_open"`: Allows up to `max_consecutive_failures` within `window_seconds`. If failures exceed the bound, the circuit trips to `on_exhausted` (default: `"fail_closed"`). + - `"fail_open_monitored"`: Follows the Core 0.1 fail-open behavior, but generates high-priority security telemetry and audit events. +* **`applicable_gates`** *(array of strings, optional)*: List of Core Gate names to which this enforcement applies. Defaults to all Gates declared by the host. +* **`applicable_failures`** *(array of strings, required)*: + - `"timeout"`: Handler fails to reply before `timeout_ms` expires. + - `"transport_error"`: Connection refusal, DNS resolution failure, or TCP connection reset. + - `"http_server_error"`: Webhook or proxy responds with HTTP 5xx status codes. + - `"malformed_response"`: Response body fails JSON parsing, schema validation, or signature verification. + - `"native_validation_failure"`: Schema-valid mutated payload (e.g. `updatedInput`, `updatedPrompt`) is rejected by the host runtime's native validation. +* **`bounded_open_policy`** *(object, optional)*: + - **`max_consecutive_failures`** *(integer, minimum: 1)*: Maximum allowed consecutive failures before tripping. + - **`window_seconds`** *(integer, minimum: 1)*: Rolling evaluation window in seconds. + - **`cooldown_seconds`** *(integer, minimum: 1)*: Time period the circuit remains tripped before attempting half-open recovery. + - **`on_exhausted`** *(string, enum: `["fail_closed", "require_interactive_approval"]`)*: Action to take once the bound is exhausted. +* **`audit_alert`** *(boolean, optional, default: `true`)*: When `true`, emits an enterprise audit record or alert for every degraded event. + +#### Precedence Rule against Core 0.1 + +When `sec.enterprise.degradation` is configured on a host or returned by an enterprise PDP, its rules **MUST take precedence** over Core 0.1 default fail-open behavior for all gates listed in `applicable_gates`. If an unlisted Gate fails, it falls back to the Core 0.1 baseline. + +--- + +### 5. Control-Plane Operation: SessionRevoke Emergency Kill Switch #### Distinction between Data Plane and Control Plane @@ -405,6 +484,40 @@ The following JSON Schemas illustrate how implementations may validate extension } ``` +### A.4 Failure & Degradation Profile (`sec.enterprise.degradation`) +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "EnterpriseDegradationExtension", + "type": "object", + "properties": { + "profile_version": { "type": "string" }, + "mode": { "type": "string", "enum": ["strict_fail_closed", "bounded_open", "fail_open_monitored"] }, + "applicable_gates": { "type": "array", "items": { "type": "string" } }, + "applicable_failures": { + "type": "array", + "items": { + "type": "string", + "enum": ["timeout", "transport_error", "http_server_error", "malformed_response", "native_validation_failure"] + } + }, + "bounded_open_policy": { + "type": "object", + "properties": { + "max_consecutive_failures": { "type": "integer", "minimum": 1 }, + "failure_rate_threshold": { "type": "number", "minimum": 0, "maximum": 1 }, + "window_seconds": { "type": "integer", "minimum": 1 }, + "cooldown_seconds": { "type": "integer", "minimum": 1 }, + "on_exhausted": { "type": "string", "enum": ["fail_closed", "require_interactive_approval"] } + } + }, + "audit_alert": { "type": "boolean" } + }, + "required": ["mode", "applicable_failures"], + "additionalProperties": true +} +``` + --- ## References diff --git a/rfcs/README.md b/rfcs/README.md index e3acc93..9e4cdfa 100644 --- a/rfcs/README.md +++ b/rfcs/README.md @@ -33,7 +33,8 @@ and schemas do not yet implement it. opt-in architectural strategy and recommended extension profiles for enterprise-grade security capabilities (cryptographic wire signing, tamper-evident audit ledgers, asynchronous HITL suspension, TOCTOU payload -integrity, and administrative session revocation). It preserves Core 0.1 +integrity, failure and degradation enforcement like fail-closed and bounded-open, +and administrative session revocation). It preserves Core 0.1 minimalism and interoperability while offering standard integration blueprints for enterprise PEPs (e.g. NeMo Relay) and PDPs (e.g. Trend Micro Vision One). From a805d36f9a7c36b2be52b99f7f0c6952e8041159 Mon Sep 17 00:00:00 2001 From: brian_chuang Date: Thu, 17 Sep 2026 21:54:07 +0800 Subject: [PATCH 3/4] rfc: specify bounded_open FSM and validate RFC examples against Core 0.1 schemas --- .../valid/pre-tool-use-wire-signing.json | 19 +--- .../valid/vendor-session-revoke.json | 14 +++ .../bounded-open-degradation-policy.json | 31 ++++++ .../valid/hitl-resumption-allow.json | 15 +++ ...enterprise-security-extensions-strategy.md | 105 +++++++++++++----- scripts/validate.mjs | 23 ++++ 6 files changed, 169 insertions(+), 38 deletions(-) create mode 100644 fixtures/hook-event/valid/vendor-session-revoke.json create mode 100644 fixtures/hook-response/valid/bounded-open-degradation-policy.json create mode 100644 fixtures/hook-response/valid/hitl-resumption-allow.json diff --git a/fixtures/hook-event/valid/pre-tool-use-wire-signing.json b/fixtures/hook-event/valid/pre-tool-use-wire-signing.json index 9b2766a..a52cbdf 100644 --- a/fixtures/hook-event/valid/pre-tool-use-wire-signing.json +++ b/fixtures/hook-event/valid/pre-tool-use-wire-signing.json @@ -2,29 +2,22 @@ "spec": "agent-hooks/0.1", "event_id": "8f2ab3e1-4c5d-4e6f-8a9b-0c1d2e3f4a5b", "hook_event_name": "PreToolUse", - "session_id": "session-42", + "session_id": "sess_production_9981", "timestamp": "2026-09-17T02:30:00Z", "sequence": 4, - "cwd": "/workspace/project", - "prompt_id": "prompt-17", - "tool_name": "Bash", + "prompt_id": "prompt-9981", + "tool_use_id": "toolu-9981-bash", + "tool_name": "bash", "tool_input": { - "command": "git status --short" + "command": "uname -a" }, - "tool_use_id": "toolu-17", "extensions": { "sec.enterprise.crypto": { "key_id": "key_enclave_prod_01", "algorithm": "ed25519", "canonical_algorithm": "RFC8785_JCS", "canonical_hash": "sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", - "signature": "MEQCIE3b2r19xL0...signature_base64..." - }, - "sec.enterprise.audit": { - "sequence": 4, - "prev_record_hash": "a1b2c3d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0", - "record_hash": "f8e7d6c5b4a3928170efdcba9876543210fedcba0987654321abcdef01234567", - "tamper_evident_status": "verified" + "signature": "MEQCIE...base64_encoded_signature..." } } } diff --git a/fixtures/hook-event/valid/vendor-session-revoke.json b/fixtures/hook-event/valid/vendor-session-revoke.json new file mode 100644 index 0000000..28c1384 --- /dev/null +++ b/fixtures/hook-event/valid/vendor-session-revoke.json @@ -0,0 +1,14 @@ +{ + "spec": "agent-hooks/0.1", + "event_id": "9f3bc4e2-5d6e-4f7a-9b0c-1d2e3f4a5b6c", + "hook_event_name": "x-nemo/SessionRevoke", + "session_id": "sess_production_9981", + "timestamp": "2026-09-17T02:35:00Z", + "sequence": 100, + "extensions": { + "sec.enterprise.control": { + "action": "terminate", + "reason": "Administrative kill-switch invoked by SOC" + } + } +} diff --git a/fixtures/hook-response/valid/bounded-open-degradation-policy.json b/fixtures/hook-response/valid/bounded-open-degradation-policy.json new file mode 100644 index 0000000..738566c --- /dev/null +++ b/fixtures/hook-response/valid/bounded-open-degradation-policy.json @@ -0,0 +1,31 @@ +{ + "spec": "agent-hooks/0.1", + "event_id": "b4e2d194-3f6e-4fe4-8f7d-c87351823963", + "decision": "allow", + "extensions": { + "sec.enterprise.degradation": { + "profile_version": "1.0", + "mode": "bounded_open", + "applicable_gates": [ + "PreToolUse", + "PreNetworkAccess", + "PreMemoryWrite", + "SubagentStart" + ], + "applicable_failures": [ + "timeout", + "transport_error", + "http_server_error", + "malformed_response", + "native_validation_failure" + ], + "bounded_open_policy": { + "max_consecutive_failures": 3, + "window_seconds": 60, + "cooldown_seconds": 300, + "on_exhausted": "fail_closed" + }, + "audit_alert": true + } + } +} diff --git a/fixtures/hook-response/valid/hitl-resumption-allow.json b/fixtures/hook-response/valid/hitl-resumption-allow.json new file mode 100644 index 0000000..a2a44fc --- /dev/null +++ b/fixtures/hook-response/valid/hitl-resumption-allow.json @@ -0,0 +1,15 @@ +{ + "spec": "agent-hooks/0.1", + "event_id": "36c2b982-1d4c-4dc2-ae5b-a65139601741", + "decision": "allow", + "reason": "Approved by security administrator Alice.", + "extensions": { + "sec.enterprise.hitl": { + "challenge_id": "ch_slack_prod_99182", + "approval_grant_token": "agt_eyJhbGciOiJFZERTQ...", + "approved_by": "alice.security.lead@example.com", + "approved_at": 1773726500, + "bound_tool_hash": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + } +} diff --git a/rfcs/0006-enterprise-security-extensions-strategy.md b/rfcs/0006-enterprise-security-extensions-strategy.md index ed2d206..a7cc98f 100644 --- a/rfcs/0006-enterprise-security-extensions-strategy.md +++ b/rfcs/0006-enterprise-security-extensions-strategy.md @@ -115,10 +115,13 @@ Used to guarantee message authenticity and provenance between Agent, PEP (Relay) ```json { "spec": "agent-hooks/0.1", - "event_id": "01J8ABCDEF1234567890abcdef", + "event_id": "8f2ab3e1-4c5d-4e6f-8a9b-0c1d2e3f4a5b", "hook_event_name": "PreToolUse", "session_id": "sess_production_9981", "timestamp": "2026-09-17T02:30:00Z", + "sequence": 4, + "prompt_id": "prompt-9981", + "tool_use_id": "toolu-9981-bash", "tool_name": "bash", "tool_input": { "command": "uname -a" @@ -183,7 +186,7 @@ Standardizes asynchronous human intervention when a Policy Decision Point return ```json { "spec": "agent-hooks/0.1", - "event_id": "01J8ABCDEF1234567891abcdef", + "event_id": "36c2b982-1d4c-4dc2-ae5b-a65139601741", "decision": "ask", "reason": "Execution of bash shell with root privilege requires administrator sign-off.", "extensions": { @@ -198,12 +201,12 @@ Standardizes asynchronous human intervention when a Policy Decision Point return } ``` -##### Resumption Callback Request (Triggered by Slack/Teams Approval) -When approved by an authorized administrator, the enterprise PDP or callback service invokes the PEP resumption endpoint with an authorized grant token: +##### Correlated Resumption Response (Triggered by Slack/Teams Approval) +When human approval resolves out-of-band, the enterprise PDP or callback service delivers an asynchronous correlated response matching the original `event_id` (`36c2b982-1d4c-4dc2-ae5b-a65139601741`) to the host or PEP resumption endpoint: ```json { "spec": "agent-hooks/0.1", - "event_id": "01J8ABCDEF1234567892abcdef", + "event_id": "36c2b982-1d4c-4dc2-ae5b-a65139601741", "decision": "allow", "reason": "Approved by security administrator Alice.", "extensions": { @@ -299,7 +302,7 @@ This profile MAY be declared in the host's capability metadata or attached to ho * **`mode`** *(string, required)*: - `"strict_fail_closed"`: Any failure in `applicable_failures` for a covered Gate results in immediate operation denial (`decision: "deny"`) and aborts the pending turn. - - `"bounded_open"`: Allows up to `max_consecutive_failures` within `window_seconds`. If failures exceed the bound, the circuit trips to `on_exhausted` (default: `"fail_closed"`). + - `"bounded_open"`: Follows the deterministic circuit breaker state machine defined below. When failures reach `max_consecutive_failures`, the circuit trips to `on_exhausted`. Requires `bounded_open_policy`. - `"fail_open_monitored"`: Follows the Core 0.1 fail-open behavior, but generates high-priority security telemetry and audit events. * **`applicable_gates`** *(array of strings, optional)*: List of Core Gate names to which this enforcement applies. Defaults to all Gates declared by the host. * **`applicable_failures`** *(array of strings, required)*: @@ -308,13 +311,51 @@ This profile MAY be declared in the host's capability metadata or attached to ho - `"http_server_error"`: Webhook or proxy responds with HTTP 5xx status codes. - `"malformed_response"`: Response body fails JSON parsing, schema validation, or signature verification. - `"native_validation_failure"`: Schema-valid mutated payload (e.g. `updatedInput`, `updatedPrompt`) is rejected by the host runtime's native validation. -* **`bounded_open_policy`** *(object, optional)*: +* **`bounded_open_policy`** *(object, required when `mode` is `"bounded_open"`)*: - **`max_consecutive_failures`** *(integer, minimum: 1)*: Maximum allowed consecutive failures before tripping. - **`window_seconds`** *(integer, minimum: 1)*: Rolling evaluation window in seconds. - - **`cooldown_seconds`** *(integer, minimum: 1)*: Time period the circuit remains tripped before attempting half-open recovery. + - **`cooldown_seconds`** *(integer, minimum: 1)*: Time period the circuit remains tripped before entering `HALF_OPEN`. - **`on_exhausted`** *(string, enum: `["fail_closed", "require_interactive_approval"]`)*: Action to take once the bound is exhausted. * **`audit_alert`** *(boolean, optional, default: `true`)*: When `true`, emits an enterprise audit record or alert for every degraded event. +#### Deterministic Circuit Breaker State Machine + +When `mode` is `"bounded_open"`, the host or PEP MUST implement the circuit breaker as a deterministic finite-state machine (FSM) governed by the following rules: + +1. **Counter Scope & Keying**: + - State and failure counters MUST be isolated and keyed per **`[gate, handler_id]`** within the agent session (or `[host_id, gate, handler_id]` for multi-tenant gateways). A failure at one tool hook handler MUST NOT trip or affect another hook handler. +2. **Success & Reset Rule**: + - In state `CLOSED`: When a handler invocation succeeds (receives a valid correlated response within `timeout_ms`), the consecutive failure counter is immediately **reset to 0**, and any prior failures outside the rolling window are pruned. + - In state `HALF_OPEN`: A single successful probe invocation immediately **resets the counter to 0** and transitions the circuit back to `CLOSED`. +3. **Trip Point (Failure $N$)**: + - The circuit transitions from `CLOSED` to `TRIPPED` **on failure $N$**, where $N = \text{max\_consecutive\_failures}$ recorded within the trailing `window_seconds`. The $N$-th failing operation and all subsequent arrivals are subjected to `on_exhausted`. +4. **Rolling-Window Calculation**: + - The rolling window tracks failure timestamps $\{t_1, t_2, \dots\}$. Timestamps older than $(T_{\text{now}} - \text{window\_seconds})$ are pruned continuously. +5. **Half-Open Probing & Concurrent Arrival**: + - When the circuit is `TRIPPED` and $(T_{\text{now}} - T_{\text{trip}} \ge \text{cooldown\_seconds})$, the circuit transitions to `HALF_OPEN`. + - In `HALF_OPEN`, the host allows **exactly one (1) probe invocation** to be dispatched to the handler. + - **Concurrency behavior**: If concurrent operations arrive while a probe is in-flight, the host MUST NOT dispatch additional probes to the degraded handler; concurrent operations MUST immediately evaluate `on_exhausted`. + - If the probe succeeds: State transitions to `CLOSED`. + - If the probe fails: State transitions back to `TRIPPED`, resets $T_{\text{trip}} = T_{\text{now}}$, and applies `on_exhausted`. +6. **Persistence & Restart**: + - The state machine is maintained in-memory by default. Upon process restart, the circuit initializes to `CLOSED` unless persistent backing storage (e.g. Redis) is explicitly configured. +7. **Interactive Fallback**: + - If `on_exhausted` is `"require_interactive_approval"` but the host environment is non-interactive or headless, the host **MUST fallback to `fail_closed`** (`decision: "deny"`). + +##### State Transition Table + +| Current State | Event / Condition | Next State | Operation Gating Action | +| :--- | :--- | :--- | :--- | +| `CLOSED` | Handler invocation succeeds | `CLOSED` | Reset consecutive failure counter to 0; allow operation. | +| `CLOSED` | Handler failure; total failures $< N$ in window | `CLOSED` | Increment counter; fail-open (allow operation); emit audit alert. | +| `CLOSED` | Handler failure; total failures $= N$ in window | `TRIPPED` | Record trip time $T_{\text{trip}}$; apply `on_exhausted` (deny operation). | +| `TRIPPED` | New operation; $T_{\text{now}} - T_{\text{trip}} < \text{cooldown}$ | `TRIPPED` | Apply `on_exhausted` (deny operation); do not dispatch to handler. | +| `TRIPPED` | $T_{\text{now}} - T_{\text{trip}} \ge \text{cooldown}$ | `HALF_OPEN` | Transition to `HALF_OPEN`; prepare single probe. | +| `HALF_OPEN` | First operation arrives | `HALF_OPEN` | Dispatch single probe request to handler. | +| `HALF_OPEN` | Concurrent operation arrives while probe in flight | `HALF_OPEN` | Apply `on_exhausted` (deny operation); do not dispatch extra probe. | +| `HALF_OPEN` | Probe succeeds | `CLOSED` | Reset failure counter & window to 0; allow operation. | +| `HALF_OPEN` | Probe fails | `TRIPPED` | Reset $T_{\text{trip}} = T_{\text{now}}$; apply `on_exhausted` (deny operation). | + #### Precedence Rule against Core 0.1 When `sec.enterprise.degradation` is configured on a host or returned by an enterprise PDP, its rules **MUST take precedence** over Core 0.1 default fail-open behavior for all gates listed in `applicable_gates`. If an unlisted Gate fails, it falls back to the Core 0.1 baseline. @@ -347,21 +388,23 @@ Implementations MAY support administrative revocation through either: ``` 2. **Namespaced Extension Event**: On internal event buses, implementations MAY emit an extension event adhering to `spec/0.1/events.md`: - ```json - { - "spec": "agent-hooks/0.1", - "event_id": "01J8ABCDEF1234567893abcdef", - "hook_event_name": "x-nemo/SessionRevoke", - "session_id": "sess_production_9981", - "timestamp": "2026-09-17T02:35:00Z", - "extensions": { - "sec.enterprise.control": { - "action": "terminate", - "reason": "Administrative kill-switch invoked by SOC" - } - } - } - ``` + +```json +{ + "spec": "agent-hooks/0.1", + "event_id": "9f3bc4e2-5d6e-4f7a-9b0c-1d2e3f4a5b6c", + "hook_event_name": "x-nemo/SessionRevoke", + "session_id": "sess_production_9981", + "timestamp": "2026-09-17T02:35:00Z", + "sequence": 100, + "extensions": { + "sec.enterprise.control": { + "action": "terminate", + "reason": "Administrative kill-switch invoked by SOC" + } + } +} +``` Upon receiving a valid revocation command, the PEP/Host MUST: - Invalidate all active tokens and standing authorizations associated with `session_id`. @@ -505,15 +548,27 @@ The following JSON Schemas illustrate how implementations may validate extension "type": "object", "properties": { "max_consecutive_failures": { "type": "integer", "minimum": 1 }, - "failure_rate_threshold": { "type": "number", "minimum": 0, "maximum": 1 }, "window_seconds": { "type": "integer", "minimum": 1 }, "cooldown_seconds": { "type": "integer", "minimum": 1 }, "on_exhausted": { "type": "string", "enum": ["fail_closed", "require_interactive_approval"] } - } + }, + "required": ["max_consecutive_failures", "window_seconds", "cooldown_seconds", "on_exhausted"], + "additionalProperties": true }, "audit_alert": { "type": "boolean" } }, "required": ["mode", "applicable_failures"], + "allOf": [ + { + "if": { + "properties": { "mode": { "const": "bounded_open" } }, + "required": ["mode"] + }, + "then": { + "required": ["bounded_open_policy"] + } + } + ], "additionalProperties": true } ``` diff --git a/scripts/validate.mjs b/scripts/validate.mjs index cbe6cfb..5adbb78 100644 --- a/scripts/validate.mjs +++ b/scripts/validate.mjs @@ -120,6 +120,29 @@ async function validateSchemasAndFixtures() { } } } + + const markdownFiles = await filesUnder(root, (file) => file.endsWith('.md')); + for (const file of markdownFiles) { + const content = await readFile(file, 'utf8'); + const blocks = Array.from(content.matchAll(/```(?:json)\n([\s\S]*?)\n```/g)); + for (let i = 0; i < blocks.length; i++) { + let parsed; + try { + parsed = JSON.parse(blocks[i][1]); + } catch { + continue; + } + if (parsed && typeof parsed === 'object' && parsed.spec === 'agent-hooks/0.1') { + const isEvent = Boolean(parsed.hook_event_name); + const schemaName = isEvent ? 'hook-event' : 'hook-response'; + const validate = schemas.get(schemaName); + if (validate && !validate(parsed)) { + const details = ajv.errorsText(validate.errors, { separator: '; ' }); + errors.push(`${path.relative(root, file)} (JSON block ${i + 1}) failed validation against ${schemaName}.schema.json: ${details}`); + } + } + } + } } await validateMarkdownLinks(); From e6f304315f2e517e6e8fa6c410f0aae501806f58 Mon Sep 17 00:00:00 2001 From: brian_chuang Date: Thu, 17 Sep 2026 22:15:57 +0800 Subject: [PATCH 4/4] rfc: standardize degradation trust model, crypto preimages, and HITL grant contract --- ...enterprise-security-extensions-strategy.md | 82 ++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/rfcs/0006-enterprise-security-extensions-strategy.md b/rfcs/0006-enterprise-security-extensions-strategy.md index a7cc98f..97555d6 100644 --- a/rfcs/0006-enterprise-security-extensions-strategy.md +++ b/rfcs/0006-enterprise-security-extensions-strategy.md @@ -149,6 +149,27 @@ Alternatively, transport-level implementations MAY transport this metadata via H Hook-Signature: key_id="key_enclave_prod_01", alg="ed25519", sig="MEQCIE..." ``` +##### Canonicalization, Preimage, and Verification Semantics + +To ensure consistent interoperability across distinct runtime languages (Python, Go, TypeScript) and implementations without signature mismatch: + +1. **Canonicalization & Encoding**: Implementations MUST canonicalize payloads using **RFC 8785 (JSON Canonicalization Scheme - JCS)** and encode to UTF-8. +2. **Exact Signed Object & Exclusion**: + - The signing input is formed from the complete Agent Hook event (or response) document. + - The self-referential signature property (`extensions["sec.enterprise.crypto"].signature`) and `canonical_hash` (if present) MUST be excluded prior to canonicalization. +3. **Domain Separator & Preimage**: + - The preimage MUST be prefixed with a strict profile/version domain separation string: + `agent-hooks/0.1:sec.enterprise.crypto:v1\n` + - The complete byte sequence for signing and verification is: + $$\text{PREIMAGE\_BYTES} = \text{"agent-hooks/0.1:sec.enterprise.crypto:v1\n"} \,||\, \text{JCS}(\text{payload\_without\_sig})$$ + - `canonical_hash` is computed as `:` over $\text{PREIMAGE\_BYTES}$. +4. **Replay & Freshness Binding**: + - The payload MUST include a valid ISO-8601 `timestamp` and a unique UUID `event_id`. + - The receiver MUST assert that `timestamp` is within the allowable clock-skew window (recommended: $\pm 300\text{ seconds}$) and that `event_id` has not been observed within the active replay cache. +5. **Key ID Resolution & Verification Failure**: + - `key_id` is resolved against the host or PEP's authorized local keystore, JWKS, or PKI trust anchors. + - If `key_id` is missing, unknown, or revoked, or if cryptographic verification fails, the PEP/Host **MUST reject the event (`fail_closed` / `decision: "deny"`)** and emit a high-priority security alert. + --- #### 3.2 Profile: Tamper-Evident Audit Ledger (`sec.enterprise.audit`) @@ -176,6 +197,18 @@ Enables forensic verification of agent operation history using back-linked hash * **`hash_algorithm`** *(string, optional, default: `"sha256"`)*: Hash algorithm used (`"sha256"`, `"sha3-512"`, `"blake3"`). * **`tamper_evident_status`** *(string, optional)*: State evaluation by the verification point (`"verified"`, `"broken_chain"`, `"unverified"`). +##### Ledger Chain Lifecycle, Scope, and Anchors + +1. **Chain Scope & Isolation**: Hash chains MUST be scoped and isolated per `session_id`. A multi-agent or multi-session host MUST NOT interleave sequences across distinct sessions. +2. **Genesis Value**: For the first event in a session (`sequence: 0`), `prev_record_hash` MUST be set to 64 hexadecimal ASCII zeros (`"0000000000000000000000000000000000000000000000000000000000000000"`). +3. **Preimage Calculation**: + - `record_hash` is computed as: + $$\text{record\_hash} = \text{HASH}(\text{prev\_record\_hash} \,||\, \text{JCS}(\text{record\_body}))$$ + where `record_body` contains the canonicalized event payload excluding `record_hash` and `tamper_evident_status`. +4. **Verification Anchors & Session Rotation**: + - Upon `SessionEnd` (or sequence rollover), the terminal `record_hash` SHOULD be anchored to external immutable/append-only storage (e.g. WORM storage, Transparency Log, or signed ledger checkpoint). + - If verification detects a hash mismatch at any sequence step, `tamper_evident_status` is marked `"broken_chain"` and administrative audit alerts are triggered. + --- #### 3.3 Profile: Asynchronous HITL Suspension (`sec.enterprise.hitl`) @@ -226,6 +259,32 @@ When human approval resolves out-of-band, the enterprise PDP or callback service * **`approval_grant_token`** *(string, optional)*: Cryptographically signed single-use grant token. * **`expires_at`** *(integer, optional)*: Unix epoch timestamp indicating expiration of the approval challenge. +##### HITL Grant & Resumption Security Contract + +To ensure human authorization cannot be replayed, forged, or decoupled from the exact suspended operation: + +1. **Standardized Resumption Grant (Signed or Opaque with Introspection)**: + The `approval_grant_token` delivered upon resumption MUST represent cryptographically verifiable authorization via either: + - **Signed Grant (JWS/JWT)**: A compact JWS token (RFC 7515) signed by the authorized HITL Policy Decision Point. + - **Opaque Grant with Token Introspection**: An opaque reference string validated via an authenticated PDP token introspection endpoint (RFC 7662 style) returning the required claims. +2. **Mandatory Bound Claims**: + Both formats MUST bind the following claims: + - `iss` *(string, required)*: Identifier of the authorized HITL authority. + - `aud` *(string, required)*: Identifier of the target Agent host or PEP. + - `sub` *(string, required)*: The original suspended `event_id` (e.g. `"36c2b982-1d4c-4dc2-ae5b-a65139601741"`). + - `sid` *(string, required)*: The session identifier (`session_id`). + - `tool` *(string, required)*: The tool name being authorized (`tool_name`). + - `input_hash` *(string, required)*: Hex-encoded `SHA-256(RFC8785_JCS(tool_input))` representing the exact parameters displayed to and approved by the human. + - `exp` *(integer, required)*: Expiration timestamp in seconds since Unix epoch. + - `jti` *(string, required)*: Globally unique grant ID for atomic single-use tracking. + - `approver` *(string, optional)*: Identity of the approving operator (e.g. email or employee ID). +3. **Host Resumption Verification & Replay Protection**: + When the correlated resumption response arrives at the PEP: + - **Freshness Check**: Assert $(T_{\text{now}} \le \text{exp})$. If expired, reject resumption (`decision: "deny"`). + - **Context Binding**: Assert that `sub == event_id`, `sid == session_id`, and `tool == tool_name` match the currently suspended turn. + - **Content Integrity Binding**: Recompute `SHA-256(RFC8785_JCS(tool_input))` against the pending tool execution parameters and assert exact match with `input_hash`. If arguments were altered during turn suspension, the host MUST reject execution (`decision: "deny"`). + - **Atomic Single-Use**: The host MUST atomically verify and mark `jti` as consumed. Any duplicate arrival with the same `jti` MUST be rejected as a replay attack. + --- #### 3.4 Profile: TOCTOU Content Fingerprint (`sec.enterprise.integrity`) @@ -247,6 +306,13 @@ Defends against Time-of-Check to Time-of-Use (TOCTOU) payload swapping attacks b * **`content_identity`** *(string, required)*: Cryptographic hash of the serialized tool input arguments (`tool_input`). * **`enforce_toctou_pre_dispatch`** *(boolean, optional, default: `true`)*: Instructs the PEP/host to verify that the executed parameters match `content_identity` identically prior to invocation. +##### Canonical Serialization & Payload Rewrite Semantics + +1. **Canonical Serialization**: `content_identity` MUST be generated as `"sha256:"` concatenated with lowercase hex of `SHA-256(RFC8785_JCS(tool_input))`. +2. **Behavior After Payload Rewrite**: + - If an authorized `PreToolUse` hook handler legitimately modifies `tool_input` (e.g. parameter sanitization or credential injection), the rewriting handler **MUST recompute** and provide the updated `content_identity` alongside the mutated payload. + - If an unauthorized handler modifies parameters, or if the dispatched parameters do not match `content_identity` immediately prior to execution, the host PEP **MUST abort dispatch (`decision: "deny"`)** with `reason: "TOCTOU integrity violation: parameters modified post-authorization"`. + --- ### Extension Profile 5: Failure & Degradation Enforcement (`sec.enterprise.degradation`) @@ -265,7 +331,7 @@ To resolve this conflict without breaking Core 0.1 minimalism, this profile esta #### Recommended Payload Structure -This profile MAY be declared in the host's capability metadata or attached to hook responses to configure degradation rules per gate: +This profile is authoritative when declared in host or administrator preconfiguration, or dynamically provisioned via authenticated control-plane policy channels: ```json { @@ -356,9 +422,21 @@ When `mode` is `"bounded_open"`, the host or PEP MUST implement the circuit brea | `HALF_OPEN` | Probe succeeds | `CLOSED` | Reset failure counter & window to 0; allow operation. | | `HALF_OPEN` | Probe fails | `TRIPPED` | Reset $T_{\text{trip}} = T_{\text{now}}$; apply `on_exhausted` (deny operation). | +#### Trust Model & Policy Lifecycle + +1. **Authoritative Provisioning Channel & Authorized Issuer**: + - The authoritative degradation policy MUST be established by **Host / Administrator Preconfiguration** (e.g. local configuration files, environment variables, or host deployment manifests). + - In distributed deployments, the host MAY accept degradation policies provisioned dynamically by an authorized Policy Administration Point (PAP) or Policy Decision Point (PDP) via an authenticated control-plane channel (e.g. mTLS or cryptographically signed policy bundle). + - **Precedence & Security Invariant**: An ordinary, unauthenticated hook handler responding to tool or lifecycle events MUST NOT be permitted to downgrade or overwrite an administrator's degradation policy (e.g., a failing handler cannot unilaterally switch the host from `strict_fail_closed` to `fail_open_monitored`). Hook responses MAY only report policy state or request a degradation policy if the issuer is explicitly authenticated as possessing administrative policy authority. +2. **Bootstrap Behavior**: + - When an agent host boots with no preconfigured degradation policy and no cached policy from an authorized PAP, it defaults to the Core 0.1 baseline (fail-open for handler errors with standard error logging), unless booted in an `enterprise-strict` profile which defaults to `strict_fail_closed` for all mutating gates. +3. **Persistence, Expiry, Replacement, and Revocation**: + - Policies dynamically provisioned by an authorized PAP MAY declare `ttl_seconds` or `expires_at`. Upon expiration, the host evicts the cached policy and falls back to host bootstrap defaults. + - An administrator or authorized PAP MAY revoke or replace a degradation policy at any time via control-plane push or administrative event (`x-nemo/SessionRevoke`), which takes effect immediately for all subsequent gate evaluations. + #### Precedence Rule against Core 0.1 -When `sec.enterprise.degradation` is configured on a host or returned by an enterprise PDP, its rules **MUST take precedence** over Core 0.1 default fail-open behavior for all gates listed in `applicable_gates`. If an unlisted Gate fails, it falls back to the Core 0.1 baseline. +When `sec.enterprise.degradation` is configured on a host or provisioned by an authorized enterprise PDP, its rules **MUST take precedence** over Core 0.1 default fail-open behavior for all gates listed in `applicable_gates`. If an unlisted Gate fails, it falls back to the Core 0.1 baseline. ---