Skip to content

docs(spec): reconcile ACS-Core signature requirement with schema optionality in §10 - #339

Open
drk-ram wants to merge 1 commit into
GenAI-Security-Project:integrationfrom
drk-ram:spec/signature-requirement-195
Open

drk-ram wants to merge 1 commit into
GenAI-Security-Project:integrationfrom
drk-ram:spec/signature-requirement-195

Conversation

@drk-ram

@drk-ram drk-ram commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

Removes the contradictory "signatures are already field-optional" phrase from docs/spec/instrument/specification.md (§10.1), aligns §10, docs/spec/conformance.md, docs/topics/core_concepts.md, specification/v0.1.0/request-envelope.json, and specification/v0.1.0/handshake.json (ServerHello.signature) on ACS-Core signature requirements and schema optionality, and adds a regression guard in tests/test_response_envelope.py.

Which issue does this implement

Fixes #195

  • This is an editorial correction (typo, grammar, link, formatting) with no change in
    meaning, so it needs no issue

Base branch

  • integration, because this touches the specification, schemas, a reference
    implementation, an adapter, tests, or CI
  • main, because every changed path is on the documentation lane allowlist in
    CONTRIBUTING.md

Type of change

  • Specification change (schema, hooks, events, AgBOM)
  • Reference implementation or adapter
  • Documentation
  • Tooling or CI
  • Governance (licensing, security policy, contributor docs)

Specification changes

  • I opened a Discussion before this PR
  • Schema changes validate against the JSON Schema spec
  • I described the impact on downstream implementers below

Breaking for implementers? No. Keeps signature optional in the JSON Schema required lists while adding signature ($ref to Signature) on ServerHello and removing the prose contradiction between §10, §10.1, conformance.md, core_concepts.md, and the envelope schemas.

I tested this

  • I synced my branch with the base branch before opening this
  • uv run pytest -v passes on my machine
  • uv run mkdocs build --strict passes on my machine

Checklist

  • Commits are signed off with git commit -s (required by the DCO)
  • Prose follows STYLE.md
  • No secrets, tokens, or internal URLs in the diff

Security

  • This change has no security impact

@github-actions

Copy link
Copy Markdown

This pull request's description says it closes #195, which is on the project roadmap. When it merges, GitHub closes that issue and the roadmap counts it as delivered. Please confirm the change delivers the whole issue before merging.

@rocklambros rocklambros left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raghuvar, you took the second option issue #195 offers and removed "field-optional" from §10.1. Your schema edit changes only a description, so the wire shape stays the same (I compared both files). The merged tree on integration 19b1197 passes every suite, with the same counts plus your one new test.

@bar-capsule @afogel, this PR waits on item 6 on issue #32. Bar's issue #308 text and this PR agree on "every response is signed" and on the unsigned error for an unknown key_id. The schema stays optional, so an unsigned step still validates and only a verifier catches it. I ran this PR beside PR #229, PR #22, PR #169, PR #183 and PR #352, and your answer needs to cover three more things:

  1. Is the error for a request that fails verification signed? This PR requires it when the key_id is known, because the key can be resolved, and Bar's text names only the unknown key_id as unsigned. The Guardians split. PR #229, PR #169 and PR #183 send an unsigned -32004 (I sent each one a changed payload), and PR #229 gives its reason at server.ts:1568: signing would make the Guardian a signing oracle. PR #22 signs it, even for an unknown key_id. There, a sender with no key got back a valid signature over a session id and an id that the sender chose.

    So this PR's exemption widens to "requests that did not verify", or those three Guardians change. PR #229's host blocked the tampered step under proceed, so the step stops either way. Your answer decides what a conformant Guardian sends.

  2. Does ServerHello get a signature field too? Item 6 already asks about error.signature. §10 here says signature is optional in response-envelope.json, but only AcsResult defines it. An error.signature of 42 and a ServerHello signature of 42 both pass the schema. PR #229 signs ServerHello anyway.

  3. Should this PR drop its new MUST, and should it wait for the issue #32 text? PR #352 rule 3 has the same rule and the same exemption words. The two PRs merge with no conflict, so Git won't warn us, and §10 then has the list twice. Bar, on issue #316 you agreed the missing-signature rule for PR #352 and left the §10.1 sentence to this PR.

    This PR also tells a client that some unsigned errors are legitimate, while §6.4 still sends an error to proceed by default. You wrote that your issue #308 text should land with issue #32. Does this PR land with it?

Once you answer, Raghuvar can push and one of you can approve and merge. That closes issue #195 and lets PR #342 go next.

Raghuvar, please wait for that answer, so you only push once. I applied all four asks to a scratch copy, and pytest and mkdocs build --strict pass.

  1. docs/spec/conformance.md:36 still says every request and response is signed, and issue #195 names this line. It now disagrees with your §10. Change the sentence to:

    every request and response carries a signature over the canonical envelope, except the envelopes [§10](./instrument/specification.md#10-cryptographic-signatures) exempts.
    
  2. specification.md needs two changes. At line 350, your sentence about the base JSON Schemas runs 45 words (STYLE.md asks for 15 to 20). It also says the optional field is why exempted envelopes validate, and that isn't true for errors, because errors have no such field. Replace it with the text below. If Bar or Ariel drop the MUST, drop the second sentence.

    `signature` is optional in the base JSON Schemas, so schema validation alone does not establish ACS-Core baseline integrity. A conformant verifier MUST enforce signature presence and verification on every non-exempt envelope.
    

    At line 360, your parenthetical says "only for exempted control traffic such as system/ping", but §10 also exempts error responses. Please delete the parenthetical.

  3. Other lines still call signatures an optional add-on. In specification.md:7, conformance.md:3 and core_concepts.md:13, change "cryptographic signatures" to "asymmetric or post-quantum signatures", which is what conformance.md:41 says. Change the first sentence of conformance.md:80 to "Adds asymmetric and post-quantum signature support beyond the HMAC-SHA256 baseline." Leave handshake.json:103 alone, because PR #352 fixes it.

  4. Your test at tests/test_spec_structure.py:57 reads no conformance.md text and no schema shape. It still passed when I changed REQUIRED to RECOMMENDED, and when I made conformance.md say a signature is optional. PR #342 follows this PR, and both add a test at the end of that file, so they conflict there.

    Moving the test fixes both problems. Delete it from tests/test_spec_structure.py, and add this version at the end of tests/test_response_envelope.py, which already has json and SPEC. Both breaks above then fail, and PR #342 merges with no conflict (I ran it: 926 collected, 925 passed, 1 skipped). If Bar or Ariel change the words in §10, change these phrases to match.

    def test_signature_requirement_and_schema_optionality_are_consistent():
        """Issue #195: section 10 states the rule, and the schemas and conformance.md agree with it."""
        root = SPEC.parents[1]
        spec_text = (root / "docs/spec/instrument/specification.md").read_text(encoding="utf-8")
        conformance = (root / "docs/spec/conformance.md").read_text(encoding="utf-8")
        concepts = (root / "docs/topics/core_concepts.md").read_text(encoding="utf-8")
        req = json.loads((SPEC / "request-envelope.json").read_text(encoding="utf-8"))
        resp = json.loads((SPEC / "response-envelope.json").read_text(encoding="utf-8"))
    
        req_sig = req["$defs"]["AcsParams"]["properties"]["signature"]
        resp_sig = resp["$defs"]["AcsResult"]["properties"]["signature"]
        assert req_sig["$ref"] == "#/$defs/Signature"
        assert "signature" not in req["$defs"]["AcsParams"]["required"]
        assert "signature" not in resp["$defs"]["AcsResult"]["required"]
        for description in (req_sig["description"], resp_sig["description"]):
            assert "Optional in the schema" in description
            assert "mandatory under ACS-Core" in description
    
        section_10 = spec_text.split("## 10. Cryptographic Signatures")[1].split("### 10.2")[0]
        assert "is REQUIRED in ACS-Core for every request and response except" in section_10
        assert "optional in the base JSON Schemas" in section_10
        assert "MUST enforce signature presence and verification" in section_10
        assert "control traffic" not in section_10
        assert "field-optional" not in spec_text
        assert "except the envelopes [§10]" in conformance
        for text in (spec_text, conformance, concepts):
            assert "cryptographic signatures" not in text

Issue #195 has no assignee yet, because GitHub still won't let me assign you. It's the same step as on your other PRs, so please leave a comment on the issue. After you push, I'll run all of this again.

@bar-capsule

Copy link
Copy Markdown
Collaborator

@rocklambros @drk-ram, item 6 from #32, answered here since this PR implements it.

Every request and every response is signed, errors and the ServerHello included. Besides system/ping, the one unsigned envelope is the SIGNATURE_INVALID for an unknown key_id (§10), and the client blocks it, as it blocks any unsigned error (#32).

  1. Yes, the Guardian signs the error for a request that failed verification when it can resolve the key. A sender without the secret can only get signed errors this way, and every one of them blocks the step, so it gains nothing it couldn't get by dropping traffic. fix(agt): implement ACS-Core wire safeguards #229, Add a Go Guardian reference implementation #169 and Add a Python Guardian reference implementation #183 sign that -32004 to match; Add adapters/ with Claude Code, Cursor, and NAT reference implementations #22 already does.
  2. Yes. The ServerHello gets the same signature field ($ref to Signature) in handshake.json, here in this PR. error.signature in response-envelope.json comes with the [Bug] §6.4 conflates Guardian refusal with decision failure; refusals are attacker-reachable and should fail closed #32 PR.
  3. Drop the new MUST. docs: tighten section 10 verifier rules #352 carries that rule (its rule 3), as we agreed on [Proposal] Verifier rules: constant-time comparison, no unsigned envelopes, only negotiated algorithms #316, so §10 states it once. This PR keeps the wording fix and doesn't need to wait for the [Bug] §6.4 conflates Guardian refusal with decision failure; refusals are attacker-reachable and should fail closed #32 text.

@drk-ram, please go ahead with Rock's four asks, without the MUST sentence in ask 2, and add the ServerHello field. Then it's ready for approval.

@drk-ram

drk-ram commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator Author

@bar-capsule @rocklambros Rebased onto integration and pushed e00e311 with all four asks and the ServerHello schema update (all CI checks including agt and test are green):

  1. Updated docs/spec/conformance.md:36 to state every request and response carries a signature over the canonical envelope, except the envelopes [§10](./instrument/specification.md#10-cryptographic-signatures) exempts.
  2. Replaced the long sentence at docs/spec/instrument/specification.md:350 with `signature` is optional in the base JSON Schemas, so schema validation alone does not establish ACS-Core baseline integrity. (without the duplicate MUST sentence covered by docs: tighten section 10 verifier rules #352) and removed the control traffic parenthetical from §10.1.
  3. Changed "cryptographic signatures" to "asymmetric or post-quantum signatures" in specification.md:7, conformance.md:3, and core_concepts.md:13, and updated the first sentence of conformance.md:80.
  4. Added signature ($ref: "request-envelope.json#/$defs/Signature") to ServerHello in specification/v0.1.0/handshake.json, and registered request-envelope.json alongside handshake.json in validateServerHello (reference-implementations/agt/packages/guardian/test/server.test.ts) so Ajv resolves the cross-file $ref.
  5. Moved test_signature_requirement_and_schema_optionality_are_consistent() from tests/test_spec_structure.py to tests/test_response_envelope.py (including ServerHello.properties.signature).

@drk-ram
drk-ram force-pushed the spec/signature-requirement-195 branch from 7bac525 to 634c723 Compare November 4, 2026 21:20
…onality in §10

Aligns §10, §10.1, conformance.md, core_concepts.md, request-envelope.json,
and handshake.json (ServerHello.signature) on ACS-Core signature requirements
and schema optionality.

Fixes GenAI-Security-Project#195

Signed-off-by: Raghuvar Karthik Durga <177662415+drk-ram@users.noreply.github.com>
@drk-ram
drk-ram force-pushed the spec/signature-requirement-195 branch from 634c723 to e00e311 Compare November 4, 2026 22:31
@bar-capsule

Copy link
Copy Markdown
Collaborator

Looks good!
One small thing: please update the description, since the test moved to tests/test_response_envelope.py and it now adds the ServerHello field and the conformance.md edits.
@rocklambros - can you verify this answer your requests?

@drk-ram

drk-ram commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

@bar-capsule Updated the PR description to name tests/test_response_envelope.py, ServerHello.signature in handshake.json, and the conformance.md / core_concepts.md edits.

justrunme added a commit to justrunme/agent-control-standard that referenced this pull request Nov 7, 2026
bar-capsule asked that rule 3 reference the exemptions stated in §10's
opening sentence ("except the envelopes §10 exempts") instead of repeating
the list. State the exemptions once in the opening (aligned with GenAI-Security-Project#339) and
keep rule 3 as a pointer.

Signed-off-by: Andrey Lesnikov <52860508+justrunme@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

[Bug] Signatures are REQUIRED in §10 and conformance, "field-optional" in §10.1, and not required by the envelope schemas

3 participants