- Wire format for
Go SignBenchmarkResult,Go A2A artifact signing, andRust RemoteJacsProvider::sign_filenow uses RFC 8785 canonical JSON / canonical JACS file envelopes via JACS, replacing hand-rolledjson.Marshal/payload_b64shapes. Records persisted by pre-0.4.0 clients carrymetadata.hashandjacsSignature.signatureover stdlib JSON / flat envelopes — they cannot be verified by 0.4.0+ clients. Re-sign or tombstone any persistedbenchmark_result/A2AWrappedArtifact/.jacsfile envelope from before this release. (Issue 003) - Removed:
compute_content_hashandAttachmentInput. Affects Pythonhaiai.compute_content_hash, NodecomputeContentHash, Rusthaiai::compute_content_hash/haiai::AttachmentInput, Gohaiai.ComputeContentHash. The wrapper duplicated JACS canonical hashing and drifted from JACS's CRLF/BOM handling. Migrate to JACS's canonical hashing helpers (jacs::crypt::hash::hash_public_keyin Rust; the equivalenthashString/hash_stringexposed by the JACS Python and Node bindings). The correspondingfixtures/email_conformance.jsonentries that referenced the helper have been trimmed. (Issue 004) - Node
signResponseandunwrapSignedEventno longer fall back to the in-processsortedKeyJsonwhen the agent / canonicalizer is omitted. RFC 8785 canonicalization is delegated to JACS with no local fallback, matching Python's behaviour.unwrapSignedEvent'sagentparameter is now required;signResponse's local-envelope path requires either a signer that exposessignResponseSyncor an explicitcanonicalizer: JacsAgent. (Task list #61.)
- Python
canonicalize_jsonagainst ephemeral JACS adapters. When the loaded agent does not exposecanonicalize_json(e.g. JACS's_EphemeralAgentAdapterwrappingSimpleAgent),signing.canonicalize_jsonnow delegates to a statelessjacs.JacsAgent()instance — RFC 8785 canonicalization is keyless, so any JACS install can produce the canonical bytes. Eliminates theJACS_TOO_OLDregression that was failing 21 Python tests. (Issue 001) - Typed errors from
/api/v1/records. Non-success responses now surface as the typedHaiError::Api { status, message }variant rather than the catch-allHaiError::Provider. Cross-language SDKs map this toErrorKind::AuthFailed/NotFound/RateLimited/ApiError. If you previously relied onIsAuthError(err)/HaiAuthError/AuthenticationErrorto fire on every records-endpoint error, audit your branches: a 5xx will no longer hit the auth-error branch. (Prior Issue 008.) query_by_type/query_by_agentcursor walk. Previously returned an empty array for anyoffset >= 100because the server's per-page cap is 100 and the SDK didn't walk further. Now walks forward via the server's cursor untillimitrecords pastoffsetare accumulated. If your code used "empty page" as a loop terminator, switch to "result count < limit" instead. (Prior Issue 009.)RemoteJacsProvider::sign_filedelegation. Now delegates toinner.sign_file_envelopeso the file envelope is byte-identical toLocalJacsProvider::sign_file(canonical(jacsType="file", jacsLevel, jacsFiles[...])shape). The previous hand-rolledpayload_b64/ flatsha256envelope diverged from the JACS schema and cross-language verification. (Prior Issue 006.)verify_dns_public_keyuses JACS canonicalhash_public_keyrather than hand-rolled SHA-256 over PEM. Hash bytes are now byte-identical to JACS's reference. (Prior Issue 012.)- Makefile
test-gonow setsCGO_LDFLAGS/DYLD_LIBRARY_PATH/LD_LIBRARY_PATHand depends onbuild-haiigoso a fresh-clonemake test-goactually runs. (Prior Issue 017.)
- Go
ctxis not propagated through cgo in the 20 doc-store methods on*Client. Method signatures take_ctx context.Contextfor forward-compatibility, but cancellation is not honoured at the FFI boundary — aWithTimeoutwill not abort the in-flight cgo call. The 19 pre-existing methods that go through the Go reqwest layer still honour ctx. Honest cancellation via a watchdog goroutine is tracked as a follow-up. (Prior Issue 015.) - PRD / TASK / EMAIL_VERIFICATION recipe docs for the 0.3.0 work were moved out of
docs/haisdk/intodocs/archive/2026-04/rather than deleted.CLAUDE.mdnow points consumers at the archivedEMAIL_VERIFICATION.md. - JACS pinned to
=0.10.0for now; bump in 0.4.x once 0.10.1+ is published.
- Remote JACS Document Store (
RemoteJacsProvider):JacsDocumentProviderimpl that talks tohai-apiat/api/v1/records/...instead of touching the filesystem. SetJACS_STORAGE=remote(or--storage remotein CLI) and the same trait calls a developer makes againstLocalJacsProvider(sign + store + fetch + version + list + search) work over HTTPS. Auth uses the existingAuthorization: JACS {jacsId}:{ts}:{sig}header; signing keys never leave the client. Maps server 4xx toHaiError::Provider(server_message)and 5xx toHaiError::Provider("server error: ..."). See~/personal/haisdk/rust/haiai/src/jacs_remote.rs. - MEMORY.md / SOUL.md wrappers (D5): New inherent methods on
RemoteJacsProvider—save_memory(Option<&str>),save_soul(Option<&str>),get_memory(),get_soul(). When called withNone, readsMEMORY.md/SOUL.mdfrom CWD. Server-route-free (setsjacsType="memory"|"soul"on the signed envelope). - Native signed-media uploads (D9):
RemoteJacsProvider::store_text_file(path)/store_image_file(path)/get_record_bytes(key)post pre-signed inline-text or image bytes to/api/v1/recordswith the rightContent-Type: text/markdown; profile=jacs-text-v1/image/png/image/jpeg/image/webp. Local sanity check rejects unsigned files BEFORE making any HTTP call. - FFI parity contract (
fixtures/ffi_method_parity.json): newjacs_document_storesection listing 20 methods (13 trait + 4 D5 + 3 D9).total_method_count72 → 92; contract test renamedffi_method_parity_total_count_is_92. - Integration tests:
~/personal/haisdk/rust/haiai/tests/jacs_remote_integration.rs— 13 tests (12#[ignore]) covering the full SDK ↔ server end-to-end surface against the hosted-stack Docker compose. Run withcargo test -p haiai --test jacs_remote_integration -- --ignoredafter exportingHAI_URLto your hosted stack. remotestorage label (TASK_010):haiai::config::resolve_storage_backend_label("remote")returnsOk("remote");JACS_STORAGE=remoteis now a valid env value alongsidefs/rusqlite/sqlite.- Local media signing (Layer 8): New
JacsMediaProvidertrait exposes JACS's offlinesign_text_file/verify_text_file/sign_image/verify_image/extract_media_signatureoperations. PNG / JPEG / WebP via metadata channel (PNG iTXt / JPEG APP11 / WebP XMP) plus optional LSB robust mode for PNG/JPEG. - CLI: 5 new subcommands (
sign-text,verify-text,sign-image,verify-image,extract-media-signature) mirroring thejacsreference CLI flag set and exit codes (0 valid / 1 bad-or-strict-missing / 2 permissive-missing). - MCP: 5 new tools (
hai_sign_text,hai_verify_text,hai_sign_image,hai_verify_image,hai_extract_media_signature) withrequire_relative_path_safetraversal guard imported fromjacs::validation. - Python / Node / Go: per-language wrappers on
HaiClientandAsyncHaiClient(Python sync+async). Result dataclasses / interfaces / structs, plusMediaVerifyStatus*constants in Go. - Public API contract: the user-facing key is
robusteverywhere (CLI flag, MCP param, Python kwarg, Node interface, Go field). The JACS-internalscan_robustfield is hidden inside binding-core's parser. - Cross-language verify-parity contract: pre-signed
fixtures/media/signed.{png,jpg,webp,md}(signed once byrust/haiai/tests/regen_media_fixtures.rs, watchdog'd byCHECKSUMS.txt+SIGNER.json) drive 32 verify-parity tests — 8 each in Rust / Python / Node / Go — proving the four SDKs agree onvalidandhash_mismatchfor the same input bytes (PRD §5.5). - JACS pinned to 0.10.0 across
rust/haiai,rust/haiai-cli, andrust/hai-mcp. Binding-core widened toBox<dyn JacsMediaProvider>(supertraitJacsProvidercontinues to compile transparently).
- One-step registration:
haiai init --name <name> --key <key>generates keypair, registers, and claims username in one command. RemovedcheckUsername/claimUsernamefrom all SDKs. - CI uses JACS workspace patch: Test jobs clone JACS at the expected relative path instead of stripping
[patch.crates-io]. - Fix JACS
rotate()API: Updated call to pass the newalgorithmparameter added upstream. - Native SDKs promoted to beta.
- FFI-first architecture: All HTTP calls, auth, retry, and URL building now live in Rust and are exposed to Python, Node, and Go via FFI bindings (PyO3, napi-rs, CGo). Eliminates 4 separate HTTP implementations.
- New crates:
hai-binding-core,haiipy,haiinpm,haiigoprovide the shared FFI dispatch and per-language bindings. - No local crypto in SDKs: Removed
go/crypto.go,go/signing.go, and equivalents. All signing/verification delegates to JACS via FFI. - Removed Go CLI and MCP server:
go/cmd/haiai/andgo/cmd/hai-mcp/deleted. Rusthaiaibinary is the canonical CLI and MCP server. - MCP email signing fixed:
EmbeddedJacsProvidernow implementssign_email_locallyvia anAgentSignerwrapper, matching CLI behavior. - MCP log file support:
haiai --log-file <path> mcpcaptures logs to a file for debugging when spawned by an MCP client. - Email reply/forward fixes: Fixed subject-line CR/LF corruption, added proper threading headers (In-Reply-To, References), fixed signature attachment handling.
- SSE and WebSocket transports migrated to FFI: Streaming connections use an opaque handle pattern through
hai-binding-core. - CI updates: Reduced PR test matrix, added concurrency controls, updated publish workflows for FFI build requirements.