publish() now delegates to getEventSigner().sign(...) instead of the inline HMAC.
verifyChain() uses the active signer, with a legacy HMAC fall-through so rows written before PR O (from PR D) still verify. Failure message embeds the active mode for debuggability.
Helpers: legacyHmac() preserves the pre-PR-O signature scheme for historical verification; payloadHashOf() re-exported.
Why
Gap v2 §7.5 flagged the event signature as HMAC-only — the arch note (§13) calls for non-repudiable signed payloads and an HSM fallback. EIP-712 gives us on-chain-verifiable signatures keyed to the same wallet that already writes to NotaryRegistry on Chain-138.
Verification
npx tsc --noEmit clean.
npx jest 93/93 green.
13 new unit tests: mode resolution (incl. fall-through on unknown values), HMAC round-trip, JWS round-trip + payload-tamper + sig-truncation rejection, EIP-712 round-trip + ethers address recovery + cross-event replay rejection.
Rollout notes
This ships with hmac as the default — no operational change until EVENT_SIGNING_MODE=eip712 is set. When that env flip happens, existing rows still verify because verifyChain() falls back to legacyHmac().
Closes gap-analysis v2 §7.5 and §10.5 (event signatures were HMAC-only).
## What
**New**: `services/eventSigner.ts` — three interchangeable strategies selected via `EVENT_SIGNING_MODE`:
| Mode | Keying | Output shape |
|---|---|---|
| `hmac` (default) | `EVENT_BUS_HMAC_SECRET` | 64-char hex |
| `eip712` | `ORCHESTRATOR_PRIVATE_KEY` (ethers Wallet) or HSM via `EVENT_SIGNING_HSM_KEY_ID` | `0x` + 130 hex (ECDSA secp256k1) |
| `jws` | `EVENT_BUS_HMAC_SECRET` (HS256) | compact JWS `header.body.sig` |
EIP-712 domain is pinned to:
```
{name: "CurrenciCombo", version: "1", chainId: CHAIN_138_CHAIN_ID, verifyingContract: NOTARY_REGISTRY_ADDRESS}
```
**Changed**: `services/eventBus.ts`:
- `publish()` now delegates to `getEventSigner().sign(...)` instead of the inline HMAC.
- `verifyChain()` uses the active signer, with a **legacy HMAC fall-through** so rows written before PR O (from PR D) still verify. Failure message embeds the active mode for debuggability.
**Helpers**: `legacyHmac()` preserves the pre-PR-O signature scheme for historical verification; `payloadHashOf()` re-exported.
## Why
Gap v2 §7.5 flagged the event signature as HMAC-only — the arch note (§13) calls for non-repudiable signed payloads and an HSM fallback. EIP-712 gives us on-chain-verifiable signatures keyed to the same wallet that already writes to NotaryRegistry on Chain-138.
## Verification
- `npx tsc --noEmit` clean.
- `npx jest` 93/93 green.
- 13 new unit tests: mode resolution (incl. fall-through on unknown values), HMAC round-trip, JWS round-trip + payload-tamper + sig-truncation rejection, EIP-712 round-trip + ethers address recovery + cross-event replay rejection.
## Rollout notes
This ships with `hmac` as the default — no operational change until `EVENT_SIGNING_MODE=eip712` is set. When that env flip happens, existing rows still verify because `verifyChain()` falls back to `legacyHmac()`.
Closes gap-analysis v2 §7.5 and §10.5.
- services/eventSigner.ts — three interchangeable strategies
selected via EVENT_SIGNING_MODE:
hmac (default; back-compat) HMAC-SHA256 via EVENT_BUS_HMAC_SECRET
eip712 EIP-712 typed data signed by ORCHESTRATOR_PRIVATE_KEY
(ethers Wallet) or via services/hsm.ts when
EVENT_SIGNING_HSM_KEY_ID is set. Domain pinned to
CurrenciCombo/1/chain-138/NOTARY_REGISTRY_ADDRESS.
jws Compact JWS (HS256), useful when the signature has to
traverse a JWT-aware infra layer.
- services/eventBus.ts — publish() now delegates to getEventSigner();
verifyChain() uses the active signer, falling through to legacyHmac
for rows written before PR O so the historical tail still verifies.
- legacyHmac() helper + payloadHashOf() re-exported.
- 13 unit tests across mode resolution, HMAC round-trip, JWS
round-trip + tamper rejection, EIP-712 round-trip + ethers address
recovery, and cross-event replay rejection.
- Full suite 93/93 green; tsc --noEmit clean.
This pull request is broken due to missing fork information.
This pull request can be merged automatically.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes gap-analysis v2 §7.5 and §10.5 (event signatures were HMAC-only).
What
New:
services/eventSigner.ts— three interchangeable strategies selected viaEVENT_SIGNING_MODE:hmac(default)EVENT_BUS_HMAC_SECRETeip712ORCHESTRATOR_PRIVATE_KEY(ethers Wallet) or HSM viaEVENT_SIGNING_HSM_KEY_ID0x+ 130 hex (ECDSA secp256k1)jwsEVENT_BUS_HMAC_SECRET(HS256)header.body.sigEIP-712 domain is pinned to:
Changed:
services/eventBus.ts:publish()now delegates togetEventSigner().sign(...)instead of the inline HMAC.verifyChain()uses the active signer, with a legacy HMAC fall-through so rows written before PR O (from PR D) still verify. Failure message embeds the active mode for debuggability.Helpers:
legacyHmac()preserves the pre-PR-O signature scheme for historical verification;payloadHashOf()re-exported.Why
Gap v2 §7.5 flagged the event signature as HMAC-only — the arch note (§13) calls for non-repudiable signed payloads and an HSM fallback. EIP-712 gives us on-chain-verifiable signatures keyed to the same wallet that already writes to NotaryRegistry on Chain-138.
Verification
npx tsc --noEmitclean.npx jest93/93 green.Rollout notes
This ships with
hmacas the default — no operational change untilEVENT_SIGNING_MODE=eip712is set. When that env flip happens, existing rows still verify becauseverifyChain()falls back tolegacyHmac().Closes gap-analysis v2 §7.5 and §10.5. - services/eventSigner.ts — three interchangeable strategies selected via EVENT_SIGNING_MODE: hmac (default; back-compat) HMAC-SHA256 via EVENT_BUS_HMAC_SECRET eip712 EIP-712 typed data signed by ORCHESTRATOR_PRIVATE_KEY (ethers Wallet) or via services/hsm.ts when EVENT_SIGNING_HSM_KEY_ID is set. Domain pinned to CurrenciCombo/1/chain-138/NOTARY_REGISTRY_ADDRESS. jws Compact JWS (HS256), useful when the signature has to traverse a JWT-aware infra layer. - services/eventBus.ts — publish() now delegates to getEventSigner(); verifyChain() uses the active signer, falling through to legacyHmac for rows written before PR O so the historical tail still verifies. - legacyHmac() helper + payloadHashOf() re-exported. - 13 unit tests across mode resolution, HMAC round-trip, JWS round-trip + tamper rejection, EIP-712 round-trip + ethers address recovery, and cross-event replay rejection. - Full suite 93/93 green; tsc --noEmit clean.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.