Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Hive did not historically maintain a complete changelog. This file starts a prag

### Security

- The Claude Code **Remote Control** bridge (session publishing to claude.ai/code) is now pinned OFF at every claude-CLI launch ([#5607](https://github.com/kubestellar/hive/issues/5607)). Since Claude Code ~2.1.226 the bridge auto-starts whenever no explicit `remoteControlAtStartup` value exists — the CLI falls through to a server-side rollout flag — so an image upgrade flipped it on fleet-wide with no local change, and the in-pane toggle is session-scoped so every relaunch brought it back. On a hive this exposed every agent (launched with `--dangerously-skip-permissions`) as a remote-controllable session under the one shared claude.ai account. Hive now merges `"remoteControlAtStartup": false` into the shared `/data/home/.claude/settings.json` for the plain claude backend, and into the inference settings seed for gateway agents, re-asserted on every launch. The merge is **add-if-missing only**: an operator who wants Remote Control sets the key to `true` in the same file and hive never clobbers it, with an Info receipt logged so an enabled bridge is always explainable. A launch-time check also warns when an agent's session file records `hasUsedRemoteControl` despite the pin.
- `hive-snapshot.service` no longer executes code out of a path an unprivileged local user could have planted ([#5483](https://github.com/kubestellar/hive/issues/5483)). This is the same class as [#5435](https://github.com/kubestellar/hive/issues/5435) and the last remaining unit in `systemd/` that ran an `ExecStart` out of the checkout: `ExecStart=/tmp/hive/dashboard/publish-snapshot.sh` resolved the executed script out of a world-writable parent that is cleared on every reboot, so a local user who created `/tmp/hive/dashboard/publish-snapshot.sh` before `hive-deploy` repopulated the checkout got code execution as the service user. Severity is lower than #5435 — the unit is `Type=oneshot` driven by `hive-snapshot.timer` rather than `Restart=always`, so the planted code runs on the timer's 15-minute schedule rather than immediately at boot, and the unit carries no `EnvironmentFile`, so systemd hands it no credential — but it is not zero: the script itself reads a GitHub App token from `/var/run/hive-metrics/gh-app-token.cache` and pushes to `kubestellar/docs`. The unit now runs `bin/hive-checkout-guard.sh` as an `ExecStartPre`, unchanged from #5481, and refuses to start unless every directory from `/` down to the checkout is owned by the service user or root and not writable by anyone else (a sticky ancestor such as `/tmp` is accepted, the leaf is not), and unless `publish-snapshot.sh` exists, is a regular file rather than a symlink, and is not group- or world-writable — the file ships mode 755, so it is the write bits rather than the exec bit that matter here. `NoNewPrivileges`, `ProtectSystem=full`, `PrivateDevices` and `RestrictSUIDSGID` are set alongside it. **The checkout stays at `/tmp/hive` and no path changes**, so existing hosts keep working. `PrivateTmp` is deliberately *not* set and `ProtectSystem` is deliberately not `strict` — either would hide or freeze both the checkout and the `/tmp/kubestellar-docs-snapshot` clone the script commits from; the contract test pins both as absent. `ProtectHome` is deliberately *not* `read-only` here, unlike `hive-discord.service`, because this script drives `git` and `gh`, which write under `$HOME`; that difference is pinned by the test as well so a later consistency pass cannot silently break the snapshot push.

## 2026-09-01 (v4.0.2)
Expand Down
1 change: 1 addition & 0 deletions src/docs/security-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Agents are unprivileged, separated, and policed at the network layer:
- **Per-agent Unix UIDs.** Each agent runs as its own user (UIDs allocated from base 2001) via `su-exec`, with its own tmux server on a per-agent socket. One agent cannot attach to another's session or signal its processes. `su-exec` is mode `4750 root:hive-launch`, so no agent UID can re-exec it to become root.
- **Per-agent GitHub tokens, not shared.** Each agent's scoped token is delivered to a per-agent cache file that is not world-readable: the entrypoint pre-creates it owned `dev:hive-<agent>` with mode `0640`, so only that agent's group can read it, and the locally-minted token path additionally writes `0600` and `chown`s to the agent's UID (deleting the file rather than leaving it shared if the chown fails). Agents cannot read each other's GitHub tokens.
- **Per-agent tool denylists.** Declarative per-agent tool rules in `hive.yaml` become hard CLI flags at launch (`--disallowed-tools` for Claude, `--deny-tool` for Copilot). Separately, GitHub MCP *write* tools are denied for **every** agent in **every** mode — the MCP layer never grants agents a GitHub write path regardless of autonomy level; GitHub writes are instead governed by the token tier and the policy proxy below.
- **Claude Remote Control is pinned off.** Since Claude Code ~2.1.226 the Remote Control bridge (session publishing to claude.ai/code) auto-starts whenever no explicit `remoteControlAtStartup` value exists — a server-side rollout flag decides, so a routine image upgrade could expose every agent as a remote-controllable session under the shared claude.ai account. Hive writes a durable `"remoteControlAtStartup": false`, re-asserted at every claude-CLI launch: merged into the shared `/data/home/.claude/settings.json` for the plain claude backend, and into the per-agent settings seed for inference-gateway agents. The merge is add-if-missing only — an operator who wants the bridge sets the key to `true` in the same file and hive never clobbers it, logging an Info receipt so an enabled bridge is always explainable — and a launch-time check warns if an agent's session file records `hasUsedRemoteControl` despite the pin.
- **A default-on GitHub policy proxy.** Every agent's `HTTPS_PROXY` points at an in-pod proxy that intercepts **GitHub API traffic** and enforces, deterministically, what the agent's current ACMM mode allows: REST method/path rules, GraphQL query-vs-mutation gating, and a **repository allowlist** — writes to any repo outside the hive's configured list are blocked with `403` (`X-Hive-Proxy-Blocked`) at the network layer, regardless of what the agent was prompted (or prompt-injected) to do. The proxy MITM-inspects only `api.github.com`; other destinations are tunneled without inspection. The same proxy attributes traffic to agents by UID (read from `/proc/net/tcp`, unforgeable) for token accounting.

## Layer 5 — GitHub blast-radius controls
Expand Down
Loading