@orchestkit/hook-contract) and
Python (orchestkit-hook-contract) sibling
packages both verify against the same 13 golden vectors. Zero runtime deps
on either side.
X-CC-Hooks-Signature: t=<unix-seconds>,v1=<hex-sha256>[,v2=...]
β β
β ββ HMAC-SHA256(secret, "t={ts}." + raw_body)
ββ timestamp the sender used at signing time
Reject reasons (stable enum, both languages):
ok β signature verified
missing_header β header absent or empty
malformed_header β present but unparseable / unexpected schema label
stale β |now - t| > tolerance_sec (default 300s)
signature_mismatchβ no scheme/secret pair matched
weak_secret β advisory only, never blocks ok
| File | Purpose |
|---|---|
packages/hook-contract/docs/signing-rfc.md | Language-neutral protocol spec (289 lines). Header grammar, replay window, multi-scheme rotation rules, Reason enum, gap analysis vs the platform consumer. |
packages/hook-contract/src/signing.ts | sign() + verify() using node:crypto (createHmac + timingSafeEqual). |
packages/hook-contract-py/src/orchestkit_hook_contract/signing.py | sign() + verify() using stdlib hmac + hashlib. Mirrors the TS file byte-for-byte for the same inputs. |
packages/hook-contract/test-vectors/signing/*.json | 13 deterministic vectors (5 positive, 8 negative). Bodies base64-encoded so vectors carry arbitrary bytes. Shared between both languages. |
packages/hook-contract/tests/signing.test.ts + tests/test_signing.py | 27 vitest + 42 pytest cases. Both load the same vectors; both pass. |
| Guard | Why |
|---|---|
8192-byte header cap β malformed_header | DoS guardrail. Attacker can't make the parser walk a megabyte header. |
10-digit timestamp cap β stale | Cross-language parity: TS Number overflows past safe-integer; Python bigints don't. Cap both. |
weak_secret fires only after parse succeeds | Info-disclosure tightening. Malformed-input attackers can't probe the verifier's secret-length configuration. |
OR-assign on compare_digest result | Non-short-circuit intent explicit. Loop runs to completion regardless of early match. |
Element-level isinstance in _normalize_secrets | Defense in depth. mypy can't subtract Sequence[bytes|str] from bytes/str (both ARE Sequences); per-element guard restores the narrowing AND catches programmer error at runtime. |
βββββββββββββββββββββββββββββββββββββββββββββββ
β test-vectors/signing/*.json β
β 13 vectors (5 positive, 8 negative) β
β secret_hex / body_b64 / header / now / β
β tolerance_sec / expected{valid, reason} β
ββββββββββββββ¬βββββββββββββββββ¬ββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββββββ
β vitest β β pytest β
β signing.test.ts β β test_signing.py β
β 27/27 β β β 42/42 β β
βββββββββββββββββββ βββββββββββββββββββββββ
A vector's expected.reason MUST match exactly on both sides.
If they diverge, the cross-language parity gate (M141-6) AND
this vector matrix catch it before the platform consumer does.
$ cd packages/hook-contract && npm test 27 vectors + signing API: PASS $ cd packages/hook-contract-py $ .venv/bin/pytest tests/test_signing.py -v ============================== 42 passed in 0.31s ============================== $ .venv/bin/mypy src Success: no issues found in 5 source files $ .venv/bin/ruff check . All checks passed!
yonatan-hq/platform.yonatan-hq/platform consumer from its hand-written HMAC verifier to this package.