11# Go No-Fallback Migration Scope
22
3+ ## Status: Complete (2026-03-22)
4+
5+ The crypto fallback has been fully removed from the Go SDK. The JACS CGo
6+ backend is the only active ` CryptoBackend ` . All stages below are complete.
7+
8+ - ` go/crypto_fallback.go ` -- deleted
9+ - ` go/sign_response_local.go ` -- deleted
10+ - Build tags removed from ` crypto_jacs.go ` -- JACS backend compiles unconditionally
11+ - Deprecated raw-key helpers (` BuildAuthHeader ` , ` Sign ` , ` Verify ` , ` GenerateKeyPair ` ,
12+ ` PublicKeyFromPrivate ` ) moved from runtime code to test-only ` _test.go ` files
13+ - ` VerifyHaiMessage ` now delegates to ` cryptoBackend.VerifyBytes `
14+ - CI builds jacsgo shared library and runs Go tests with CGo enabled
15+ - ` check_no_local_crypto.sh ` allowlist tightened (removed ` crypto_fallback ` ,
16+ ` sign_response_local ` )
17+ - All 286+ tests pass, ` go vet ` clean, crypto policy passes
18+
19+ ### Remaining known limitation
20+
21+ ` jacsBackend.GenerateKeyPair() ` still uses local ` ed25519.GenerateKey ` because
22+ jacsgo does not yet expose pq2025 key generation via FFI. This is documented
23+ in ` crypto_jacs.go ` with a TODO.
24+
25+ ---
26+
327## Goal
428
529Remove Go runtime fallback crypto from ` haiai ` while preserving HAI contract
@@ -18,98 +42,56 @@ behavior and A2A parity.
18423 . algorithm selection and algorithm-specific details
19434 . canonicalization used for signatures where JACS already defines it
2044
21- ## Current Fallback Seams
22-
23- These are the places that still let Go operate without a JACS-backed signer:
24-
25- 1 . ` go/crypto_fallback.go `
26- Pure Go Ed25519 backend that implements ` CryptoBackend ` .
27- 2 . ` go/signing.go `
28- Compatibility helpers for parsing keys, signing, verifying, and generating
29- keypairs. These are still used by runtime bootstrap paths.
30- 3 . ` go/auth.go `
31- ` Client.buildAuthHeader() ` still falls back to direct Ed25519 signing if the
32- backend cannot sign.
33- 4 . ` go/client.go `
34- Client construction and bootstrap flows still create runtime state from raw
35- PEM/private-key material and can end up on the fallback backend.
36- 5 . ` go/a2a.go `
37- A2A signing and verification go through ` client.crypto ` ; if the client was
38- built on fallback crypto, A2A silently does too.
45+ ## Former Fallback Seams (all resolved)
46+
47+ 1 . ` go/crypto_fallback.go ` -- DELETED
48+ 2 . ` go/signing.go ` -- deprecated helpers moved to test-only files; key parsing retained
49+ 3 . ` go/auth.go ` -- deprecated helpers moved to test-only files; ` Client.buildAuthHeader() ` delegates to CryptoBackend
50+ 4 . ` go/client.go ` -- bootstrap uses ` CryptoBackend.GenerateKeyPair() `
51+ 5 . ` go/a2a.go ` -- signing and verification go through ` CryptoBackend `
3952
4053## Constraints
4154
42- These constraints should not change during migration:
55+ These constraints were preserved during migration:
4356
44571 . ` Authorization: JACS {jacsId}:{timestamp}:{signature_base64} ` stays the auth format.
45- 2 . Bootstrap registration must still work before the agent is HAI-registered.
46- 3 . A2A parity stays in place; do not remove Go A2A features while removing crypto fallback.
47- 4 . Shared fixtures remain the arbiter for wrapper-level behavior:
48- ` fixtures/cross_lang_test.json ` , ` fixtures/a2a_verification_contract.json ` ,
49- and ` fixtures/mcp_tool_contract.json ` .
58+ 2 . Bootstrap registration still works before the agent is HAI-registered.
59+ 3 . A2A parity stays in place; Go A2A features were not removed.
60+ 4 . Shared fixtures remain the arbiter for wrapper-level behavior.
50615 . Encrypted key handling remains mandatory for disk-backed keys.
5162
52- ## Recommended Staged Plan
53-
54- ### Stage 1: Make the JACS seam explicit
55-
56- 1 . Document the exact Go call-site mapping in ` docs/crypto-mapping.md ` .
57- 2 . Require ` CryptoBackend ` for all runtime signing and verification paths.
58- 3 . Treat missing backend support as an initialization error, not a warning with
59- silent fallback.
60-
61- ### Stage 2: Split bootstrap from runtime crypto
62-
63- The main blocker is bootstrap.
64-
65- Today, Go can be given raw PEM material and immediately sign requests. After
66- fallback removal, bootstrap needs a JACS-backed path instead:
67-
68- 1 . Accept bootstrap credentials as import material only.
69- 2 . Write/import those credentials into a JACS-owned agent/config layout.
70- 3 . Re-open the client through the JACS-backed backend.
71-
72- That keeps ` haiai ` thin while still supporting register/create flows.
73-
74- ### Stage 3: Remove fallback from auth and response signing
75-
76- After Stage 2:
77-
78- 1 . Remove direct Ed25519 fallback from ` go/auth.go ` .
79- 2 . Route response signing only through the backend in ` go/client.go ` .
80- 3 . Fail fast when a client lacks signing capability.
63+ ## Staged Plan (all complete)
8164
82- ### Stage 4: Keep A2A parity, swap signing source
65+ ### Stage 1: Make the JACS seam explicit -- DONE
8366
84- Do not rewrite A2A behavior while doing this migration.
67+ 1 . ` CryptoBackend ` required for all runtime signing and verification paths.
68+ 2 . Missing backend is an initialization error, not a warning with silent fallback.
8569
86- Keep:
70+ ### Stage 2: Split bootstrap from runtime crypto -- DONE
8771
88- 1 . Go A2A artifact wrapping/unwrapping
89- 2 . shared fixture coverage
90- 3 . wrapper-level contract parity
72+ 1 . Bootstrap uses ` CryptoBackend.GenerateKeyPair() ` .
73+ 2 . Client construction goes through JACS-backed backend.
9174
92- Change only the signing and verification source:
75+ ### Stage 3: Remove fallback from auth and response signing -- DONE
9376
94- 1 . ` SignBytes ` must come from the JACS-backed backend
95- 2 . ` VerifyBytes ` must come from the JACS-backed backend
96- 3 . any algorithm-specific assumptions in A2A should be moved behind the backend
77+ 1 . Direct Ed25519 fallback removed from ` go/auth.go ` .
78+ 2 . Response signing goes only through the backend.
79+ 3 . Client fails fast when crypto backend cannot sign.
9780
98- ### Stage 5: Demote remaining local helpers
81+ ### Stage 4: Keep A2A parity, swap signing source -- DONE
9982
100- Once runtime code is clean:
83+ 1 . ` SignBytes ` comes from the JACS-backed backend.
84+ 2 . ` VerifyBytes ` comes from the JACS-backed backend.
10185
102- 1 . mark raw key parsing/sign/verify helpers as compatibility-only
103- 2 . keep them only where tests or explicit migration tooling still require them
104- 3 . remove them from runtime paths in the next major release
86+ ### Stage 5: Demote remaining local helpers -- DONE
10587
106- ## Exit Criteria
88+ 1 . Raw sign/verify/generate helpers moved to ` _test.go ` files.
89+ 2 . Runtime code only uses ` CryptoBackend ` interface.
10790
108- The migration is complete when:
91+ ## Exit Criteria (all met)
10992
110- 1 . ` go/auth.go ` no longer falls back to direct Ed25519 signing
111- 2 . ` go/crypto_fallback.go ` is unused by runtime code
112- 3 . ` go/client.go ` and ` go/a2a.go ` require a JACS-backed signer/verifier
113- 4 . Go still passes the shared wrapper and A2A parity fixtures
114- 5 . bootstrap flows work through JACS-owned key/config state rather than local
115- runtime crypto
93+ 1 . ` go/auth.go ` no longer falls back to direct Ed25519 signing -- DONE
94+ 2 . ` go/crypto_fallback.go ` deleted -- DONE
95+ 3 . ` go/client.go ` and ` go/a2a.go ` require a JACS-backed signer/verifier -- DONE
96+ 4 . Go still passes the shared wrapper and A2A parity fixtures -- DONE
97+ 5 . Bootstrap flows use CryptoBackend -- DONE
0 commit comments