g8e v1.6.0
[1.6.0] - 2026-07-20
Overview
Cross-language governance hash parity, enrollment token TOCTOU fix, SSE resilience improvements, and governance model schema alignment. This release ensures Go and Python produce identical transaction hash digests by aligning canonicalization logic, hardens enrollment token consumption against race conditions with atomic conditional updates, makes the SSE heartbeat interval configurable, fixes multi-line SSE data parsing per spec, improves pubsub reconnect backoff reset logic, updates Python governance models to match proto definitions, adds a canonical governance JSON schema, and expands test coverage by approximately 1,665 lines across 15 new test files.
Added
- Governance JSON schema —
protocol/models/governance.json(263 lines) defines canonical governance wire shapes matchingprotocol/proto/g8e/common/v1/common.proto. Coversl1_metadata,l2_vote,l2_metadata,l3_proof,l3_metadata,governance_metadata, andgovernance_envelope. GovernanceL3ProofPython model —protocol/python/g8e/models/governance.pyaddsGovernanceL3Proofmodel representing the L3 notary proof union (WebAuthn or CLI/mTLS). Fields:client_data_json,authenticator_data,signature,credential_id(WebAuthn),mtls_cert_fingerprint,cli_signature(CLI/mTLS). Exported fromprotocol/python/g8e/models/__init__.py.- Cross-language hash parity conformance tests —
protocol/conformance/test_hash_parity.py(127 lines) verifies Pythoncompute_transaction_hashmatches GoGenerateMessageIDusing shared test vectors inprotocol/conformance/hash_vectors.json(109 lines). Covers standard, nested intent, unicode, empty payload, empty intent, optional omitted, and timestamp normalization cases. DocConditionalUpdatemethod —internal/services/gateway/document_store_service.goaddsDocConditionalUpdate(collection, id, setFields, conditionField, conditionValue)for atomic conditional SQL updates. Usesjson_extractin a WHERE clause to check a condition andjson_setchain to apply updates in a single statement, preventing TOCTOU races.- Configurable SSE heartbeat interval —
HTTPHandlerDependenciesandHTTPHandlergainSSEHeartbeatIntervalfield (internal/services/gateway/gateway_http.go). Defaults to 30 seconds when zero.handleInternalSSEStreamusesh.sseHeartbeatIntervalinstead of a hardcoded 30s constant. ErrPasskeySSEStreamClosederror —internal/constants/errors.goadds error for unexpected SSE stream disconnection during passkey enrollment, enabling distinct handling of timeout vs. stream-closed scenarios.programSenderinterface —internal/cli/auth/passkey_bootstrap.goaddsprogramSenderinterface (implemented by*tea.Program) to decouple SSE monitoring from the terminal program, enabling unit testing ofmonitorPasskeyRegistrationwith a channel-based mock.SetSubscribeErrormock method —internal/services/pubsub/pubsubtest/mock_client.goaddsSetSubscribeError(err)to inject subscribe failures in tests.- Static linking verification —
.github/workflows/build-and-test.ymladds a CI step that verifies the Linux binary is statically linked usingfileandlddchecks. - Governance model conformance tests —
protocol/conformance/test_models.pyaddsTestGovernanceL1Conformance,TestGovernanceL2Conformance,TestGovernanceL3Conformance,TestGovernanceMetadataConformance, andTestGovernanceEnvelopeConformanceclasses with round-trip serialization, default value, and schema field parity tests for the updated governance models.
Changed
- Go
canonicalizeMap/canonicalizeValueerror propagation —internal/governance/envelope.go: Both functions now return(string, error)instead ofstring. Unknown types produce an error instead of silently falling back tojson.Marshal, ensuring cross-language hash parity.GenerateMessageIDpropagates canonicalization errors. Removedencoding/jsonimport. - Python
compute_transaction_hashrewrite —protocol/python/g8e/models/governance.py: Rewritten to match Go'sGenerateMessageIDexactly. Empty/None fields are omitted entirely (no value, no separator). A trailing|follows each present field. Timestamps normalized to fixed 6-digit microsecond UTC via_normalize_timestamp(). Intent data canonicalized askey=value,key=valuevia_canonicalize_map()/_canonicalize_value()instead of JSON. Unsupported types raiseTypeError. - Python governance model field updates —
protocol/python/g8e/models/governance.py:GovernanceL1.violationschanged fromlist[dict[str, Any]]tolist[str]GovernanceL2Votefields changed from (voter_id,decision,timestamp,signature) to (signer_key_id,consensus_signature,decision)GovernanceL2fields changed from (votes,consensus_reached,threshold) to (tribunal_id,votes)GovernanceL3fields changed from (notary_id,signature,timestamp,certificate_chain) to (proof)
- Enrollment token atomic consumption —
internal/services/gateway/enrollment_token_service.go:ValidateAndConsumeTokennow usesDocConditionalUpdatewithconsumed = 0condition instead of read-then-write. The atomic conditional UPDATE prevents TOCTOU races where concurrent callers could both readconsumed=falsebefore either writesconsumed=true. ReturnsErrEnrollmentTokenConsumedif another caller consumed it between read and update. - Passkey bootstrap SSE monitoring extraction —
internal/cli/auth/passkey_bootstrap.go: SSE monitoring logic extracted intomonitorPasskeyRegistration(ctx, sseClient, sender)function. On clean SSE disconnect without a registration event, sendsenrollErrMsgwithErrPasskeySSEStreamClosed(previously only handled context cancellation/timeout). - SSE multi-line data parsing —
internal/cli/sse/client.go:parseSSEStreamnow concatenates multipledata:lines with\nbetween them per the SSE specification, instead of overwriting the previous data line. - Pubsub reconnect backoff reset —
internal/services/pubsub/pubsub_commands.go: Reconnect attempt counter (attempts) is now reset to 0 on successful connection. Previously, attempts accumulated across disconnect cycles, causing premature give-up after transient failures. Backoff delay calculation extracted intonextReconnectDelay(current, max)and give-up check intoshouldGiveUp(attempts, maxAttempts). actions/setup-gov6 to v7 — Bumped across.github/workflows/build-and-test.ymland.github/workflows/release-binary.yml.- Governance architecture doc —
docs/architecture/governance.mdupdated: intent data canonicalization now rejects unsupported types with an error (no silent fallback), ensuring cross-language hash parity between Go and Python. - Tests documentation —
docs/devs/tests.mdupdated: conformance suite count updated from 330 to 420 tests across 3 files, withtest_hash_parity.pydescription added.
Fixed
- Enrollment token TOCTOU race — Concurrent calls to
ValidateAndConsumeTokencould both readconsumed=falseand proceed to consume the same token. Fixed with atomic conditional SQL update that only setsconsumed=trueif the current value is0(false). - SSE multi-line data loss —
parseSSEStreamoverwrotedataon eachdata:line instead of concatenating, causing multi-line SSE data payloads to be truncated. Fixed to join lines with\nper spec. - Pubsub premature reconnect give-up — The
attemptscounter was never reset on successful reconnection, so a sequence of transient disconnects across multiple connection lifetimes could exhaustmaxReconnectAttemptseven though each individual disconnect was recoverable. Fixed by resettingattemptsto 0 when a connection is established.
Tests
internal/cli/auth/passkey_bootstrap_sse_test.go— 159 lines:monitorPasskeyRegistrationbehavior with mockprogramSender, covering registration event, timeout, and stream-closed scenariosinternal/cli/sse/sse_parser_test.go— 98 lines: SSE parser edge cases including multi-line data, event/data ordering, and blank line dispatchinternal/governance/envelope_hash_parity_test.go— 175 lines: Go hash canonicalization with nested maps, arrays, unicode, empty fields, and unsupported type error casesinternal/services/gateway/enrollment_token_concurrent_test.go— 114 lines: Concurrent token consumption race condition verificationinternal/services/gateway/enrollment_token_service_test.go— 16 lines: Additional enrollment token service edge casesinternal/services/gateway/sse_backpressure_test.go— 111 lines: SSE backpressure and slow consumer handlinginternal/services/gateway/sse_event_cleanup_test.go— 117 lines: SSE event cleanup and subscriber removalinternal/services/governance/l4_warden_dependency_failure_test.go— 128 lines: L4 Warden behavior when dependencies failinternal/services/governance/l4_warden_tribunal_edge_test.go— 278 lines: L4 Warden tribunal edge casesinternal/services/governance/signer_store_test.go— Renamed fromwarden_edge_test.go: signer store test coverageinternal/services/pubsub/reconnect_backoff_test.go— 219 lines: Reconnect backoff progression, cap, and reset behaviorinternal/services/pubsub/reconnect_reset_test.go— 162 lines: Reconnect attempt counter reset on successful connectioninternal/services/tribunal/factory_error_test.go— Renamed fromfactory_edge_test.go: tribunal factory error casesinternal/services/tribunal/service_concurrent_test.go— 81 lines: Tribunal service concurrent operation safetyprotocol/conformance/test_hash_parity.py— 127 lines: Cross-language hash parity with shared test vectorsprotocol/conformance/test_models.py— Governance model conformance tests for updated L1, L2, L3, L3Proof, Metadata, and Envelope modelsprotocol/python/tests/test_models.py— Updated governance model tests to match new field names and structures