Skip to content

[RefImpl] Consolidate AGT wire signing, config and protocol constants into a shared acs-wire package #230

Open

Description

@jrcrittenden

Target runtime or language

Hardening the existing AGT reference implementation (reference-implementations/agt/, TypeScript on Bun).

What this proves

That ACS signing and canonicalization can live in one small package that both the Guardian and a host import. §10 requires both sides to compute the same canonical input, and today each side carries its own copy.

This is the refactor @afogel proposed in the review of #229. It was deferred from that PR so the PR stays reviewable.

1. Add a shared packages/acs-wire package (server.ts:658). Its only dependency is node:crypto. The host still never imports the Guardian.

  • protocol.ts: ACS_VERSION, the §17.1 AcsError codes, AcsSignature and the algorithm names.
  • jcs.ts: one RFC 8785 implementation. Today there are two, in verify-signature.ts and sign-envelope.ts.
  • authenticator.ts: createHmacAuthenticator(ikm) and NULL_AUTHENTICATOR, which implement a RequestVerifier role for the Guardian and a RequestSigner role for the host. HKDF derivation moves in here, and there is one copy of it.

2. Guardian: verify each request once. Remove the inline check at server.ts:864–886, requestHasValidSignature, extractSessionId, the five base64 decodes of the secret, and every if (hmacSecret) branch. buildServerHello reads authenticator.algorithms.

3. Host: let GuardianClient sign as well as verify (govern-step.ts:665). Remove the hmacSecret option from governStep, from negotiateSessionConfig and from OpenCode's Deployment, and delete sign-envelope.ts.

4. Read ACS_HMAC_SECRET once, through one validator (acs-hook.ts:536). Use strict base64 and a 32-byte minimum. Today both hosts silently run unsigned when the variable is empty and accept short keys. Also:

5. Give each constant one source, split by owner (handshake.ts:116).

  • Values fixed by the spec go in acs-wire/protocol.ts. These are the version, the error codes, the algorithm names and the signature type. The host's REFUSAL_RPC_CODES is then built from the named codes instead of a copied list.
  • Values chosen by the deployment go in the Guardian's config. These are the skew window, timeouts, replay-store limits and the minimum key length. ServerHello and the timestamp check then read the same skewWindowMs and cannot drift apart.

Done when no secret is decoded outside the authenticator, and JCS and HKDF each exist once.

Do you intend to implement this yourself

Yes, I want this assigned to me

Current Priority Scope

Feeds the runnable Guardian reference implementation

Issue drafted with Claude Code.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions