diff --git a/Cargo.lock b/Cargo.lock index 011688b6..105a7fd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "accessory" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87537f9ae7cfa78d5b8ebd1a1db25959f5e737126be4d8eb44a5452fc4b63cde" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "adler2" version = "2.0.1" @@ -385,15 +373,6 @@ dependencies = [ "cipher 0.5.2", ] -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - [[package]] name = "bumpalo" version = "3.20.3" @@ -669,15 +648,6 @@ dependencies = [ "hybrid-array", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher 0.4.4", -] - [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -803,18 +773,6 @@ dependencies = [ "regex", ] -[[package]] -name = "delegate-display" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "der" version = "0.7.10" @@ -969,18 +927,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fancy_constructor" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b19d0e43eae2bfbafe4931b5e79c73fb1a849ca15cd41a761a7b8587f9a1a2" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "fastrand" version = "2.4.1" @@ -1190,19 +1136,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "growable-bloom-filter" version = "2.1.1" @@ -1749,23 +1682,6 @@ dependencies = [ "quote", ] -[[package]] -name = "indexed_db_futures" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43315957678a70eb21fb0d2384fe86dde0d6c859a01e24ce127eb65a0143d28c" -dependencies = [ - "accessory", - "cfg-if", - "delegate-display", - "fancy_constructor", - "js-sys", - "uuid", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "indexmap" version = "2.14.0" @@ -1967,53 +1883,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -[[package]] -name = "macroific" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd" -dependencies = [ - "macroific_attr_parse", - "macroific_core", - "macroific_macro", -] - -[[package]] -name = "macroific_attr_parse" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "macroific_core" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "macroific_macro" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae" -dependencies = [ - "macroific_attr_parse", - "macroific_core", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "maplit" version = "1.0.2" @@ -2113,7 +1982,6 @@ dependencies = [ "language-tags", "matrix-sdk-base", "matrix-sdk-common", - "matrix-sdk-indexeddb", "matrix-sdk-sqlite", "mime", "mime2ext", @@ -2154,7 +2022,6 @@ dependencies = [ "futures-util", "growable-bloom-filter", "matrix-sdk-common", - "matrix-sdk-crypto", "matrix-sdk-store-encryption", "once_cell", "regex", @@ -2187,81 +2054,9 @@ dependencies = [ "tracing", "tracing-subscriber", "wasm-bindgen", - "wasm-bindgen-futures", "web-sys", ] -[[package]] -name = "matrix-sdk-crypto" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61bf6c3195de301c98339283413a4e9d9d63c4f214ef8955147643caab161256" -dependencies = [ - "aes", - "aquamarine", - "as_variant", - "async-trait", - "bs58", - "byteorder", - "cfg-if", - "ctr", - "eyeball", - "futures-core", - "futures-util", - "hkdf", - "hmac", - "itertools 0.14.0", - "js_option", - "matrix-sdk-common", - "pbkdf2", - "rand 0.8.6", - "rmp-serde", - "ruma", - "serde", - "serde_json", - "sha2", - "subtle", - "thiserror 2.0.18", - "time", - "tokio", - "tokio-stream", - "tracing", - "ulid", - "url", - "vodozemac", - "zeroize", -] - -[[package]] -name = "matrix-sdk-indexeddb" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2752015e69b6b56a8df72e52f91a834771259f755eb1c65232b77348ffb16b" -dependencies = [ - "anyhow", - "async-trait", - "base64", - "getrandom 0.2.17", - "gloo-utils", - "hkdf", - "indexed_db_futures", - "js-sys", - "matrix-sdk-crypto", - "matrix-sdk-store-encryption", - "rmp-serde", - "ruma", - "serde", - "serde-wasm-bindgen", - "serde_json", - "sha2", - "thiserror 2.0.18", - "tokio", - "tracing", - "wasm-bindgen", - "web-sys", - "zeroize", -] - [[package]] name = "matrix-sdk-sqlite" version = "0.14.0" @@ -2273,7 +2068,6 @@ dependencies = [ "deadpool-sqlite", "itertools 0.14.0", "matrix-sdk-base", - "matrix-sdk-crypto", "matrix-sdk-store-encryption", "num_cpus", "rmp-serde", @@ -3060,7 +2854,6 @@ dependencies = [ "getrandom 0.2.17", "http", "indexmap", - "js-sys", "js_int", "konst", "percent-encoding", @@ -3319,17 +3112,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - [[package]] name = "serde_bytes" version = "0.11.19" @@ -3683,12 +3465,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", "time-core", - "time-macros", ] [[package]] @@ -3697,16 +3477,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.3" @@ -4031,16 +3801,6 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" -[[package]] -name = "ulid" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" -dependencies = [ - "rand 0.9.4", - "web-time", -] - [[package]] name = "unicase" version = "2.9.0" diff --git a/Cargo.toml b/Cargo.toml index bbcbb2f1..cae6b9d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,5 @@ matrix-sdk = { version = "0.14", default-features = false, features = [ "rustls-tls", "sqlite", "markdown", - "e2e-encryption", ] } futures-util = "0.3" diff --git a/docs/matrix.md b/docs/matrix.md index 7e3dd972..67cb5f5f 100644 --- a/docs/matrix.md +++ b/docs/matrix.md @@ -119,22 +119,8 @@ Initial rollout settings: engage). The absent `yes_i_am_very_very_sure_…_open_registration_…` flag keeps the server closed to anyone without the token. -- `allow_encryption` — server-side E2EE switch, sourced from - `services.hyperhive.matrix.allowEncryption` (**default `false`**, opt-in). - Off by default because on the hive-internal homeserver the operator - already controls the transport; turn it on for encrypted rooms on - external / federated homeservers or to keep contents opaque to the - homeserver admin. **The agent matrix client always supports decryption - regardless of this flag** — it uses the `e2e-encryption` feature of - `matrix-sdk` so it can read encrypted rooms it is invited to even when - this homeserver doesn't permit room encryption. Crypto keys are stored - in the per-agent sqlite store under the state dir and persist across - restarts (lost on `--purge`). `read_room` decrypts via - `room.messages()` — UTD events surface as `event_type = - "m.room.encrypted"` with `body = "[unable to decrypt]"`. - Cross-signing and automatic key backup are not enabled for the first - pass: static bearer-token bot accounts can't bootstrap cross-signing - without MSC3967. +- `allow_encryption = false` per operator call; E2EE re-enabling is + deferred to a follow-up. ## Hive Matrix Space diff --git a/hive-matrix-mcp/src/client.rs b/hive-matrix-mcp/src/client.rs index efe73e29..e5ea8e0a 100644 --- a/hive-matrix-mcp/src/client.rs +++ b/hive-matrix-mcp/src/client.rs @@ -9,13 +9,6 @@ //! UIAA flow already minted the token + user/device, hive-c0re just //! handed us the bearer in a file. matrix-sdk's `restore_session` with //! a constructed `MatrixSession` skips the login flow entirely. -//! -//! E2EE is enabled via `with_encryption_settings(EncryptionSettings::default())`. -//! Crypto keys are persisted in the sqlite store under `state_dir` -//! (survives container restarts, lost on `--purge`). Cross-signing and -//! automatic key backup are deliberately left at their defaults (disabled) -//! for the first pass: bot accounts authenticated with a static bearer token -//! can't bootstrap cross-signing without MSC3967 on the server side. use std::path::Path; @@ -23,7 +16,6 @@ use anyhow::{Context, Result, anyhow}; use matrix_sdk::{ Client, SessionMeta, SessionTokens, authentication::matrix::MatrixSession, - encryption::EncryptionSettings, ruma::{OwnedDeviceId, OwnedUserId}, }; use serde::Deserialize; @@ -116,7 +108,6 @@ pub async fn build_and_restore( let client = Client::builder() .homeserver_url(homeserver) .sqlite_store(state_dir, None) - .with_encryption_settings(EncryptionSettings::default()) .build() .await .with_context(|| format!("build matrix client for {homeserver}"))?; diff --git a/hive-matrix-mcp/src/handlers.rs b/hive-matrix-mcp/src/handlers.rs index cd38835b..23125db7 100644 --- a/hive-matrix-mcp/src/handlers.rs +++ b/hive-matrix-mcp/src/handlers.rs @@ -116,24 +116,6 @@ fn extract_body(event: &matrix_sdk::ruma::events::AnyTimelineEvent) -> String { } } -/// Same as [`extract_body`] but for the `AnySyncTimelineEvent` variant -/// returned by `TimelineEvent::raw()` (the sdk-level decrypted form from -/// `room.messages()`). -fn extract_body_sync(event: &matrix_sdk::ruma::events::AnySyncTimelineEvent) -> String { - use matrix_sdk::ruma::events::{AnySyncMessageLikeEvent, AnySyncTimelineEvent}; - match event { - AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(ev)) => ev - .as_original() - .map_or_else(String::new, |orig| match &orig.content.msgtype { - MessageType::Text(t) => t.body.clone(), - MessageType::Notice(n) => n.body.clone(), - MessageType::Emote(e) => format!("* {}", e.body), - _ => String::new(), - }), - _ => String::new(), - } -} - pub async fn send_message(client: &Client, room_ref: &str, body: &str) -> DaemonResponse { let room = match resolve_room(client, room_ref).await { Ok(r) => r, @@ -426,37 +408,32 @@ pub async fn invite_user(client: &Client, room_ref: &str, user_id: &str) -> Daem } pub async fn read_room(client: &Client, room_ref: &str, limit: Option) -> DaemonResponse { - use matrix_sdk::room::MessagesOptions; + use matrix_sdk::ruma::api::Direction; + use matrix_sdk::ruma::api::client::message::get_message_events; let room = match resolve_room(client, room_ref).await { Ok(r) => r, Err(e) => return e, }; - let limit_val = u32::try_from(limit.unwrap_or(50).min(200)).unwrap_or(50); - let mut opts = MessagesOptions::backward(); - opts.limit = matrix_sdk::ruma::UInt::from(limit_val); - // room.messages() transparently decrypts events in encrypted rooms. - // UTD (unable to decrypt) events surface via `ev.kind.is_utd()` and - // get a sentinel body so claude knows decryption failed rather than - // seeing the raw encrypted blob. - let msgs = match room.messages(opts).await { - Ok(m) => m, - Err(e) => return DaemonResponse::error(format!("messages: {e}")), + let limit = limit.unwrap_or(50).min(200); + let mut req = + get_message_events::v3::Request::new(room.room_id().to_owned(), Direction::Backward); + req.limit = matrix_sdk::ruma::UInt::try_from(limit as u64) + .unwrap_or(matrix_sdk::ruma::UInt::from(50u32)); + let resp = match client.send(req).await { + Ok(r) => r, + Err(e) => return DaemonResponse::error(format!("get_message_events: {e}")), }; - let events: Vec = msgs + let events: Vec = resp .chunk .iter() - .filter_map(|ev| { - let parsed = ev.raw().deserialize().ok()?; + .filter_map(|raw| { + let parsed = raw.deserialize().ok()?; let event_id = parsed.event_id().to_string(); let sender = parsed.sender().to_string(); let origin_server_ts: i64 = parsed.origin_server_ts().0.into(); let event_type = parsed.event_type().to_string(); - let body = if ev.kind.is_utd() { - "[unable to decrypt]".to_owned() - } else { - extract_body_sync(&parsed) - }; + let body = extract_body(&parsed); Some(TimelineEvent { event_id, sender, diff --git a/nix/modules/hive-matrix.nix b/nix/modules/hive-matrix.nix index ca287a7b..5c8b5ba7 100644 --- a/nix/modules/hive-matrix.nix +++ b/nix/modules/hive-matrix.nix @@ -241,25 +241,6 @@ in ''; }; - allowEncryption = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Server-side switch for matrix end-to-end encryption — sets - tuwunel's `allow_encryption`. Off by default: on the hive-internal - homeserver the operator already controls the transport, so server - E2EE adds key-management overhead (cross-signing, device - verification, undecryptable-message recovery) without a clear - threat-model win for the common single-hive case. Turn on when - agents join encrypted rooms on external / federated homeservers, - or when the operator wants message contents opaque to the - homeserver admin. Independent of the agent matrix client, which - always supports decryption so it can read encrypted rooms it is - invited to regardless of this flag; this option only governs - whether THIS homeserver permits room encryption. - ''; - }; - gui = { enable = lib.mkOption { type = lib.types.bool; @@ -422,9 +403,8 @@ in # LoadCredential below copies the host file into a # 0400 dynamic-user-owned path; tuwunel reads from there. registration_token_file = "/run/credentials/tuwunel.service/registration_token"; - # Server-side E2EE is opt-in (default off); the agent matrix - # client always supports decryption regardless. - allow_encryption = cfg.allowEncryption; + # E2EE disabled in initial rollout; tracked in the issue tracker. + allow_encryption = false; # Tuwunel's default suffix is " 💕" — suppress it so agent # display names are clean (just the agent name, no emoji). new_user_displayname_suffix = "";