diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index 9ccdf24d..c6339f52 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -116,7 +116,7 @@ optionally to rooms: ```bash hivectl matrix invite mara -hivectl matrix invite @mara:yourserver --room '#hive-chat:yourserver' +hivectl matrix invite @mara:yourserver --room '#some-room:yourserver' ``` ## 6 · Your first agent diff --git a/docs/integrations/matrix.md b/docs/integrations/matrix.md index 2f60f311..d54e728d 100644 --- a/docs/integrations/matrix.md +++ b/docs/integrations/matrix.md @@ -97,7 +97,7 @@ through the gateway; from outside the host that takes `gateway.openFirewall`. hive-c0re provisions matrix (its own sender login, the hive Space and -chat room, invites) through `swarm.matrix.apiUrl`, which defaults to +invites) through `swarm.matrix.apiUrl`, which defaults to `https://` on every hive. That login carries the hive's matrix access token, and on a hive that doesn't run the homeserver it crosses the network, so the default is TLS; hive-c0re @@ -259,9 +259,8 @@ authenticates as — an account the swarm's appservice creates — rather than any privilege level, because it carries none. It needs no promotion for what the hive does with it. Creating the hive -Space and the chat room, writing their hierarchy and join rules, and -inviting agents into them are all ordinary client calls that ride on -being the rooms' own creator at power level 100 — there is no homeserver +Space and inviting agents into it are ordinary client calls that ride on +being the Space's own creator at power level 100 — there is no homeserver admin in any of it, and no Synapse admin API to reach for either, since tuwunel has none. @@ -355,28 +354,19 @@ into it. This gives the operator a single Space in FluffyChat or any Matrix client that groups all agent-to-agent + operator rooms in one place. - -The sweep also provisions a default **`hive-chat` room** as an -`m.space.child` of the Space. Joining a Space doesn't autojoin -child rooms — the explicit room entry ensures the operator and every -agent can find a common chat room without manual setup. Room join is -restricted (any Space member including the operator can join; agents -are explicitly invited). Room version pinned to 10 for the restricted -join floor. - +**State**: hive-c0re persists the Space's room ID to +`/var/lib/hyperhive/matrix/space-room-id` (mode `0600`, owned by the +hive-c0re service user). The path is **outside** every agent state dir and +**isn't** deleted by `nixos-container destroy --purge`, so it survives full +agent purges, and hive-c0re reuses it on re-provision. -**State**: hive-c0re persists both room IDs to `/var/lib/hyperhive/matrix/` -(mode `0600`, owned by the hive-c0re service user): -- `space-room-id` — the Space itself -- `chat-room-id` — the `hive-chat` room +An earlier version also provisioned a `hive-chat` room and kept its id in +`chat-room-id`. The sweep no longer touches either; an existing room and +file are harmless leftovers that can be removed by hand. -These paths are **outside** every agent state dir and **aren't** deleted by -`nixos-container destroy --purge` — both survive full agent purges, and -hive-c0re reuses them on re-provision. - -**Idempotent**: if the files exist and are non-empty, hive-c0re considers the Space and -room already created. Delete the files to force -re-creation (for example after a homeserver wipe). +**Idempotent**: if the file exists and is non-empty, hive-c0re considers the +Space already created. Delete the file to force re-creation (for example +after a homeserver wipe). ## Configuration tuning diff --git a/docs/tools/hivectl-cli.md b/docs/tools/hivectl-cli.md index 85a7ec03..eda3044d 100644 --- a/docs/tools/hivectl-cli.md +++ b/docs/tools/hivectl-cli.md @@ -116,7 +116,7 @@ Always prints the diff first. `--from forge` resets the local checkout to forge matrix-tuwunel invites. -Manual invites into the hive Space and its rooms; c0re's periodic matrix sweep provisions the Space, the chat room and the agents' invites on its own. +Manual invites into the hive Space and its rooms; c0re's periodic matrix sweep provisions the Space and the agents' invites on its own. **Usage:** `hivectl matrix ` diff --git a/docs/tools/hivectl.md b/docs/tools/hivectl.md index a31057ee..491bd03a 100644 --- a/docs/tools/hivectl.md +++ b/docs/tools/hivectl.md @@ -50,7 +50,7 @@ running (`services.hyperhive.deploy.matrix.enable = true`). ```bash hivectl matrix invite mara # invite a user to the hive Space -hivectl matrix invite @mara:server --room '#hive-chat:server' # ...or to a specific room/alias +hivectl matrix invite @mara:server --room '#some-room:server' # ...or to a specific room/alias ``` Human matrix accounts come from SSO, not `hivectl`; matrix homeserver diff --git a/hive-c0re/src/job_queue/exec.rs b/hive-c0re/src/job_queue/exec.rs index bb0c9228..fe4ff0b1 100644 --- a/hive-c0re/src/job_queue/exec.rs +++ b/hive-c0re/src/job_queue/exec.rs @@ -412,8 +412,7 @@ fn matrix_sweep_warning(ctx: sweep_health::SweepFailure) -> String { ); format!( "matrix user/space sweep failing ({} consecutive, {age}) \ - — some agents may be missing matrix accounts, space membership, \ - or chat-room invites", + — some agents may be missing matrix accounts or space membership", ctx.consecutive ) } diff --git a/hive-c0re/src/job_queue/resource.rs b/hive-c0re/src/job_queue/resource.rs index 1c103e74..c2a6b549 100644 --- a/hive-c0re/src/job_queue/resource.rs +++ b/hive-c0re/src/job_queue/resource.rs @@ -32,7 +32,7 @@ pub enum Resource { /// `.git/index.lock` and the remote-tracking ref lock. KnowledgeTree, /// The hive's matrix provisioning, held by every `MatrixSweep`. Two - /// overlapping passes on a hive with no persisted Space / chat-room id both + /// overlapping passes on a hive with no persisted Space id both /// miss the by-name lookup and both `createRoom`, leaving a duplicate room /// with agents invited to both. MatrixSweep, diff --git a/hive-c0re/src/main.rs b/hive-c0re/src/main.rs index 13f037ae..14c8909f 100644 --- a/hive-c0re/src/main.rs +++ b/hive-c0re/src/main.rs @@ -308,7 +308,7 @@ async fn cmd_serve( } }); // Matrix sweep (`matrix::ensure_all`): the hive's own account, the hive - // Space and chat room, and every agent container's invite to both. It + // Space, and every agent container's invite to it. It // creates no agent accounts or tokens — swarm-controller mints those. // No-op when the hive-matrix container isn't running. Re-submitted every // 30 minutes; the startup pass is the boot `MatrixSweep` node. diff --git a/hive-c0re/src/matrix.rs b/hive-c0re/src/matrix.rs index 70b95a96..0e5cc385 100644 --- a/hive-c0re/src/matrix.rs +++ b/hive-c0re/src/matrix.rs @@ -1,5 +1,5 @@ //! Optional matrix-tuwunel wiring: the hive's `@hive-:` sender token, -//! taken from the swarm secret store, and the hive Space, chat room and +//! taken from the swarm secret store, and the hive Space and //! invites provisioned with it. No-op when no homeserver is configured, so //! operators who haven't flipped `services.hyperhive.deploy.matrix.enable = //! true` pay nothing. @@ -60,8 +60,8 @@ fn matrix_base() -> Result<&'static str> { /// [`stored_sender_token`] reads it; hive-c0re never creates it. /// /// An ordinary account, with no homeserver-admin standing: what it -/// provisions — the Space, the chat room, the invites — it provisions as -/// the creator of those rooms. +/// provisions — the Space and the invites — it provisions as the +/// creator of the Space. /// /// # Errors /// When `HYPERHIVE_HIVE_NAME` is unset or empty. That is the same variable @@ -86,16 +86,6 @@ pub fn hive_localpart() -> Result { /// being created on the next sweep. pub const HIVE_SPACE_NAME: &str = "hive"; -/// Display name of the default "hive chat" room — the `m.space.child` of -/// the hive Space that every agent + the operator can join. Plain text so -/// it stays rediscoverable by name (mirrors [`HIVE_SPACE_NAME`]) when the -/// persisted room-id file is lost, preventing duplicate chat rooms. -pub const HIVE_CHAT_ROOM_NAME: &str = "hive-chat"; - -/// Topic for the default hive chat room. -const HIVE_CHAT_ROOM_TOPIC: &str = - "Hive-wide chat for all agents and the operator. Auto-provisioned by hive-c0re."; - /// Host path for the appservice sender account's matrix access token. Outside every /// purgeable path — not deleted by `destroy --purge` on any agent. #[must_use] @@ -110,13 +100,6 @@ pub fn hive_space_room_id_path() -> PathBuf { crate::paths::matrix_space_room_id() } -/// Host path where the default hive chat room ID is persisted. Outside -/// every purgeable path — not deleted by `destroy --purge`. -#[must_use] -pub fn hive_chat_room_id_path() -> PathBuf { - crate::paths::matrix_chat_room_id() -} - /// Whether this hive has a homeserver to provision against. /// /// **A configured API URL, not a local container**: whether the homeserver @@ -408,233 +391,6 @@ pub async fn ensure_hive_space(client: &reqwest::Client, sender_token: &str) -> Ok(room_id) } -/// Persist the hive chat room id to [`hive_chat_room_id_path()`] (0600). -fn persist_chat_room_id(room_id: &str) -> Result<()> { - let path = hive_chat_room_id_path(); - crate::secret_file::write_secret_0600(&path, format!("{room_id}\n").as_bytes()) - .with_context(|| format!("matrix: write chat room_id to {}", path.display())) -} - -/// Scan the admin account's joined rooms for the canonical hive chat room: -/// a non-space room named [`HIVE_CHAT_ROOM_NAME`]. Mirrors -/// [`find_space_by_name`] so a lost room-id file recovers the existing chat -/// room instead of spawning a duplicate. `None` if the homeserver is -/// unreachable or no match exists. -async fn find_chat_room_by_name(client: &reqwest::Client, sender_token: &str) -> Option { - find_joined_room( - client, - matrix_http()?, - sender_token, - false, - HIVE_CHAT_ROOM_NAME, - ) - .await -} - -/// Whether the state event still has to be written. -/// -/// `current` is the room's existing content for this `(type, state_key)`, -/// or `None` when it could not be read. **Unreadable means write**: the -/// re-apply exists so a missing link gets repaired, and one redundant -/// event is cheaper than a hierarchy that never reconverges. -/// -/// Comparison is `serde_json::Value` equality, which compares objects as -/// maps — key order in the response does not matter. -fn state_needs_write(current: Option<&serde_json::Value>, desired: &serde_json::Value) -> bool { - current != Some(desired) -} - -/// Read the room's current content for one state event. `None` on any -/// failure — absent state, transport error, a refusal, or an unparseable body -/// are all "we don't know", and [`state_needs_write`] turns that into a write. -/// -/// A failure other than absent state is logged at `warn`: **a read that breaks -/// while the write still works** makes the guard degrade to writing every -/// time, and the sweep resumes waking the hive with nothing else to show for -/// it. An absent state event (404) is the expected first-setup case. -async fn current_room_state( - client: &reqwest::Client, - base: &str, - sender_token: &str, - room_id: &str, - event_type: &str, - state_key: &str, -) -> Option { - match swarm_matrix_client::room_state( - client, - base, - sender_token, - room_id, - event_type, - state_key, - ) - .await - { - Ok(content) => content, - Err(e) => { - tracing::warn!( - error = %format!("{e:#}"), - %room_id, - event_type, - "matrix: state read failed while writes still work — the \ - skip-if-unchanged guard is off and every sweep will re-emit" - ); - None - } - } -} - -/// PUT a state event into `room_id` using the sender token, **skipping the -/// write when the room already carries identical content**. -/// -/// The read is not an optimisation. A PUT of identical content is a no-op -/// on the room's *state*, and the homeserver still appends an event to the -/// *timeline* — so "idempotent" was true one level too high. Downstream, -/// an event is unread activity, which is a todo, which is a turn: a caller -/// re-applying a link on a periodic sweep wakes every agent in the room on -/// that sweep's cadence, forever. Measured at ~30 minutes per wake per -/// agent before this guard existed. -/// -/// The self-healing property the re-apply exists for is unaffected: a -/// missing or divergent link still gets written. -async fn set_room_state( - client: &reqwest::Client, - sender_token: &str, - room_id: &str, - event_type: &str, - state_key: &str, - content: &serde_json::Value, -) -> Result<()> { - let base = matrix_base()?; - let current = - current_room_state(client, base, sender_token, room_id, event_type, state_key).await; - if !state_needs_write(current.as_ref(), content) { - tracing::debug!( - %room_id, - event_type, - "matrix: state already current, skipping PUT (no timeline event)" - ); - return Ok(()); - } - swarm_matrix_client::put_state( - client, - base, - sender_token, - room_id, - event_type, - state_key, - content, - ) - .await - .context("matrix: set room state") -} - -/// Create (or recover) the default hive chat room and wire it as a child of -/// the hive Space, persisting its room id to [`hive_chat_room_id_path()`]. -/// -/// The room is a normal room (not an `m.space`) named [`HIVE_CHAT_ROOM_NAME`] -/// with a `restricted` join rule allowing any member of the hive Space to -/// join — so the operator (who is in the Space) and every agent can chat -/// without needing an explicit invite. It's linked bidirectionally to the -/// Space: `m.space.child` on the Space points at the room, `m.space.parent` -/// on the room points back. Joining a Space does NOT auto-join its children -/// (Matrix semantics), so this gives clients a concrete room to surface + -/// join instead of an empty Space. -/// -/// Dedup mirrors [`ensure_hive_space`]: persisted id wins, else rediscover -/// by name, else create. The space-child link is re-checked on every call -/// so a recovered room reconverges its hierarchy link — and written only -/// when it differs, see [`set_room_state`]. -/// -/// # Errors -/// -/// Returns an error if the homeserver is unreachable, `createRoom` fails, -/// or the room-ID file cannot be written. A failure wiring the space-child -/// link is logged but not fatal (the room still exists + is joinable). -pub async fn ensure_hive_chat_room( - client: &reqwest::Client, - sender_token: &str, - space_room_id: &str, - server_name: &str, -) -> Result { - let base = matrix_base()?; - // 1. Stored room id wins (fast path). 2. Rediscover by name before - // creating (prevents duplicates after a state wipe). 3. Create. - let room_id = if let Some(id) = std::fs::read_to_string(hive_chat_room_id_path()) - .ok() - .map(|s| s.trim().to_owned()) - .filter(|s| !s.is_empty()) - { - tracing::debug!(room_id = %id, "matrix: hive chat room already provisioned"); - id - } else if let Some(id) = find_chat_room_by_name(client, sender_token).await { - persist_chat_room_id(&id)?; - tracing::info!(room_id = %id, "matrix: recovered hive chat room by name"); - id - } else { - // `initial_state` is applied after the preset-derived state, so the - // restricted join rule overrides private_chat's invite-only default. - let body = serde_json::json!({ - "name": HIVE_CHAT_ROOM_NAME, - "topic": HIVE_CHAT_ROOM_TOPIC, - // Pin the room version explicitly: the `restricted` join rule - // below needs room version >= 8. tuwunel's default is currently - // higher, but pinning makes the dependency explicit so a future - // homeserver-default change can't silently invalidate the - // restricted rule (which would quietly fall back to invite-only - // and break the "operator joins from the Space" path). - "room_version": "10", - "preset": "private_chat", - "visibility": "private", - "initial_state": [ - { - "type": "m.room.join_rules", - "state_key": "", - "content": { - "join_rule": "restricted", - "allow": [ - { "type": "m.room_membership", "room_id": space_room_id } - ] - } - }, - { - "type": "m.space.parent", - "state_key": space_room_id, - "content": { "via": [server_name], "canonical": true } - } - ] - }); - let id = swarm_matrix_client::create_room(client, base, sender_token, &body) - .await - .context("matrix: create hive chat room")?; - persist_chat_room_id(&id)?; - tracing::info!(room_id = %id, "matrix: created hive chat room"); - id - }; - - // Wire the Space → room child link. Without an `m.space.child` carrying - // a `via`, the room won't surface in the Space hierarchy. `suggested` - // hints clients to surface it prominently. - let child_content = serde_json::json!({ - "via": [server_name], - "suggested": true, - }); - if let Err(e) = set_room_state( - client, - sender_token, - space_room_id, - "m.space.child", - &room_id, - &child_content, - ) - .await - { - tracing::warn!(error = ?e, "matrix: set m.space.child on hive space failed"); - } - - Ok(room_id) -} - /// Invite `@{localpart}:{server_name}` to `room_id` using the admin /// account. Idempotent — treats already-member responses as success. async fn invite_to_room( @@ -777,7 +533,7 @@ pub async fn invite_user( } /// Make sure the hive's own `@hive-:` account exists, then provision -/// the hive Space and chat room and invite every agent container to both. +/// the hive Space and invite every agent container to it. /// Agents' own accounts are not created here: `swarm-controller` mints them /// with the swarm's appservice token. Runs only as a `MatrixSweep` job node /// (see `job_queue::templates::matrix_sweep`). No-op when the @@ -804,9 +560,8 @@ pub async fn ensure_all() -> bool { } }; // The hive's `@hive-:` account first, because everything below provisions - // THROUGH it (the Space, the chat room and every invite are sent with - // its token) — as an ordinary user that created those rooms, not as a - // homeserver admin. + // THROUGH it (the Space and every invite are sent with its token) — as + // an ordinary user that created the Space, not as a homeserver admin. if let Err(e) = ensure_hive_user().await { tracing::warn!(error = ?e, "matrix: ensure_hive_user failed"); ok = false; @@ -829,8 +584,8 @@ pub async fn ensure_all() -> bool { ok } -/// Provision the hive Space + default chat room and invite every agent -/// (+ the admin account) to both. Split out of [`ensure_all`] purely to +/// Provision the hive Space and invite every agent (+ the admin account) +/// to it. Split out of [`ensure_all`] purely to /// keep that function under the `too_many_lines` threshold — this is the /// tail half of the same sequential sweep and shares its aggregate-bool, /// log-and-continue failure handling. @@ -878,40 +633,6 @@ async fn provision_space(client: &reqwest::Client, agent_names: &[String]) -> bo ok = false; } } - - // Provision the default hive chat room as an m.space.child of the Space - // and invite `@hive-:` + every agent. Joining a Space alone surfaces no - // rooms to chat in (Matrix semantics — children aren't auto-joined), so - // without this the Space is empty. The restricted join rule additionally - // lets the operator (a Space member) join from the Space hierarchy. - match ensure_hive_chat_room(client, &sender_token, &room_id, &server_name).await { - Ok(chat_room_id) => { - if let Err(e) = invite_to_room( - client, - &sender_token, - &chat_room_id, - &localpart, - &server_name, - ) - .await - { - tracing::warn!(error = ?e, %localpart, "matrix: invite the hive account to the chat room failed"); - ok = false; - } - for name in agent_names { - if let Err(e) = - invite_to_room(client, &sender_token, &chat_room_id, name, &server_name).await - { - tracing::warn!(%name, error = ?e, "matrix: invite agent to chat room failed"); - ok = false; - } - } - } - Err(e) => { - tracing::warn!(error = ?e, "matrix: ensure_hive_chat_room failed"); - ok = false; - } - } ok } @@ -956,43 +677,6 @@ mod tests { assert_eq!(sender_source(None, None), SenderSource::Unavailable); } - /// The steady state. This is the whole point of the guard: the sweep - /// runs forever, and every write it makes is a wake for every agent - /// in the room. - #[test] - fn state_matching_the_room_is_not_rewritten() { - let desired = serde_json::json!({ "via": ["example.org"], "suggested": true }); - let current = desired.clone(); - assert!(!state_needs_write(Some(¤t), &desired)); - } - - /// Key order in the homeserver's response must not force a write — - /// the guard leans on `serde_json::Value` comparing objects as maps, - /// and a byte- or order-sensitive comparison would silently degrade - /// to writing every time while still looking correct. - #[test] - fn state_matching_but_reordered_is_not_rewritten() { - let desired = serde_json::json!({ "via": ["example.org"], "suggested": true }); - let current = serde_json::json!({ "suggested": true, "via": ["example.org"] }); - assert!(!state_needs_write(Some(¤t), &desired)); - } - - #[test] - fn diverged_state_is_rewritten() { - let desired = serde_json::json!({ "via": ["example.org"], "suggested": true }); - let current = serde_json::json!({ "via": ["old.example.org"], "suggested": true }); - assert!(state_needs_write(Some(¤t), &desired)); - } - - /// Fail-open, and deliberately so: an unreadable current state must - /// write. The re-apply exists to repair a missing link, so "we don't - /// know" has to behave like "it's missing", not like "it's fine". - #[test] - fn unknown_state_is_written() { - let desired = serde_json::json!({ "via": ["example.org"], "suggested": true }); - assert!(state_needs_write(None, &desired)); - } - /// A banned target or a sender without power gets a 403; with no /// membership behind it, the invite did not happen. #[test] diff --git a/hive-c0re/src/paths.rs b/hive-c0re/src/paths.rs index bcc5b140..fdc7876d 100644 --- a/hive-c0re/src/paths.rs +++ b/hive-c0re/src/paths.rs @@ -110,13 +110,6 @@ pub fn matrix_space_room_id() -> PathBuf { matrix_dir().join("space-room-id") } -/// `matrix/chat-room-id` — persisted default "hive chat" room id (the -/// `m.space.child` of the hive Space every agent + the operator can join). -#[must_use] -pub fn matrix_chat_room_id() -> PathBuf { - matrix_dir().join("chat-room-id") -} - /// `run/` — runtime maps hive-c0re regenerates on every meta sync. #[must_use] pub fn run_dir() -> PathBuf { diff --git a/hivectl/src/cli.rs b/hivectl/src/cli.rs index c8b62c68..1c1c349f 100644 --- a/hivectl/src/cli.rs +++ b/hivectl/src/cli.rs @@ -39,8 +39,8 @@ pub enum Cmd { /// matrix-tuwunel invites. /// /// Manual invites into the hive Space and its rooms; c0re's periodic - /// matrix sweep provisions the Space, the chat room and the agents' - /// invites on its own. + /// matrix sweep provisions the Space and the agents' invites on + /// its own. Matrix { #[command(subcommand)] cmd: MatrixCmd, diff --git a/nix/host-modules/hive-c0re/environment.nix b/nix/host-modules/hive-c0re/environment.nix index 867245ef..2f32ea48 100644 --- a/nix/host-modules/hive-c0re/environment.nix +++ b/nix/host-modules/hive-c0re/environment.nix @@ -170,7 +170,7 @@ in } // lib.optionalAttrs (config.services.hyperhive.swarm.matrix.apiUrl != null) { # Client-server API base hive-c0re uses to provision matrix (register - # agent users, create the hive space + chat room, invite members). + # agent users, create the hive space, invite members). # Supplied by `services.hyperhive.swarm.matrix.apiUrl`, which defaults to # the same gateway vhost as HIVE_MATRIX_URL above, over https because it # carries the hive's matrix token and may cross the network. Its own diff --git a/nix/host-modules/hive-matrix-service.nix b/nix/host-modules/hive-matrix-service.nix index a2340839..53292fc9 100644 --- a/nix/host-modules/hive-matrix-service.nix +++ b/nix/host-modules/hive-matrix-service.nix @@ -59,8 +59,8 @@ in example = "https://matrix.example.com"; description = '' Client-server API base URL **hive-c0re itself** uses to - provision matrix (register agent users, create the hive space - and chat room, invite members). Distinct from the agent-facing + provision matrix (register agent users, create the hive space, + invite members). Distinct from the agent-facing `services.hyperhive.agent.matrix.url`, which is the gateway vhost handed to each agent's `hive-matrix-daemon`. diff --git a/nix/host-modules/hive-matrix.nix b/nix/host-modules/hive-matrix.nix index c84ac209..0848b498 100644 --- a/nix/host-modules/hive-matrix.nix +++ b/nix/host-modules/hive-matrix.nix @@ -913,8 +913,8 @@ in # the swarm appservice's sender. Its token is read by # matrix-ctl and swarm-controller only. Each `@hive-` # stays an ordinary user, because its hive reads its - # credential; what it does (the Space, the chat room, the - # invites) rides on being the rooms' creator, not on admin. + # credential; what it does (the Space and the invites) + # rides on being the Space's creator, not on admin. # # The registration load creates the sender inside # `Services::start()`, and `admin_execute` runs after that and diff --git a/swarm-matrix-client/README.md b/swarm-matrix-client/README.md index cbb97454..8636de9d 100644 --- a/swarm-matrix-client/README.md +++ b/swarm-matrix-client/README.md @@ -15,7 +15,7 @@ device's token instead of adding a device. the operator names. Neither call is an appservice call, so neither carries the appservice token. -`hive-c0re` provisions the hive Space and chat room with the room calls +`hive-c0re` provisions the hive Space with the room calls (`joined_rooms`, `room_state`, `put_state`, `create_room`, `invite`, `resolve_alias`), and `hive-matrix-mcp` recovers a session's user and device id with `whoami_session`. Each takes the caller's `reqwest::Client`, so the caller diff --git a/swarm-matrix-client/src/lib.rs b/swarm-matrix-client/src/lib.rs index 5a573a0e..8e126330 100644 --- a/swarm-matrix-client/src/lib.rs +++ b/swarm-matrix-client/src/lib.rs @@ -1,7 +1,7 @@ //! The calls against a Matrix homeserver's client-server API shared by the //! swarm and the hive: the appservice calls (register, log in, ask a token who //! it is), the room calls `swarm-controller`'s admin-room commands and -//! `hive-c0re`'s Space and chat-room provisioning need (resolve an alias, list +//! `hive-c0re`'s Space provisioning need (resolve an alias, list //! members and joined rooms, read and set room state, create a room, invite, send //! a message, check an account exists), and the password login and logout //! `swarm-controller` runs for an operator-linked account on any homeserver.