You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Cross-hub operator relay — synapse federation relay release relays a governed
operator action to a peer hub over the existing federation transport, the first
being a force-release of a stuck lease the peer holds. The peer authorises the
relay deny-by-default (mutual TLS + the peering's bounded scope granting the
action's verb in the namespace + the peer must own the namespace) and refuses an
unverified peer or an unregistered action fail-closed. An applied release is
journalled twice on the acting hub: a standard release for state reconstruction
and a new audit-only operator_relay event carrying the cross-hub provenance a
release never records — the verified peer, the asserting operator and origin hub,
and the previous holder — and the hub's own agents are told the lease was revoked.
New core.operator_relay (deny-by-default policy + relayable-action registry), core.operator_relay_wire, core.operator_relay_transport, and a serving handler,
plus SynapseState.force_release. Relayable actions are an explicit allowlist, so a
new cross-hub capability is a deliberate registry entry, never an accident of the wire.
Origin-side routing for the cross-hub operator relay — an operator can target their
own hub, and a hub configured with a relay-peer route to the namespace's owner
forwards the relay on their behalf and relays the verdict back, so the operator never
needs the owning hub's credentials (the origin-side counterpart of claim forwarding).
The relay is now audited on both hubs: the origin hub records an outbound operator_relay event (a new direction field distinguishes it from the owning hub's
inbound one) naming the requester and the destination owner, and stamps its own id as
the forwarded request's origin so the owner attributes the relay to the hub that
relayed it, never a value the requester asserted. Routing is deny-by-default: a relay
for a namespace the hub neither owns nor has a route to is refused fail-closed. New core.operator_relay_routing (pure route resolution) and core.operator_relay_forwarding
(the origin-side gate), plus SynapseHub(relay_peers=…, relay_forwarder=…), a route map
kept separate from the claim-forwarding peers because relaying a force-release is more
privileged than forwarding a claim.
TPM 2.0 hardware key-encryption-key backend for at-rest wrapped keys (optional synapse-channel[tpm2] extra). A decrypt-only RSA-2048 key-encryption key is derived
from the device's storage seed and a fixed template — the identical key every process,
so no handle is persisted — and wraps and unwraps the data key with RSA-OAEP; the RSA
private key is generated inside the TPM and never leaves it. synapse encrypt-key generate-wrapped-tpm2 writes such a file (--tcti / TPM2_TCTI,
defaulting to the in-kernel resource manager device:/dev/tpmrm0), recording only the
template version — never a device path. New core.at_rest_tpm2
(Tpm2KeyEncryptionKey, generate_wrapped_key_file_tpm2, cipher_from_wrapped_key_file_tpm2) implementing the KeyEncryptionKey protocol over
the same wrapped-key file format. CI installs swtpm so the backend is exercised, not
skipped. This completes the pluggable hardware backend family (passphrase, PKCS#11, TPM).
PKCS#11 hardware key-encryption-key backend for at-rest wrapped keys (optional synapse-channel[pkcs11] extra). A key-encryption key held on a PKCS#11 token — a
YubiKey PIV, a cloud or network HSM, or SoftHSM for tests — wraps and unwraps the
data key on the device via RFC 3394 AES key wrap (C_WrapKey / C_UnwrapKey), so
the token key never leaves the hardware. synapse encrypt-key generate-wrapped-pkcs11
writes such a file (--pkcs11-module / PKCS11_MODULE, --token-label, --key-label, --no-create-kek; PIN from PKCS11_PIN or a prompt), recording only
the token and key labels — never the PIN or module path. New core.at_rest_pkcs11
(Pkcs11KeyEncryptionKey, generate_wrapped_key_file_pkcs11, cipher_from_wrapped_key_file_pkcs11) implementing the KeyEncryptionKey protocol
over the same wrapped-key file format. CI installs SoftHSM2 so the backend is
exercised, not skipped.
Envelope-encrypted (KEK-wrapped) at-rest key files with a pluggable
key-encryption-key backend, the foundation for hardware-backed keys (PKCS#11 /
TPM / YubiKey / cloud HSM). A random data key does the bulk AES-GCM while a
key-encryption key wraps it with RFC 3394 AES-KW; the wrapped-key file records
which backend produced it (passphrase-scrypt today, hardware backends as
optional extras next) so a fresh process rebuilds the matching key. synapse encrypt-key generate-wrapped writes one and synapse encrypt-key rewrap rotates
its passphrase without re-encrypting any data, because only the
key-encryption key changes and the data key underneath is unchanged. New KeyEncryptionKey protocol and PassphraseKeyEncryptionKey, wrap_data_key / unwrap_data_key / generate_wrapped_key_file / rewrap_wrapped_key_file, and AtRestCipher.from_wrapped_key_file in core.at_rest. The optional PKCS#11 / TPM
/ YubiKey key-encryption-key backends implement the same protocol and plug into
this same wrapped-key format.
synapse auto-action gives the opt-in auto-action reactor a discoverable CLI
surface. The reactor (which turns the session advisor's per-round signals into
automatic compact/log/handover actions) was previously reachable only in-process
through react_to_advice, so an operator could not see what it does. The command
prints the signal-to-action map, the signals that deliberately map to no action,
and — with --arm/--all — a preview of a policy's armed posture. It reads the
static model only (starts nothing, fires nothing), and states honestly that
arming happens in the orchestration loop, not through a hub-side toggle. New
read-only describe_auto_actions/auto_action_report_to_json/ render_auto_action_report in participants.auto_action.
synapse encrypt-key generate --from-passphrase derives the at-rest key from a
passphrase (prompted twice) via scrypt instead of random bytes, with the scrypt
cost tunable through --scrypt-n (a power of two), --scrypt-r, and --scrypt-p
for a security/performance trade-off. A fresh random salt is drawn per derivation
and discarded — the written file is a normal owner-only 32-byte key of record,
protected exactly like a random one, and the passphrase alone cannot reconstruct
it. The default remains a random key; the passphrase path (and its scrypt
parameters, previously reachable only through the AtRestCipher.from_passphrase
library API) is now exposed on the CLI. New generate_key_file_from_passphrase.
Security
AtRestCipher now enforces the AES-GCM per-key safety bound for random 96-bit
nonces. It counts the messages it seals (exposed as encrypted_count), logs a
one-time rekey warning once it passes fifteen-sixteenths of the GCM_MESSAGE_LIMIT
(2**32), and raises the new AtRestKeyExhausted rather than encrypt past it —
so a key is rotated before the nonce-collision probability can rise past the 2**-32 bound. The count is per cipher instance and resets when the cipher is
rebuilt, guarding a single long-running process rather than a key's cumulative
lifetime across restarts.