Context
Currently, payloads are signed as raw bytes. While this is technically valid, it leads to inconsistent HMAC verification across SDKs when JSON key ordering differs.
This issue proposes introducing canonical JSON signing to ensure deterministic HMAC across SDKs.
Example:
- Python SDK sends:
{"b":2,"a":1}
- Go sidecar receives:
{"a":1,"b":2}
- Result: ❌ HMAC mismatch — despite semantically identical payloads
Relates to #18, #23.
Solution
Canonicalize JSON before HMAC computation in both the Go sidecar and Python SDK:
- Parse payload as JSON (reject if invalid)
- Re-encode with sorted keys, no whitespace
- Sign the canonical bytes
This guarantees identical HMAC input regardless of SDK or serialization order.
Changes Required
Go (sidecar/internal/transport/)
frame.go — Canonicalize in SignedMessage(), update return signature to ([]byte, error)
frame.go — Propagate errors in EncodeRequest() / DecodeRequest()
listener.go — Handle canonicalization errors
frame_test.go — Add tests for key order, whitespace, invalid JSON, cross-SDK interop
Python (sdk/python/acf/frame.py)
frame.py — Canonicalize in signed_message() using json.dumps(sort_keys=True)
test_frame.py — Mirror Go test cases
Acceptance Criteria
SignedMessage() / signed_message() canonicalize JSON with sorted keys
- Invalid JSON is rejected with a clear error
- Python and Go produce identical signatures for semantically identical JSON
- All existing tests pass
- New tests cover: key order, whitespace, cross-language interop, error cases
phase1.md documentation updated
- No breaking public API changes
Context
Currently, payloads are signed as raw bytes. While this is technically valid, it leads to inconsistent HMAC verification across SDKs when JSON key ordering differs.
This issue proposes introducing canonical JSON signing to ensure deterministic HMAC across SDKs.
Example:
{"b":2,"a":1}{"a":1,"b":2}Relates to #18, #23.
Solution
Canonicalize JSON before HMAC computation in both the Go sidecar and Python SDK:
This guarantees identical HMAC input regardless of SDK or serialization order.
Changes Required
Go (
sidecar/internal/transport/)frame.go— Canonicalize inSignedMessage(), update return signature to([]byte, error)frame.go— Propagate errors inEncodeRequest()/DecodeRequest()listener.go— Handle canonicalization errorsframe_test.go— Add tests for key order, whitespace, invalid JSON, cross-SDK interopPython (
sdk/python/acf/frame.py)frame.py— Canonicalize insigned_message()usingjson.dumps(sort_keys=True)test_frame.py— Mirror Go test casesAcceptance Criteria
SignedMessage()/signed_message()canonicalize JSON with sorted keysphase1.mddocumentation updated