From c5cd8f2ac43b17c61c27872bb6119a09c1dd61df Mon Sep 17 00:00:00 2001 From: atlas Date: Tue, 4 Aug 2026 00:29:58 +0200 Subject: [PATCH] docs: state the unlink mechanism precisely (write bit, not sticky bit) Both the gateway doc and the tmpfiles comment said "a directory without the sticky bit lets any user unlink files in it". True of the old 0777, but it names the wrong lever: write permission on a directory is what confers the right to unlink its entries, and the sticky bit is only a restraint on that -- it was never set here, so it is not what 0751 changes. Dropping o=w removes the permission outright. The fix is unchanged; this is so a future reader doesn't go looking for a sticky bit that was never there. Caught in review by argus. --- docs/gateway.md | 9 ++++++--- hive-priv/src/main.rs | 14 +++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/gateway.md b/docs/gateway.md index f486c51d..38d86296 100644 --- a/docs/gateway.md +++ b/docs/gateway.md @@ -107,9 +107,12 @@ now set unconditionally for every agent. The mechanism: Do **not** reintroduce a chown here: tmpfiles re-applies this entry on every boot *and* every agent spawn/destroy, so any ownership set afterwards is reverted the next time any agent changes. The mode is - also load-bearing — a directory without the sticky bit lets any user - unlink files in it, so a world-writable socket dir would let anything - that can reach the path replace an agent's socket with its own. + also load-bearing — write permission on a *directory* is what confers + the right to unlink its entries, whoever owns them, and the sticky bit + is the only thing that would restrain that (it is not set here). So a + world-writable socket dir would let anything able to reach the path + replace an agent's socket with its own; `o=--x` removes that + permission outright rather than qualifying it. 3. **Marker gate**. After successful `bind_unix`, the harness drops `/hyperhive-socket-bound` next to the socket. c0re's `agent_sockets::write` filters its JSON map by marker presence — diff --git a/hive-priv/src/main.rs b/hive-priv/src/main.rs index 27312cb4..fb9b8ee7 100644 --- a/hive-priv/src/main.rs +++ b/hive-priv/src/main.rs @@ -2430,11 +2430,15 @@ async fn sync_agent_tmpfiles(agents: &[AgentTmpfilesEntry]) -> Result<(String, S // (dials web.sock, and has all of /run/hive-agent bind-mounted in). // Both sockets are 0666, so traversal is all they need. // - // 0751 rather than the historical 0777 is a fix, not a tidy-up: a - // directory without the sticky bit lets *any* user unlink files in it, - // so world-writable here means anything that can reach the path could - // delete an agent's socket, bind its own, and receive that agent's - // todos. Declaring the owner here also ends the tug-of-war with the + // 0751 rather than the historical 0777 is a fix, not a tidy-up: + // write permission on a *directory* is what confers the right to + // unlink its entries, whoever owns them — the sticky bit is the only + // thing that would restrain that, and it was never set here. So the + // old world-writable mode let anything able to reach the path delete + // an agent's socket, bind its own, and receive that agent's todos. + // Dropping `o=w` removes that permission outright rather than + // qualifying it. Declaring the owner here also ends the tug-of-war + // with the // old ChownSocketDir: `d` re-applies on every sync, so a chown made // afterwards was reset by the next agent's spawn. if let (Some(uid), Some(gid)) = (entry.uid, entry.gid) {