v0.99.12
Security
- Always install a WebSocket handshake Origin/Host guard on the core hub
(even when metrics are disabled): browser Origins require an explicit
concrete--allow-originallow-list; Host authorities are derived from
the loopback bind and optional--advertised-host(no wildcard trust);
origin-less native clients remain compatible only through a trusted Host;
disallowed upgrades receive a deterministic403before the socket
upgrades and cannot read history. - Persist a hash-chained, owner-only Merkle checkpoint of the durable event
log OUTSIDE the log it attests, and verify it fail-closed before the hub
serves: a log shorter than its checkpoint (tail truncation) or a recomputed
prefix root that differs (log replacement) now raisesAntiRollbackError
at startup instead of restarting silently. Once verified, the hub anchors
the current state as the newest chain link.synapse merkle checkpoint
shows the newest link or verifies the log against it (exit 0 clean / 2
detection, JSON verdict). This is the local anti-rollback layer only;
external witnessing stays owner-gated and out of scope. - Bound open-hub idle sockets and single-host connection floods by default:
--max-connections-per-hostnow defaults to 32 (pass0to disable),
and every hub — open or secured — reaps a socket that does not bind a name
within--auth-timeout(close code4012; open hubs use reason
registration timeout). Secure mode clamps the open default down to its
stricter per-host ceiling rather than refusinghub --securesolely for
inheriting the open default. - Serve the multi-hub event log only under an operator-configured
MultiHubServingPolicy: a hub with no policy now refuses every peer
(fail-closed) instead of serving the entire log to any unauthenticated
frame, matching the operator-relay posture. A refused peer receives an
empty snapshot that leaks neither the log nor the grant's existence. - Key wait edges by the waited task instead of the incumbent holder and
resolve ownership live at cycle-check time: a claim, renewal, or handoff
for one task can no longer erase a wait on an unrelated task, and a
release, lease expiry, handoff, or disconnect can no longer leave a stale
agent edge that refuses a later legitimate wait as a false-positive
deadlock. Wait edges prune on release and on every heartbeat-driven lease
expiry. - Reserve the global agent names
SynapseHub,Synapse, andsystem
case-insensitively so a client cannot impersonate hub or protocol provenance.
Registration is refused before authentication, trust-on-first-use pinning,
takeover, or ownership-lease state changes. Project-scoped identities such as
PROJECT/systemremain valid; clients using a reserved global name must
migrate to a non-reserved or project-scoped identity. - Persist authenticated-frame nonce decisions so a journal-backed hub restart
no longer re-opens the in-memory replay window. Optional compatibility or
strict sequence-floor modes use the same durable boundary; configured stale
sequences, nonce replays, sender mismatches, expired keys, and invalid domains
fail closed. - Bind relay-approval and live-claim quota accounting to server-derived
principals rather than client-selected display names, closing identity-churn
bypasses without weakening project-scoped identities. - Bound accepted durable chat ingress before history or journal growth, using
server-derived principal windows over event count and full normalized frame
bytes; active buckets cannot be reset by reconnect churn. - Journal claim and guard denials as authenticated evidence. A guard that
cannot persist its denial remains fail-closed and does not silently turn an
unrecorded refusal into an allow. - Bound A2A HTTP admission and request-body reads so slow or oversized clients
cannot occupy the bridge indefinitely.
Added
- Board tasks carry an optional project scope and a monotonic version, with an
expected_versioncompare-and-set guard onledger_task/ledger_task_update
and matchingsynapse task declare --project/--expected-versionand
task update --project/--expected-versionflags. A re-declaration with a
conflicting non-empty scope is refused, the guard refuses stale writers, and
both fields journal and replay durably. - Persistent capability registration for automated dispatch: an
advertise
withpersist: trueregisters a project-scoped seat's card so it survives
disconnects (24-hour refresh TTL), withdispatchableopt-out, a structural
-rxsidecar path for wake listeners registering their seat, and additive
persistent/dispatchablekeys in the manifest.synapse wait --capability-card FILEre-registers the card on every (re)connect, so
registration self-heals across re-arms and hub restarts. - Opt-in ready-task dispatcher:
synapse dispatch --project Pcomputes a
deterministic plan from live board/state/manifest/who snapshots, pins
suggested_ownerthrough the version compare-and-set guard, and wakes the
seat's online identity under a stable idempotency key — exactly once, even
across crashes, via an append-only JSONL outbox that reconciles pending
intents on restart. Adispatch:<project>singleton lease serialises
concurrent dispatchers; the dispatcher never claims real tasks, approves,
lands, or broadcasts. - Native Agent Evidence Format (AEF) runtime: restricted JCS canonicalization,
integer time, versioned signature domains, Ed25519 receipt verification,
durable replay/conflict decisions, legacy-event projection, native chained
receipt emission, a crash-safe outbox, and hub/CLI runtime integration. The
verifier distinguishes legacy evidence from native AEF rather than upgrading
old guarantees by presentation. - Durable federation partition/heal lifecycle evidence, including replay after
restart, so a past degraded interval is queryable instead of existing only
as transient operator state.
Changed
- Validation documentation now reports the enforced 98% coverage floor and
the dated 98.85% Codecov measurement separately; it no longer turns the
badge's rounded 99% display into an unsupported "above 99%" claim. - The release workflow now publishes the verified wheel and sdist SHA-256
digests in the GitHub Release notes body itself (a fenced "Artifact checksums
(SHA-256)" section), not only as the attachedSHA256SUMSasset, so a
consumer can confirm own-provenance straight from the release notes. The
digests are the same onessha256sum --check --strict-verified before the
assets are attached. - The identity-and-ACL guide now carries an end-to-end worked example for
recovering a changed machine key through the governed pin reclaim: a
copy-pasteidentity-pin-reclaimACL grant, the fresh-TOFU recovery-operator
recipe that sidesteps the bootstrap paradox, and the exactsynapse identity reclaiminvocation with its compare-and-swap and audit-trail guarantees. - Cockpit and Studio bootstrap configuration is now inert JSON consumed by
same-origin scripts; productionscript-srcstays at'self'without an
executable inline-script exception. - Ordinary CLI and MCP path claims inside Git now resolve the same canonical
worktree identity asgit-claim, while deliberate keyless mutexes and
genuine non-Git scopes retain their previous semantics.
Fixed
- Keep ambient agent-tmux wakes from replacing active user work with an
unconditional stop. The fixed payload-free prompt now gates work on the
configured/current identity and an exact-target inbox item, ignores routine
status and broadcast-only wakes, resumes the active user-directed task when
there is no actionable directed work, and waits only when otherwise idle. - Apply durable claim-family mutations atomically: provisional state remains
private until the SQLite transaction commits, failed persistence restores
the prior state exactly, and post-commit cleanup cannot invert durable/live
truth. The serialized mutation actor moves guarded storage work off the event
loop and drains in-flight work before cancellation propagates. - Carry a bounded
client_msg_idthrough live chat, retained history, durable
replay, mailbox delivery, and receipt frames so at-least-once receivers can
deduplicate reconnect retries without the hub silently dropping attempts. - Reject oversized JSON integer literals as malformed frames instead of letting
Python's integer-conversion limit escape the protocol boundary and close the
client with an internal-error status. - Use the supported asyncio WebSocket server API across the declared
websockets>=13range, restoring real registration plus/healthand
/metricson both the dependency floor and current releases. - Exit
synapse listennon-zero when the hub closes the connection instead of
leaving a dead listener process apparently alive. - Keep container health probes aligned with the concrete Host authority on a
wildcard bind; both Docker's health check and the compose CI smoke use
ws://127.0.0.1:8876.
Artifact checksums (SHA-256)
f4a6e5bd17e3081ee004df22a78727a40c87610e5e81aafc558d9609e567d44c synapse_channel-0.99.12-py3-none-any.whl
089e540242a2ad713ae81d345c04bb527fb8206e988a36e32324a411f5e20f6c synapse_channel-0.99.12.tar.gz
2bbbb2ade980a8c78f37afbb9aabe493792d42d8f6a8bbe2c12c54930d540649 synapse-channel-v0.99.12-sbom.cdx.json