From 25cf2daacf927d48554fbc8f48e95a56f1d976f2 Mon Sep 17 00:00:00 2001 From: Bar Kaduri Date: Mon, 15 Jun 2026 11:09:53 +0300 Subject: [PATCH 1/3] Slim ACS-Core: relax MODIFY, system/ping, wrapped MCP to SHOULD/MAY Lowers the adoption floor for frameworks claiming ACS-Core conformance by relaxing three items within Core to conditional requirements, without removing them from Core or introducing new profiles. The hook taxonomy minimum, dispositions ALLOW/DENY/ASK/DEFER, SessionContext with chain hash and Intent, replay protection, baseline HMAC-SHA256 integrity, and decision-honoring behavior remain MUST. Changes: - Dispositions: ALLOW/DENY/ASK/DEFER stay MUST. MODIFY moves to SHOULD, with composition rules ([..]/specification.md#63-modify-composition-normative) still normative for implementers. Deployments that do not implement MODIFY declare so in the handshake; Guardians MUST NOT return MODIFY to such deployments (DENY with audit substitution). - Liveness `system/ping`: SHOULD-implement. MAY be omitted by deployments where both parties co-locate or rely on transport-level liveness. - Wrapped MCP `protocols/MCP/*`: SHOULD-implement by deployments that govern MCP tool calls. MAY be omitted by deployments that do not use MCP. Support is declared in the handshake. What stays mandatory in Core (the differentiation from a stateless single-call evaluator): SessionContext + chain_hash + Intent option, the 6-hook lifecycle minimum, four dispositions, replay protection, HMAC baseline integrity, and decision honoring. These preserve the runtime governance properties that distinguish ACS from a stateless policy evaluator. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/spec/conformance.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/spec/conformance.md b/docs/spec/conformance.md index 95ad9f7..9f8c4cd 100644 --- a/docs/spec/conformance.md +++ b/docs/spec/conformance.md @@ -17,13 +17,13 @@ A v0.1.0-conformant deployment MUST implement ACS-Core. ACS-Core comprises: - **Handshake** — `handshake/hello` with ClientHello/ServerHello ([Specification §4](./instrument/specification.md#4-capability-negotiation-handshake)). - **Request/response envelope** — JSON-RPC 2.0 with ACS extensions ([§3](./instrument/specification.md#3-wire-format)). `request_id`, `timestamp`, `acs_version`, `metadata` required on every request. - **Hook taxonomy** — At minimum: `sessionStart`, `userMessage` or `agentTrigger`, `toolCallRequest`, `toolCallResult`, `agentResponse`, `sessionEnd`. Additional hooks (`turnStart`/`turnEnd`, `preCompact`/`postCompact`, `subagentStart`/`subagentStop`, `knowledgeRetrieval`, `memoryContextRetrieval`, `memoryStore`, `skillRegister`/`skillLoad`/`skillUnload`) are normatively defined and SHOULD be implemented when the harness can observe the corresponding event; they are capability-negotiated via the handshake. A deployment whose AgBOM includes `skill` components SHOULD emit the skill lifecycle hooks, so a Guardian is not blind to a composition surface the inventory already exposes. -- **Dispositions** — All five (ALLOW, DENY, MODIFY, ASK, DEFER) with required fields per [§6](./instrument/specification.md#6-disposition-vocabulary). +- **Dispositions** — Four MUST-support: ALLOW, DENY, ASK, DEFER, with required fields per [§6](./instrument/specification.md#6-disposition-vocabulary). MODIFY is SHOULD-support: deployments that implement MODIFY MUST follow the MODIFY composition rules ([§6.3](./instrument/specification.md#63-modify-composition-normative)); deployments that do not implement MODIFY MUST advertise this in the handshake, and a Guardian targeting such a deployment MUST NOT return MODIFY (it substitutes DENY with an audit event recording the substitution). - **SessionContext and Intent**: `session_id`, `chain_hash` (rolling SHA-256), append-only ContextEntry chain, with the Guardian publishing the chain head (`chain_hash`) on responses for content-bearing steps ([§8](./instrument/specification.md#8-sessioncontext-and-intent)). Intent is optional but normative when IBAC is the enforcement paradigm. - **Replay protection** — `request_id` (UUID) and `timestamp` on every request; Guardians MUST reject replays per [§10.3](./instrument/specification.md#103-replay-protection). - **Baseline integrity**: every request and response carries a signature over the canonical envelope ([§10](./instrument/specification.md#10-cryptographic-signatures)). `HMAC-SHA256` with an HKDF-derived per-session key from deployment-provided key material is the baseline; asymmetric and post-quantum algorithms are the ACS-Crypto profile. - **Decision honoring**: the Observed Agent MUST wait for the Guardian's decision up to the negotiated timeout and apply it; on a decision failure (timeout, transport failure, or an error without a decision) it applies the `on_decision_failure` posture (default `proceed`, fail-open) and records every fail-open proceed as an audit event ([§6.4](./instrument/specification.md#64-honoring-decisions-normative)). Handshake failure follows the deployment's startup posture ([§4.1](./instrument/specification.md#41-handshake-failure-normative)). -- **Liveness** — `system/ping` ([§13](./instrument/specification.md#13-liveness-system-methods)). -- **Wrapped MCP** — `protocols/MCP/*` ([Hooks](./instrument/hooks.md#protocolsmcp)). +- **Liveness** — `system/ping` ([§13](./instrument/specification.md#13-liveness-system-methods)) SHOULD be implemented; MAY be omitted by deployments where both parties co-locate or rely on transport-level liveness signals. +- **Wrapped MCP** — `protocols/MCP/*` ([Hooks](./instrument/hooks.md#protocolsmcp)) SHOULD be implemented by deployments that govern MCP tool calls under the Guardian; MAY be omitted by deployments that do not use MCP. Support is declared in the handshake. ACS-Core does NOT require: field-level Provenance objects, Trace event emission, AgBOM, asymmetric or post-quantum signatures, or `request_hash` on ContextEntry (`request_hash` remains SHOULD). It DOES require the baseline signature (§10) and decision honoring (§6.4). ACS-Core deployments validate hook payloads against the base schemas, where `provenance` is OPTIONAL; field-level Provenance is added by the ACS-Provenance profile. From 3ec047f10cc8e78efd7d046c0c8ba5997f1e8335 Mon Sep 17 00:00:00 2001 From: Bar Kaduri Date: Mon, 15 Jun 2026 13:13:55 +0300 Subject: [PATCH 2/3] Promote subagentStart and subagentStop to mandatory in ACS-Core Adds subagent lifecycle hooks to the Core minimum (8 hooks instead of 6). Rationale (Ariel): a sub-agent is itself an Observed Agent under delegated authority. Without subagentStart/subagentStop on the Core floor, a Guardian is blind to cross-agent propagation and to the confused-deputy attack class that delegation enables. ACS already has the schemas, the AgBOM models subagent components, and the wire shape is identical to other steps -- the cost of mandating these is marginal, the cost of leaving them optional is a structural gap in runtime governance. Frameworks without a sub-agent abstraction satisfy this requirement vacuously: the hooks never fire, the framework still declares them as implemented in the handshake. Net Core hook count: 6 -> 8. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/spec/conformance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/conformance.md b/docs/spec/conformance.md index 9f8c4cd..11b0e86 100644 --- a/docs/spec/conformance.md +++ b/docs/spec/conformance.md @@ -16,7 +16,7 @@ A v0.1.0-conformant deployment MUST implement ACS-Core. ACS-Core comprises: - **Handshake** — `handshake/hello` with ClientHello/ServerHello ([Specification §4](./instrument/specification.md#4-capability-negotiation-handshake)). - **Request/response envelope** — JSON-RPC 2.0 with ACS extensions ([§3](./instrument/specification.md#3-wire-format)). `request_id`, `timestamp`, `acs_version`, `metadata` required on every request. -- **Hook taxonomy** — At minimum: `sessionStart`, `userMessage` or `agentTrigger`, `toolCallRequest`, `toolCallResult`, `agentResponse`, `sessionEnd`. Additional hooks (`turnStart`/`turnEnd`, `preCompact`/`postCompact`, `subagentStart`/`subagentStop`, `knowledgeRetrieval`, `memoryContextRetrieval`, `memoryStore`, `skillRegister`/`skillLoad`/`skillUnload`) are normatively defined and SHOULD be implemented when the harness can observe the corresponding event; they are capability-negotiated via the handshake. A deployment whose AgBOM includes `skill` components SHOULD emit the skill lifecycle hooks, so a Guardian is not blind to a composition surface the inventory already exposes. +- **Hook taxonomy** — At minimum: `sessionStart`, `userMessage` or `agentTrigger`, `toolCallRequest`, `toolCallResult`, `agentResponse`, `sessionEnd`, `subagentStart`, `subagentStop`. Subagent hooks are mandatory because a sub-agent is itself an Observed Agent under delegated authority; without them, a Guardian is blind to cross-agent propagation (the confused-deputy attack class). Frameworks that do not have a sub-agent abstraction satisfy this requirement vacuously (the hooks never fire). Additional hooks (`turnStart`/`turnEnd`, `preCompact`/`postCompact`, `knowledgeRetrieval`, `memoryContextRetrieval`, `memoryStore`, `skillRegister`/`skillLoad`/`skillUnload`) are normatively defined and SHOULD be implemented when the harness can observe the corresponding event; they are capability-negotiated via the handshake. A deployment whose AgBOM includes `skill` components SHOULD emit the skill lifecycle hooks, so a Guardian is not blind to a composition surface the inventory already exposes. - **Dispositions** — Four MUST-support: ALLOW, DENY, ASK, DEFER, with required fields per [§6](./instrument/specification.md#6-disposition-vocabulary). MODIFY is SHOULD-support: deployments that implement MODIFY MUST follow the MODIFY composition rules ([§6.3](./instrument/specification.md#63-modify-composition-normative)); deployments that do not implement MODIFY MUST advertise this in the handshake, and a Guardian targeting such a deployment MUST NOT return MODIFY (it substitutes DENY with an audit event recording the substitution). - **SessionContext and Intent**: `session_id`, `chain_hash` (rolling SHA-256), append-only ContextEntry chain, with the Guardian publishing the chain head (`chain_hash`) on responses for content-bearing steps ([§8](./instrument/specification.md#8-sessioncontext-and-intent)). Intent is optional but normative when IBAC is the enforcement paradigm. - **Replay protection** — `request_id` (UUID) and `timestamp` on every request; Guardians MUST reject replays per [§10.3](./instrument/specification.md#103-replay-protection). From c46f69513bebd2a6d0d0e151b1b73fa1624912c2 Mon Sep 17 00:00:00 2001 From: Bar Kaduri Date: Tue, 18 Aug 2026 17:08:44 +0300 Subject: [PATCH 3/3] Address Rock's PR #21 review: MODIFY declaration, subagent split, MCP fallback, liveness, harmonization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blocking / important items from Rock's review: - MODIFY declaration moves off-wire. New §6.5 (MODIFY-incapable clients) mirrors the §9.2 ASK precedent: Guardian determines client capability by deployment-defined means, substitutes DENY with reason_codes: ["modify_unsupported"] and an audit event. A client that receives an unapplicable MODIFY MUST treat it as DENY + audit event (Rock's item 1 and 2). - Subagent taxonomy split: subagentStart MUST-emit for subagent-capable frameworks (real gate, decision-eligible); subagentStop SHOULD-emit (audit-only). Vacuity determined by deployment-defined means, mirroring §9.2 (Rock's item 3, first half). - subagent-stop.json: final_chain_hash promoted to optional. Frameworks that maintain no session-chain MAY omit rather than fabricate; Guardian MUST treat omission as "chain not maintained" not integrity failure (Rock's item 3, second half). - Wrapped MCP MUST-implement for deployments whose sessions involve MCP at any point (closes the resources/read prompt-injection ingress gap). Mid-session MCP addition in a deployment that declared none is non-conformant until v0.2 ships renegotiation (Rock's item 4). - Liveness requires a declared mechanism: system/ping or a named transport-level alternative (TCP keepalive, HTTP/2 PING, process supervision, continuous observed hook traffic). Omitting system/ping without a declared alternative is non-conformant, so the startup_posture / on_decision_failure / no-liveness chain of defaults cannot produce silent fail-open (Rock's item 5). Cross-spec harmonization (called out during review): - §6.3 (malformed MODIFY): SHOULD record an audit event → MUST record an audit event. - §9.2 (approver-incapable): adds MUST record substitution as audit event, so substitution rate is machine-detectable rather than only reachable by grepping log prose. - All fallback/substitution rules now consistent across §6.3, §6.4, §6.5, and §9.2: MUST-DENY + MUST-audit. Cosmetic sweep: - conformance.md quick-ref table row for acs-core updated to reflect new baseline (subagentStart in taxonomy, four MUST-support dispositions + MODIFY SHOULD, declared liveness, Wrapped MCP conditional). - docs/acs.md ACS-Core description rewritten to match. - conformance.md guarantee paragraph updated: minimum-conformant deployment can permit or refuse but cannot alter; redaction and other content rewrites require MODIFY support. Versioning: - version.txt and pyproject.toml bumped 0.1.0 → 0.1.1 (Ariel + Rock both requested). - CHANGELOG.md created with the 0.1.1 entry. Related: Rock's item 6 (Guardian-side hook-coverage unfalsifiability) is out of scope for this PR; filed as GenAI-Security-Project/agent-control-standard#31 and linked from PR #21. Co-Authored-By: Claude Opus 4.7 Signed-off-by: Bar Kaduri --- CHANGELOG.md | 37 +++++++++++++++++++ docs/acs.md | 2 +- docs/spec/conformance.md | 12 +++--- docs/spec/instrument/specification.md | 16 +++++++- pyproject.toml | 2 +- specification/v0.1.0/hooks/subagent-stop.json | 4 +- version.txt | 2 +- 7 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..10d57df --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog + +All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) once it reaches 1.0.0. Pre-1.0 releases MAY change the ACS-Core baseline in patch versions; the wire format version (`acs_version` in the handshake) evolves separately in the `specification/` directory. + +## [0.1.1] — 2026-07-09 + +### Changed — ACS-Core baseline (relaxations) + +- `MODIFY` is now SHOULD-support rather than MUST-support. Deployments whose framework cannot mutate a request (shell-hook integrations, IDE plugins without an argument-mutation surface) or that disable `MODIFY` for auditability reasons are conformant without it. See new [§6.5 MODIFY-incapable clients](docs/spec/instrument/specification.md#65-modify-incapable-clients-normative). +- `subagentStop` is now SHOULD-emit rather than MUST-emit; `subagentStart` remains MUST-emit for subagent-capable frameworks. +- `system/ping` is now SHOULD-implement rather than MUST-implement, provided the deployment declares an alternative liveness mechanism (transport-level keepalive on HTTP; process supervision or heartbeat on stdio; continuous observed hook traffic). Omitting `system/ping` without a declared alternative is non-conformant. +- Wrapped MCP (`protocols/MCP/*`) MUST be implemented by deployments whose sessions involve MCP at any point (tools, resources, prompts, notifications), and MAY be omitted by deployments whose sessions never involve MCP. Deployments that declared no MCP at handshake MUST NOT spawn or register MCP servers mid-session; renegotiation is undefined in v0.1. + +### Changed — ACS-Core baseline (additions) + +- `subagentStart` is now part of the MUST-emit minimum hook set for subagent-capable frameworks. A client whose framework has no sub-agent abstraction has nothing to emit; the Guardian determines client subagent-emission capability by deployment-defined means (mirroring the [§9.2 ASK precedent](docs/spec/instrument/specification.md#92-approver-incapable-clients-normative)). + +### Added + +- **§6.5 MODIFY-incapable clients (normative)** in `docs/spec/instrument/specification.md`. Defines Guardian-side substitution (`DENY` with `reason_codes: ["modify_unsupported"]` + audit event) and client-side fallback (a client receiving an unapplicable `MODIFY` MUST treat it as `DENY` + audit event). +- Audit-event mandate in **§9.2** (approver-incapable clients): the Guardian MUST record the ASK-substitution as an audit event, so the substitution rate is machine-detectable rather than only reachable by grepping log prose. + +### Changed — normative force harmonization + +- **§6.3** (malformed MODIFY): `SHOULD record an audit event` → `MUST record an audit event`. All fallback / substitution rules now use consistent MUST-audit language across §6.3, §6.4, §6.5, and §9.2. + +### Changed — schema + +- `specification/v0.1.0/hooks/subagent-stop.json`: `final_chain_hash` moved from `required` to optional. Frameworks that maintain no session-chain (shell-hook integrations without an internal audit chain) MAY omit the field rather than fabricate a value; fabrication would corrupt the exact artifact the field exists to produce. A conformant Guardian MUST treat omission as "chain not maintained by this framework" rather than as an integrity failure. + +### Related issues + +- Guardian-side hook-coverage unfalsifiability tracked at [#31](https://github.com/GenAI-Security-Project/agent-control-standard/issues/31); out of scope for this release, needed for the enforcement side of `subagentStart`'s security rationale to be checkable. + +## [0.1.0] — 2026-06-05 + +- Initial canonical v0.1.0 spec integrated (see [#2](https://github.com/GenAI-Security-Project/agent-control-standard/pull/2)). diff --git a/docs/acs.md b/docs/acs.md index 0baff63..badfa7e 100644 --- a/docs/acs.md +++ b/docs/acs.md @@ -8,7 +8,7 @@ ACS extends existing standards rather than reinventing them: JSON-RPC 2.0 for th ## What v0.1.0 ships -- **ACS-Core** (mandatory baseline) — capability-negotiation handshake, JSON-RPC envelope, 16 native lifecycle hooks (`sessionStart`/`End`, `agentTrigger`, `userMessage`, `agentResponse`, `turnStart`/`End`, `toolCallRequest`/`Result`, `knowledgeRetrieval`, `memoryContextRetrieval`, `memoryStore`, `preCompact`/`postCompact`, `subagentStart`/`Stop`), wrapped MCP, five dispositions (`allow`, `deny`, `modify`, `ask`, `defer`), SessionContext with rolling SHA-256 chain hash, optional Intent with immutability rule, replay protection, and `system/ping` liveness. +- **ACS-Core** (mandatory baseline) — capability-negotiation handshake, JSON-RPC envelope, minimum hook set (`sessionStart`, `userMessage` or `agentTrigger`, `toolCallRequest`/`Result`, `agentResponse`, `sessionEnd`, `subagentStart` for subagent-capable frameworks), four MUST-support dispositions (`allow`, `deny`, `ask`, `defer`) with `modify` SHOULD-support, SessionContext with rolling SHA-256 chain hash, optional Intent with immutability rule, replay protection, baseline HMAC-SHA256 signature, decision honoring, declared liveness mechanism (`system/ping` or a transport-level alternative), and Wrapped MCP when sessions involve MCP. Additional hooks (`turnStart`/`End`, `preCompact`/`postCompact`, `subagentStop`, `knowledgeRetrieval`, `memoryContextRetrieval`, `memoryStore`, `skillRegister`/`Load`/`Unload`) are normatively defined and SHOULD be implemented when the harness can observe the corresponding event. - **ACS-Trace** profile — OpenTelemetry semconv mapping + OCSF event-class mapping, with decisions emitted as span events on the parent step span. - **ACS-Inspect** / **ACS-Inspect-Dynamic** profiles — canonical AgBOM with `agbom/snapshot` and `agbom/changed`, deterministic CycloneDX / SPDX / SWID derivations. - **ACS-Provenance** profile — field-level `Provenance` objects with `origin`, `source_id`, `derived_from`, and an OPTIONAL wire-format `trust` enum that obeys the monotonicity rule. diff --git a/docs/spec/conformance.md b/docs/spec/conformance.md index 11b0e86..bb83007 100644 --- a/docs/spec/conformance.md +++ b/docs/spec/conformance.md @@ -16,18 +16,18 @@ A v0.1.0-conformant deployment MUST implement ACS-Core. ACS-Core comprises: - **Handshake** — `handshake/hello` with ClientHello/ServerHello ([Specification §4](./instrument/specification.md#4-capability-negotiation-handshake)). - **Request/response envelope** — JSON-RPC 2.0 with ACS extensions ([§3](./instrument/specification.md#3-wire-format)). `request_id`, `timestamp`, `acs_version`, `metadata` required on every request. -- **Hook taxonomy** — At minimum: `sessionStart`, `userMessage` or `agentTrigger`, `toolCallRequest`, `toolCallResult`, `agentResponse`, `sessionEnd`, `subagentStart`, `subagentStop`. Subagent hooks are mandatory because a sub-agent is itself an Observed Agent under delegated authority; without them, a Guardian is blind to cross-agent propagation (the confused-deputy attack class). Frameworks that do not have a sub-agent abstraction satisfy this requirement vacuously (the hooks never fire). Additional hooks (`turnStart`/`turnEnd`, `preCompact`/`postCompact`, `knowledgeRetrieval`, `memoryContextRetrieval`, `memoryStore`, `skillRegister`/`skillLoad`/`skillUnload`) are normatively defined and SHOULD be implemented when the harness can observe the corresponding event; they are capability-negotiated via the handshake. A deployment whose AgBOM includes `skill` components SHOULD emit the skill lifecycle hooks, so a Guardian is not blind to a composition surface the inventory already exposes. -- **Dispositions** — Four MUST-support: ALLOW, DENY, ASK, DEFER, with required fields per [§6](./instrument/specification.md#6-disposition-vocabulary). MODIFY is SHOULD-support: deployments that implement MODIFY MUST follow the MODIFY composition rules ([§6.3](./instrument/specification.md#63-modify-composition-normative)); deployments that do not implement MODIFY MUST advertise this in the handshake, and a Guardian targeting such a deployment MUST NOT return MODIFY (it substitutes DENY with an audit event recording the substitution). +- **Hook taxonomy** — At minimum: `sessionStart`, `userMessage` or `agentTrigger`, `toolCallRequest`, `toolCallResult`, `agentResponse`, `sessionEnd`, `subagentStart`. A subagent-capable client MUST emit `subagentStart` when it spawns a sub-agent; a client whose framework has no sub-agent abstraction has nothing to emit. The Guardian determines client subagent-emission capability by deployment-defined means (agent identity bound at handshake, policy keyed on `agent_id`, organizational configuration), mirroring the ASK precedent in [§9.2](./instrument/specification.md#92-approver-incapable-clients-normative). Without `subagentStart`, a Guardian is blind to cross-agent propagation (the confused-deputy attack class). `subagentStop` is SHOULD-emit (audit-only, not decision-eligible; its `final_chain_hash` field is optional for frameworks maintaining no session-chain — see [`subagent-stop.json`](https://github.com/afogel/ACS_official/blob/dev/specification/v0.1.0/hooks/subagent-stop.json)). Additional hooks (`turnStart`/`turnEnd`, `preCompact`/`postCompact`, `knowledgeRetrieval`, `memoryContextRetrieval`, `memoryStore`, `skillRegister`/`skillLoad`/`skillUnload`) are normatively defined and SHOULD be implemented when the harness can observe the corresponding event; they are capability-negotiated via the handshake. A deployment whose AgBOM includes `skill` components SHOULD emit the skill lifecycle hooks, so a Guardian is not blind to a composition surface the inventory already exposes. +- **Dispositions** — Four MUST-support: ALLOW, DENY, ASK, DEFER, with required fields per [§6](./instrument/specification.md#6-disposition-vocabulary). MODIFY is SHOULD-support: deployments that implement MODIFY MUST follow the MODIFY composition rules ([§6.3](./instrument/specification.md#63-modify-composition-normative)); deployments that do not implement MODIFY are handled per [§6.5](./instrument/specification.md#65-modify-incapable-clients-normative) (the Guardian substitutes `DENY` with `reason_codes: ["modify_unsupported"]` and an audit event recording the substitution, determined by deployment-defined means rather than a wire declaration, mirroring the §9.2 precedent for ASK). - **SessionContext and Intent**: `session_id`, `chain_hash` (rolling SHA-256), append-only ContextEntry chain, with the Guardian publishing the chain head (`chain_hash`) on responses for content-bearing steps ([§8](./instrument/specification.md#8-sessioncontext-and-intent)). Intent is optional but normative when IBAC is the enforcement paradigm. - **Replay protection** — `request_id` (UUID) and `timestamp` on every request; Guardians MUST reject replays per [§10.3](./instrument/specification.md#103-replay-protection). - **Baseline integrity**: every request and response carries a signature over the canonical envelope ([§10](./instrument/specification.md#10-cryptographic-signatures)). `HMAC-SHA256` with an HKDF-derived per-session key from deployment-provided key material is the baseline; asymmetric and post-quantum algorithms are the ACS-Crypto profile. - **Decision honoring**: the Observed Agent MUST wait for the Guardian's decision up to the negotiated timeout and apply it; on a decision failure (timeout, transport failure, or an error without a decision) it applies the `on_decision_failure` posture (default `proceed`, fail-open) and records every fail-open proceed as an audit event ([§6.4](./instrument/specification.md#64-honoring-decisions-normative)). Handshake failure follows the deployment's startup posture ([§4.1](./instrument/specification.md#41-handshake-failure-normative)). -- **Liveness** — `system/ping` ([§13](./instrument/specification.md#13-liveness-system-methods)) SHOULD be implemented; MAY be omitted by deployments where both parties co-locate or rely on transport-level liveness signals. -- **Wrapped MCP** — `protocols/MCP/*` ([Hooks](./instrument/hooks.md#protocolsmcp)) SHOULD be implemented by deployments that govern MCP tool calls under the Guardian; MAY be omitted by deployments that do not use MCP. Support is declared in the handshake. +- **Liveness** — `system/ping` ([§13](./instrument/specification.md#13-liveness-system-methods)) SHOULD be implemented. A deployment MAY omit `system/ping` only if it has declared an alternative liveness mechanism in its configuration (for example: TCP keepalive or HTTP/2 PING on HTTP transports; process supervision or an out-of-band heartbeat on stdio; or continuous observed hook traffic where the session's own cadence acts as the probe). Omitting `system/ping` without a declared alternative is non-conformant: the chain of defaults (`startup_posture: proceed`, `on_decision_failure: proceed`, no liveness) produces silent fail-open on Guardian outage, which the Core baseline cannot allow. +- **Wrapped MCP** — `protocols/MCP/*` ([Hooks](./instrument/hooks.md#protocolsmcp)) MUST be implemented by deployments whose sessions involve MCP at any point (tools, resources, prompts, notifications), so that `protocols/MCP/resources/read` — the canonical prompt-injection ingress for attacker-controlled external content — is not bypassed. Support is declared in the handshake via `wrapped_protocols`. Deployments that declared no MCP at handshake MUST NOT spawn or register MCP servers mid-session; renegotiation is undefined in v0.1, so mid-session MCP addition is non-conformant until v0.2 ships it. Deployments whose sessions never involve MCP MAY omit the namespace. ACS-Core does NOT require: field-level Provenance objects, Trace event emission, AgBOM, asymmetric or post-quantum signatures, or `request_hash` on ContextEntry (`request_hash` remains SHOULD). It DOES require the baseline signature (§10) and decision honoring (§6.4). ACS-Core deployments validate hook payloads against the base schemas, where `provenance` is OPTIONAL; field-level Provenance is added by the ACS-Provenance profile. -What "ACS-Core conformant" guarantees: the channel is authenticated and the Observed Agent honors the Guardian's decisions. It does NOT assert that a deployment's policies are strict, nor that the audit chain is tamper-evident against a compromised Guardian (that is the ACS-Crypto and ACS-Audit profiles, since the HMAC baseline is symmetric). A permissive Guardian is a conformant but permissive deployment, not a violation. +What "ACS-Core conformant" guarantees: the channel is authenticated and the Observed Agent honors the Guardian's decisions on the four MUST-support dispositions (ALLOW, DENY, ASK, DEFER). Deployments that also implement MODIFY (SHOULD-support, [§6.5](./instrument/specification.md#65-modify-incapable-clients-normative)) honor it too; deployments that do not receive `DENY` substitutions with `reason_code: modify_unsupported`. A minimum-conformant deployment can therefore permit or refuse an action but cannot alter it — redaction and other content rewrites require MODIFY support. This clause does NOT assert that a deployment's policies are strict, nor that the audit chain is tamper-evident against a compromised Guardian (that is the ACS-Crypto and ACS-Audit profiles, since the HMAC baseline is symmetric). A permissive Guardian is a conformant but permissive deployment, not a violation. ## ACS-Trace @@ -80,7 +80,7 @@ Profiles compose. A deployment that wants full observability and supply-chain in | Profile | What it adds | When to claim | |---|---|---| -| `acs-core` | Handshake, envelope, hook taxonomy, dispositions, SessionContext + published chain head, replay protection, baseline signature (HMAC-SHA256), decision honoring, ping | Always (mandatory) | +| `acs-core` | Handshake, envelope, minimum hook set (incl. `subagentStart` for subagent-capable clients), four MUST-support dispositions (ALLOW/DENY/ASK/DEFER) with MODIFY SHOULD-support (§6.5), SessionContext + published chain head, replay protection, baseline signature (HMAC-SHA256), decision honoring, declared liveness mechanism, Wrapped MCP when sessions involve MCP | Always (mandatory) | | `acs-trace` | OTel + OCSF event emission per step | Cross-vendor observability or SIEM integration | | `acs-inspect` | `agbom/snapshot` + canonical AgBOM serialization | Policy depends on component inventory | | `acs-inspect-dynamic` | `agbom/changed` on mutation | Agent hot-swaps components mid-session | diff --git a/docs/spec/instrument/specification.md b/docs/spec/instrument/specification.md index 92f6ee3..535fd42 100644 --- a/docs/spec/instrument/specification.md +++ b/docs/spec/instrument/specification.md @@ -109,7 +109,7 @@ Inspect-pillar methods (`agbom/*`): `agbom/snapshot` and `agbom/changed` (see [I |---|---|---| | `ALLOW` | Proceed | none (`reasoning` RECOMMENDED when user-visible audit trails are expected) | | `DENY` | Block | `reasoning` | -| `MODIFY` | Proceed with changes (covers redaction via `modifications.redactions`; composition rules in [§6.3](#63-modify-composition-normative)) | `reasoning`, `modifications` | +| `MODIFY` | Proceed with changes (covers redaction via `modifications.redactions`; composition rules in [§6.3](#63-modify-composition-normative); substituted with `DENY` for MODIFY-incapable clients, see [§6.5](#65-modify-incapable-clients-normative)) | `reasoning`, `modifications` | | `ASK` | Pause and request approval (substituted with `DEFER` or `DENY` for approver-incapable clients; see [§9.2](#92-approver-incapable-clients-normative)) | `reasoning`, `ask_details` | | `DEFER` | Verdict not yet reachable | `reasoning`, `defer_details` | @@ -143,7 +143,7 @@ These fields support the v0.1 paradigm targets (FIDES, CaMeL, AARM-style cumulat - **Wholesale replacement.** `modified_content` replaces the entire payload. It is exclusive: a MODIFY that carries `modified_content` MUST NOT also carry `redactions` or `parameter_overrides`, because path-addressed edits have nothing to address inside an opaque replacement string. - **Structured edits.** `redactions` and `parameter_overrides` MAY appear together, but their targets MUST be disjoint: no `redactions` path may address the same field as a `parameter_overrides` key, nor an ancestor or descendant of it. Disjoint edits commute, so the effective payload is well-defined with no apply-order rule. -A Guardian MUST NOT emit a `modifications` object that violates either rule. An Observed Agent that receives one cannot determine the Guardian's intent and MUST fail closed, treating the decision as `DENY`, and SHOULD record an audit event. +A Guardian MUST NOT emit a `modifications` object that violates either rule. An Observed Agent that receives one cannot determine the Guardian's intent and MUST fail closed, treating the decision as `DENY`, and MUST record an audit event so the enforcement gap is visible. The disjoint-target rule is deliberately narrower than a precedence rule. A fixed apply-order would force every overlap to silently pick a winner, and either order has a failure mode: applying overrides last can re-expose a field a redaction just removed, and applying redactions last lets arbitrary replacement text overwrite a value an override deliberately sanitized. Requiring disjoint targets removes the conflict rather than resolving it by an order the Guardian cannot observe. @@ -157,6 +157,16 @@ A step suffers a **decision failure** when no usable decision arrives within the Every step that proceeds without a decision MUST be recorded as an audit event, so the bypass is visible rather than silent. When a decision does arrive within the timeout, the agent MUST honor it regardless of the posture. Fail-open trades enforcement for availability under disruption: an adversary who can disrupt the channel converts control into audit. Deployments for which that trade is unacceptable set `on_decision_failure: deny`. +### 6.5 MODIFY-incapable clients (normative) + +Some Observed Agents cannot apply a `MODIFY` disposition. This covers both framework constraints (shell-hook integrations whose hook API returns allow/deny only, IDE plugins without a mid-flight argument-mutation surface) and deployment preferences (organizations that disable MODIFY for auditability, so the executed action always equals the requested action). The Guardian determines client MODIFY-handling capability by deployment-defined means such as agent identity bound at handshake, policy keyed on `agent_id`, organizational configuration, or any other out-of-band signal the deployment trusts. ACS does not put this declaration on the wire in v0.1; it is part of the Guardian's policy bundle, mirroring the ASK precedent in [§9.2](#92-approver-incapable-clients-normative). + +When the Guardian determines that the client cannot apply `MODIFY`, the Guardian MUST NOT return `MODIFY`. The Guardian MUST instead substitute `DENY` with `reason_codes: ["modify_unsupported"]` and `reasoning` that names the intended modification, and MUST record the substitution as an audit event so the enforcement gap is visible. + +If a client receives a `MODIFY` it cannot apply — from a Guardian that misjudged capability, or a Guardian that did not consult the declaration — the client MUST treat the decision as `DENY` and MUST record an audit event with `reason_codes: ["modify_unsupported"]`. Proceeding with the original payload is non-conformant: a Guardian that intended to redact a secret out of a tool argument would otherwise get the unredacted secret shipped while the audit log recorded a modification that never happened. + +This rule preserves the security guarantee (actions that would have been rewritten by the Guardian are not silently allowed with the unmodified payload) while letting clients whose framework cannot mutate requests, or deployments that choose strict allow/deny for auditability, participate in ACS sessions as fully conformant ACS-Core deployments. + ## 7. Provenance The Provenance concept and its fields are defined in [Concepts › Provenance](../../concepts/provenance.md) (normative). This section specifies the wire shape and the v0.1 trust-classification stance. @@ -279,6 +289,8 @@ When the Guardian determines that the client cannot resolve `ASK`, the Guardian 1. `DEFER` with `timeout_decision: "deny"`: when the underlying issue might resolve through retry, an out-of-band escalation, or a later state change. The deferred verdict still counts toward cascading-deferral limits (§6). 2. `DENY` with `reason_codes: ["approver_unavailable"]` and `reasoning` that names the missing capability: when no recovery path exists. +In either case, the Guardian MUST record the substitution as an audit event so the enforcement gap is visible. Without a recorded substitution the substitution rate is only reachable by grepping log prose, which defeats the one metric that catches a client misdeclaring its capability. + The choice is policy-driven: deployments SHOULD prefer `DEFER` when the request is potentially recoverable through a different surface, and `DENY` when the action is unconditionally outside the client's reachable authority. This rule preserves the security guarantee (actions that would have been `ASK`'d in an approver-capable deployment are not silently allowed) while letting clients without approver UX participate in ACS sessions as fully conformant ACS-Core deployments. diff --git a/pyproject.toml b/pyproject.toml index 02eac05..36af930 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "acs" -version = "0.1.0" +version = "0.1.1" description = "ACS Documentation" requires-python = ">=3.8" dependencies = [ "mike>=1.2.0", "mkdocs-material>=9.6.14", "pymdown-extensions>=10.0.0",] diff --git a/specification/v0.1.0/hooks/subagent-stop.json b/specification/v0.1.0/hooks/subagent-stop.json index e4dc1b7..c7a4940 100644 --- a/specification/v0.1.0/hooks/subagent-stop.json +++ b/specification/v0.1.0/hooks/subagent-stop.json @@ -4,7 +4,7 @@ "title": "steps/subagentStop payload", "description": "Fires when an in-process subagent terminates. The envelope's metadata.session_id is the PARENT's session (the parent receives the subagent-stop notification on its own audit chain); the subagent's own session has by this point already emitted its sessionEnd with its own final_chain_hash. This hook lets the parent's audit chain reference the subagent's terminal state without merging the two chains. Not decision-eligible — the subagent has already terminated.", "type": "object", - "required": ["subagent_session_id", "outcome", "final_chain_hash"], + "required": ["subagent_session_id", "outcome"], "properties": { "subagent_session_id": { "type": "string", @@ -19,7 +19,7 @@ "final_chain_hash": { "type": "string", "pattern": "^[0-9a-f]{64}$", - "description": "The subagent's final SessionContext chain_hash at sessionEnd. Lets parent-side audit replay verify the subagent's chain integrity without the parent maintaining a copy of it." + "description": "OPTIONAL. The subagent's final SessionContext chain_hash at sessionEnd. When present, lets parent-side audit replay verify the subagent's chain integrity without the parent maintaining a copy of it. Frameworks that maintain no session-chain (e.g., shell-hook integrations without an internal audit chain) MAY omit this field rather than fabricate a value — fabrication would corrupt the exact artifact the field exists to produce. A conformant Guardian MUST treat omission as 'chain not maintained by this framework' rather than as an integrity failure." }, "summary": { "type": "object", diff --git a/version.txt b/version.txt index 6c6aa7c..6da28dd 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.1.0 \ No newline at end of file +0.1.1 \ No newline at end of file