Security Policy
How to report a vulnerability, which versions get fixes, and how Hive protects you.
Reporting a vulnerability
If you discover a security vulnerability in Hive, please report it privately through GitHub Security Advisories.
Please do not open a public issue for security vulnerabilities. Private reports give us time to investigate, reproduce, and address the issue before details are disclosed publicly.
We acknowledge reports within three working days, agree on a fix and a disclosure date with you, and credit you in the advisory unless you prefer otherwise. We aim to release a fix within 30 days of a confirmed report, sooner for issues under active exploitation.
When reporting a vulnerability, please include enough information for us to reproduce the issue, including relevant versions (hive version --json), configuration, reproduction steps, and any potential impact.
Supported versions
| Version | Supported |
|---|---|
| Latest 1.x minor release | ✅ fixes and security fixes |
| The 1.x minor release before it | ✅ security fixes |
| Older releases, pre-1.0 releases | ❌ upgrade with hive update |
Within a minor version, only its latest patch release is supported (during the 1.0 train, the latest v1.0.x): a fix ships as a new patch, and hive update moves you to it. Release candidates (v1.0.0-rc.N, the preview channel) get fixes by the next candidate. Previews (currently the desktop app) are fixed in their next build. GOVERNANCE.md has the support and deprecation policy.
Threat model and reviews
docs/threat-model.md describes what Hive protects, from whom, its trust boundaries and what it does not try to do. Plugin isolation has limits it spells out. docs/security-review.md records the last review against it: what was checked, what was found and fixed, and what is still open.
How Hive protects you
Hive runs coding agents as you, with your permissions. It does not sandbox them: an agent can do whatever you can. What Hive does is make sure nobody else can drive your agents, and that what it installs is what was released.
The daemon and its shims accept only you.
- Every socket lives in a directory only you can enter (0700) and is itself 0600.
- Each connection’s peer is checked by the kernel’s credentials:
SO_PEERCREDon Linux,LOCAL_PEERCRED(getpeereid) on macOS. A connection from another user is refused and logged, even if a socket path were reachable some other way. - The daemon listens on no network port. The exception is local discovery, when you turn it on (
discovery.advertise): then the daemon answers mDNS questions on UDP 5353 with an announcement, and does nothing else on that port.
Remote machines are reached only through your own ssh.
hive --remoteand saved machines runssh <host> hive proxy, so authentication, host keys,~/.ssh/configand agent forwarding are ssh’s, as you set them up.- The remote daemon is as private as the local one.
- Discovering a machine grants nothing. An announcement has a name, an identity ID, the ssh port and the API level. It has no user names, sessions, agents or credentials. Pairing connects over ssh, and has the machine sign a fresh challenge with its identity key (ed25519, kept 0600 in
identity.json). Pairing refuses a machine that cannot prove the identity it announced, and saves a machine only after you confirm. A paired machine that later proves another identity is not used until you accept the new one (hive machine trust). An announcement never moves a paired machine to a new address unless the machine proves its identity there. See docs/discovery.md.
Observers read, and only what their credential allows.
- The observer socket (
observer.sock) offers only read-only methods, after authentication with an observer token. There is no method there that types into a terminal, changes a layout or starts anything. - A credential is scoped to the whole session or to listed environments and panes, may or may not read screens, and always expires. Revoking it ends its connections at once. The daemon stores only a hash of each token; tokens are never logged, and every credential made, connection, refusal and out-of-scope request is written to
observers/audit.log. - What observers receive leaves out environment variables, command lines, working directories, PIDs and agents’ session IDs. Screens, when allowed, show whatever is on them, secrets included.
- This is access control in Hive’s API, not isolation by the operating system: the observer socket is as private as the others (only you can connect), and a program running as you could still open the control socket. To hold an observer to its token, run it somewhere else and let it in only through an ssh key restricted to the observer socket (
command="hive proxy --observer",restrictinauthorized_keys). See docs/observers.md.
The web observer shows, and only what you share.
hive web serveis a process of its own that reaches daemons only through their observer sockets, with credentials you make for it (hive web runtime add); it has no way to type, change layouts or start anything, and neither do its pages.- Browsers sign in with one-time links whose code travels in the URL fragment (never sent to a server or logged) and is exchanged for an HttpOnly, SameSite=Strict cookie. Links and access expire;
hive web revokesigns browsers out at once. Only hashes of codes and cookies are stored. - It refuses to serve beyond this machine without TLS (its own, or a proxy’s). Pages load nothing from elsewhere (Content-Security-Policy), are never cached, cannot be framed, and keep nothing in the browser but the theme.
- Screens and output history are shown only when both the runtime and the link allow them; history is read on request and audited. The multiplexer shows when someone observes, and which screens are watched. See docs/web.md.
- Notifications (Web Push) go from the web observer straight to the browser’s push service, signed with its own key and encrypted for the device, so the push service cannot read them; no Hive service relays them. They name an agent at most (or nothing, if you choose), never screen contents, only for agents the link can see, and stop when the link is revoked or expires. The installed app caches only its own code, never pages or screens. See docs/mobile.md.
The desktop app has the CLI’s trust, and no more reach.
- The app (preview, docs/desktop.md) talks to the daemon over its local socket, as
hivedoes. It opens no network port and sends no telemetry. - Its webview loads only the app’s own code: a Content-Security-Policy allows no remote scripts, styles, frames or connections.
- The page can call one Go service of typed methods. Each method checks its input before it reaches the daemon, and none of them runs commands or reads files.
- Links open in the browser, and only
http,httpsandmailtolinks open. - The page is never given environment variables, command lines or arguments, and keeps no terminal contents in storage.
- A pasted text cannot end its own bracketed paste.
- Images that agents print are decoded and encoded again as PNG in Go, within the daemon’s limits, before the webview shows them. The webview never decodes an agent’s bytes as they were sent.
- An observer window gets an observer credential of its own (scoped and expiring), reads only through the observer socket, and the control service refuses its calls. Closing the window revokes the credential.
- The app’s test build (
-tags server) serves the app over HTTP without authentication. It listens on localhost and is never released.
Installing hive elsewhere, plugins and updates need your yes, and updates are verified.
hive updatedownloads the release, checks the archive againstchecksums.txt, and checkschecksums.txtagainst its ed25519 signature (checksums.txt.sig) with the release key built into your binary. An unsigned or wrongly signed release is not installed. Builds from source carry no release key and check only the checksum (they say so).- An update that cannot run is not kept: a new binary that does not start is removed before it is installed, and if the new daemon fails to start,
hive updateputs the previous binary back and restarts the daemons on it.hive update --rollbackgoes back by hand. See docs/upgrading.md. install.shandinstall.ps1checkchecksums.txtagainst the release key’s signature in OpenSSH’s format (checksums.txt.sshsig, withssh-keygen -Y verify), then the archive against it. A signature that does not verify stops the install; a missingssh-keygenstops it too, unlessHIVE_ALLOW_UNVERIFIED=1accepts the checksum alone. See docs/upgrading.md.hive version --verifychecks that the running binary is byte for byte the signed release it claims to be.hive --remoteinstalls or updates hive on another machine only after a yes/no question (or--yes).hive plugin installshows everything a plugin will run, and when, before asking. The commit installed is recorded. Plugins run as you and are not sandboxed: install only plugins you trust, from a tag you have read.- A registry release is pinned to a commit and an archive checksum, both checked before installing. The registry can revoke a release (a compromised one, say): Hive never installs or updates to it,
hive plugin updatemoves off it, andhive plugin doctorreports it. See docs/plugins/ecosystem.md. - Agent detection manifests are built into the binary or read from your own
~/.config/hive/agent-detection. Hive downloads none.
What agents print is kept private.
- Output logs, snapshots and pane history are written 0600 in 0700 directories.
- Pane history replay (
session.replay_history), which keeps screen text, is off by default. Turning it off deletes what it kept. - Metadata tokens are kept in memory only.
- Programs in panes may set your clipboard (OSC 52), as in most terminals, but never read it: Hive drops clipboard queries.
Agents start clean.
- Panes do not inherit the variables of the terminal or the coding agent that started the daemon (
TMUX,CLAUDECODE,CODEX_SANDBOX*,HIVE_*, …). - Hive sets only its own variables in them.
The code is checked.
- CI runs
gosec(with the other linters), the race detector, goroutine-leak checks, and fuzzers on every parser of untrusted input: the wire protocol, frames, configuration, Kitty graphics, discovery messages, and agent and plugin manifests. govulncheckchecks both Go modules for known vulnerabilities the code can reach, on every change and every night, andnpm auditchecks the desktop app’s frontend. Release builds use the Go toolchaingo.modnames, kept on the latest patch. Dependabot proposes dependency updates.- A nightly run fuzzes each of those parsers for five hours, runs a chaos soak (daemon and shims killed, the network cut) to check that faults never lose or corrupt state, and upgrades from every earlier 1.x release and back.
Release signing
Releases are built by the release workflow, only after the release gate has seen CI, the nightly checks and (for minor releases) the 72-hour chaos soak pass on the tagged commit. The workflow signs checksums.txt with the project’s ed25519 key (tools/release-sign), as checksums.txt.sig and in OpenSSH’s format as checksums.txt.sshsig, attaches an SBOM per archive, and records a build provenance attestation for every archive. Builds are reproducible. To verify a download by hand:
go run ./tools/release-sign verify <public key> checksums.txtgh attestation verify <archive> -R admirable-oss/hiveThe public key is published with each release, built into every release binary (buildinfo.releaseKey) and embedded in the installers. docs/releasing.md describes the whole process.