Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions fixtures/hook-event/valid/pre-tool-use-wire-signing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"spec": "agent-hooks/0.1",
"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"
},
"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..."
}
}
}
14 changes: 14 additions & 0 deletions fixtures/hook-event/valid/vendor-session-revoke.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
15 changes: 15 additions & 0 deletions fixtures/hook-response/valid/ask-with-hitl-suspension.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
31 changes: 31 additions & 0 deletions fixtures/hook-response/valid/bounded-open-degradation-policy.json
Original file line number Diff line number Diff line change
@@ -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
}
}
}
24 changes: 24 additions & 0 deletions fixtures/hook-response/valid/fail-closed-degradation-policy.json
Original file line number Diff line number Diff line change
@@ -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
}
}
}
15 changes: 15 additions & 0 deletions fixtures/hook-response/valid/hitl-resumption-allow.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
670 changes: 670 additions & 0 deletions rfcs/0006-enterprise-security-extensions-strategy.md

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions rfcs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ 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, 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).

23 changes: 23 additions & 0 deletions scripts/validate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading