Skip to content

TypeScript SDK: frame encoding, canonical request signing and HMAC parity with Python SDK#42

Open
shayankashif123 wants to merge 5 commits intoc2siorg:mainfrom
shayankashif123:pr-a-typescript-sdk-34
Open

TypeScript SDK: frame encoding, canonical request signing and HMAC parity with Python SDK#42
shayankashif123 wants to merge 5 commits intoc2siorg:mainfrom
shayankashif123:pr-a-typescript-sdk-34

Conversation

@shayankashif123
Copy link
Copy Markdown

This PR is a direct result of the review comment on the original mixed PR. As requested, the api/ Python FastAPI facade and rules engine have been split out into a separate PR (PR B) so this core
SDK wire-path work can be reviewed and merged independently without getting stalled behind API-level design questions.
This PR contains exactly what the reviewer scoped as PR A:

  • TypeScript SDK + canonical signing
  • CI additions
  • .gitignore
  • Python-side sort_keys parity fix
  • README prerequisites note

What changed and why

sdk/typescript/src/frame.ts — canonical signing

sortKeysRecursive and canonicalPayload give the TypeScript SDK deterministic byte-level output for HMAC parity with Python. Every key in the payload object tree is sorted alphabetically before
serialization — this is the JS equivalent of sort_keys=True in
Python's json.dumps.

Without this, two SDKs inserting keys in different orders produce different byte sequences and therefore different HMAC signatures. The sidecar has no way to distinguish that from tampering — it
rejects the request.

sdk/typescript/src/transport.ts — UDS client

Unix Domain Socket client using Node.js net.createConnection.
Signs every payload with HMAC-SHA256 via Node stdlib crypto — zero external dependencies. Retry logic handles the race condition between agent startup and sidecar readiness.

sdk/typescript/src/firewall.ts — Firewall class

Async/await interface for the four v1 hook call sites, mirroring the Python SDK exactly:

onPrompt(text: string): Promise
onContext(chunks: string[]): Promise
onToolCall(name: string, params: Record): Promise
onMemory(key: string, value: string, op: string): Promise

sdk/typescript/tests/parity.e2e.ts — parity tests

Runs identical payloads through both SDKs against a live sidecar and compares HMAC signatures byte-for-byte. This is the definitive guard that sortKeysRecursive and sort_keys=True stay in sync across future changes.

.github/workflows/ci.yml — CI additions (Node 20)

24 lines adding TypeScript build and test on Node 20 with cache,
wiring up the three npm scripts the Go sidecar expects:
npm run test # unit tests
npm run test:e2e # sidecar integration tests
npm run test:parity # byte-level parity against Python SDK

Note: The TS test step should be made a required status
check in branch protection settings — otherwise TypeScript
regressions can land silently.

sdk/python/acf/firewall.pysort_keys=True (line 146)

Directly addresses the parity gap flagged in the review comment:

# Before
json.dumps(ctx, separators=(",", ":")).encode("utf-8")

# After
json.dumps(ctx, separators=(",", ":"), sort_keys=True).encode("utf-8")

Python dicts preserve insertion order since 3.7 so this was invisible in simple cases — a developer who always inserts keys
in the same order would never hit it. But any nested object where Python and TypeScript insertion order diverged would produce different HMAC signatures and cause the sidecar to reject
legitimate requests. sort_keys=True closes the parity gap permanently at the source.

README.md — prerequisites note for parity test

Directly addresses the nit flagged in the review comment. Added
one line before npm run test:parity:

# Prerequisites: export ACF_HMAC_KEY=$(python3 -c "import secrets;
# print(secrets.token_hex(32))") and start the sidecar in a
# separate shell (see step 2 above)
cd sdk/typescript && npm run test:parity

Testing

# Unit tests
cd sdk/typescript && npm run test

# Sidecar integration tests
cd sdk/typescript && npm run test:e2e

# Parity tests — requires ACF_HMAC_KEY exported and sidecar
# running in a separate shell first
cd sdk/typescript && npm run test:parity

What this PR does NOT include

The api/ Python FastAPI facade, api/rules/engine.py,
api/models.py, api/config/rules.yaml, and docs/api.md
have been moved to PR B as requested. That work needs its own
issue and architectural review from @tharindupr before merging.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant