[Phase 1] TypeScript SDK — frame encoding with canonical request signing and test parity with Python SDK#40
Conversation
- FastAPI app with GET /health and POST /validate endpoints - In-process regex rule engine (6 rules, 24+ patterns) - Pre-filter + sidecar two-layer enforcement flow - Pydantic request/response contracts - 82 tests — all passing, no sidecar required - SDK zero-dependency contract preserved
…ry framing, and HMAC signing inputs. transport layer: IPC connection handling, retry logic, and response frame retrieval/decoding.
…idecar interactions
|
the typescript signing path closing out #34 is a clear win. the new two things worth flagging that i think are separate asks from the core work: split the api/ directory out. the pr title is "TypeScript SDK frame encoding with canonical request signing" but the diff also adds
python-side parity for canonicalisation. on the ci additions. 24 lines added to on readme. the new "Build the TypeScript SDK" and test-command sections read clean. minor nit: the parity test line says "against live sidecar" but a reader coming fresh might not realise they need to export once the split happens i'd +1 part A quickly. part B (the api/ facade) would benefit from its own issue and design discussion |
|
That plan makes total sense. I’ll go ahead and split the work into two distinct PRs to make the review process cleaner. The Split: PR A (Fixes #34): This will focus on the TypeScript SDK wire path (models/frame/transport/firewall), canonical signing, TS tests, and the new CI job. I’ll also include the relevant README updates here. PR B: This will be dedicated to the Python API facade (api/* + docs/api.md) so we can have a separate architectural discussion there. Parity & CI Updates: I’m also setting the TypeScript check as a required status in CI and adding the ACF_HMAC_KEY / sidecar prerequisite to the README as we discussed. I'll get these pushed shortly. Once PR A is isolated, I'll ping you for a final look to close out #34. |
Implements the TypeScript SDK wire path with canonical request
signing, closing the cross-SDK HMAC mismatch gap identified in #34.
Closes #34.
Problem
JavaScript's JSON.stringify does not sort keys by default. Without canonicalization, the TypeScript SDK would produce different HMACs than the Python SDK for semantically identical payloads — the Go
sidecar would reject every TypeScript request with HMAC verification failed. This is the exact bug #32 fixed for Python and Go. This PR builds the TypeScript implementation correctly from day one.
What Was Implemented
src/models.tsDecision enum with correct wire bytes, decisionFromByte(),
SanitiseResult, ChunkResult, FirewallError, FirewallConnectionError.
src/frame.tssortKeysRecursive() — recursive key sort at all nesting levels.
canonicalPayload() — parse, sort, compact re-encode, throws
CanonicaliseError on invalid JSON.
signedMessage() — version(1B) || length(4B BE) || nonce(16B) ||
canonical_payload. Matches Python/Go layout exactly.
encodeRequest(), decodeRequest(), encodeResponse(), decodeResponse().
src/transport.tsAsync UDS transport via net.createConnection.
Injectable dialer for deterministic unit tests.
Exponential backoff retry — 100ms/200ms, 3 attempts.
Chunked response reassembly for fragmented TCP delivery.
FirewallConnectionError after retry exhaustion.
src/firewall.tsFirewall class with all four async hook methods.
onPrompt / onContext / onToolCall / onMemory.
HMAC key resolution from ACF_HMAC_KEY env variable.
RiskContext payload matches Python SDK _build_payload() exactly.
Cross-SDK Guarantee
Given the same nonce, TypeScript and Python produce identicalHMAC signatures for semantically identical JSON regardless of key ordering. Verified in frame.test.ts:
"produces identical HMAC for equivalent payload key orders
— core cross-SDK guarantee"
Unicode normalization: \u0041 → A matches Go encoding/json behaviour.
Test Coverage
models.ts 16 tests — enum values, error hierarchy
frame.ts 21 tests — canonicalization, interop, frame codec
transport.ts 7 tests — retry logic, chunked reassembly
firewall.ts 22 tests — constructor, hooks, decisions, errors
──────────────────────────────────────────
Total 66 tests 0 failures
All tests use mocks — no sidecar required for CI.
Zero external dependencies — Node stdlib only.
Relates To
this PR matches)
What This Does Not Include