homebrew-tap

kavilo-hive — operator guide

This is the server guide. If you’re an end user driving the kavilo-hive CLI against a hub someone else runs, see user-guide.md.

What you’re running

kavilo-hived is a single Go binary that hosts one or more isolated hives under /h/<hive-ref> on the same HTTPS listener. It does:

The server never sees plaintext message content. The Ciphertext field on every relayed envelope is opaque bytes — the Rust client encrypts with libsignal before signing the envelope.

For protocol-level detail and the threat model, read docs/adr/0001-signal-level-e2e.md in the source repo.

Install

docker pull ghcr.io/kavilo-bot/kavilo-hived:0.1.2

Multi-arch (linux/amd64, linux/arm64), cosign-keyless signed under the homebrew-tap workflow’s OIDC identity, with an in-toto SPDX SBOM attestation. Verify before deploying:

cosign verify \
  --certificate-identity-regexp '^https://github.com/kavilo-bot/homebrew-tap/.github/workflows/build-kavilo-hive-release.yml@.*$' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  ghcr.io/kavilo-bot/kavilo-hived:0.1.2

The image ships both /app/kavilo-hived (the server) and /app/kavilo-cert-tool (the sealed-sender certificate minter the server shells out to). /app is on $PATH.

Direct tarball

Versioned assets on the Releases page under the kavilo-hive-vX.Y.Z tag:

KH_VER=0.1.2
curl -fsSL -o /tmp/kavilo-hived.tgz \
  https://github.com/kavilo-bot/homebrew-tap/releases/download/kavilo-hive-v${KH_VER}/kavilo-hived_${KH_VER}_linux_amd64.tar.gz
sudo tar -xzf /tmp/kavilo-hived.tgz -C /usr/local/bin/ kavilo-hived

The tarballs contain just the binary; you’ll want kavilo-cert-tool alongside it for sealed sender (build it from source or pull from the Docker image). The Docker route is easier.

From source

git clone https://github.com/kavilo-bot/kavilo-hive
cd kavilo-hive
go build -trimpath -ldflags="-s -w" -o /usr/local/bin/kavilo-hived ./cmd/kavilo-hived

Quick start

kavilo-hived keeps all state under ~/.kavilo-hive (the managed home directory). Three bootstrap commands followed by serve:

kavilo-hived admin init -password "<admin-password>"
kavilo-hived hub init
kavilo-hived hive create -name "Team Alpha"
kavilo-hived serve

That gets you:

kavilo-hived health runs a TLS + admin-API round-trip; add `-hive

` for per-hive health. ## Docker compose The source repo ships a `docker-compose.yml` for a complete operator setup. The pattern is: bootstrap inside a one-shot container, then start the long-running `serve`: ```bash docker compose run --rm kavilo-hived admin init -password "" docker compose run --rm kavilo-hived hub init docker compose run --rm kavilo-hived hive create -name "Team Alpha" docker compose up -d ``` The compose file mounts `${HOME}/.kavilo-hive` from the host into `/home/nonroot/.kavilo-hive` in the container — TLS assets, actor metadata, admin state, and the bootstrap-code store all persist on the host. ## Public exposure via kavilo-tunnel If you don't want to give kavilo-hived a directly routable address on the public internet, the compose stack ships an optional [`kavilo-tunnel`](https://github.com/kavilo-bot/homebrew-tap#kavilo-tunnel-client-cli) sidecar that connects to a tunnel edge you (or your operator) runs and forwards a public HTTPS URL to the local hub. End users connect to that URL; kavilo-hived itself only listens on the compose bridge network. The default compose setup uses HTTP on the agent-to-hived hop — kavilo-tunnel's reqwest client uses rustls with bundled webpki-roots and has no flag to trust a private CA for the local upstream, so pointing it at kavilo-hived's self-signed HTTPS listener fails with `HTTP 502 — error sending request for url`. Public-side TLS (Let's Encrypt at the tunnel edge) is terminated regardless, so end-user traffic stays HTTPS; the Signal-protocol end-to-end encryption between clients is unaffected — the tunnel sees the same opaque ciphertext kavilo-hived does. ### Setup 1. Get a `kavilo-tunnel` token and endpoint from your tunnel operator. Drop both into a `.env` file alongside `docker-compose.yml` (template at `.env.example`): ```bash KAVILO_TUNNEL_TOKEN=… KAVILO_TUNNEL_ENDPOINT=https://tunnel.example.com:7777 KAVILO_TUNNEL_NAME=myhive # Default upstream is http://kavilo-hived:8443 — leave unset unless # you're pointing the tunnel at a different in-network service. ``` 2. Bootstrap the hub once (any sane `-public-url` placeholder works on this first pass — we'll fix it in step 4 once we know the actual public URL the tunnel assigns): ```bash docker compose run --rm kavilo-hived admin init \ -password "" \ -public-url "https://placeholder.invalid" docker compose run --rm kavilo-hived hub init docker compose run --rm kavilo-hived hive create -name "Your Hive" ``` 3. Edit `~/.kavilo-hive/config.yaml` and set: ```yaml server: require_tls: false ``` This switches kavilo-hived's listener to plain HTTP on `:8443`. The tunnel agent connects to it cleartext over the compose bridge (a private Docker network on your host); public-side traffic still arrives over Let's Encrypt HTTPS at the tunnel edge. 4. Bring up the stack with the `tunnel` profile and watch the sidecar log for the public URL the operator assigned: ```bash docker compose --profile tunnel up -d docker compose logs -f kavilo-tunnel # … # kavilo-tunnel ready # public URL: https://myhive-./ # forwarding to: http://kavilo-hived:8443 ``` The `` (e.g. `alice-7f0c`) is the tail your tunnel operator assigns to your account; it's stable across reconnects once you've claimed a `--name`. The `` is the operator-configured public hostname. 5. Patch the now-known public URL back into config so claim bundles end users redeem reference the public hostname rather than the placeholder, then restart hived: ```yaml server: public_url: https://myhive-. ``` ```bash docker compose restart kavilo-hived ``` Without the `tunnel` profile, `docker compose up` runs only `kavilo-hived` — the default standalone behaviour is unchanged. The sidecar persists its login state in a named volume (`kavilo-tunnel-state`) so restarts don't re-auth. ### Verifying it works From outside the host: ```bash PUB=https://myhive-. curl -sI "${PUB}/" # → 302 redirect to /admin curl -s "${PUB}/ca.crt" # → kavilo-hived's CA PEM (served unauth) ``` A `HTTP 502 error sending request for url (https://...)` from the tunnel agent means kavilo-tunnel hit the HTTPS upstream — double-check that `require_tls: false` is set in `config.yaml` and that `KAVILO_TUNNEL_URL` (env or compose default) is `http://`, not `https://`. ## The managed home directory ``` ~/.kavilo-hive/ ├── config.yaml # root admin config ├── hub/ │ ├── config.yaml # hub registry │ └── hives//config.yaml # per-hive config ├── tls/ # generated CA + server cert └── registrations/ # short-lived bootstrap codes ``` `config.yaml` is intended to be edited by hand for things the CLI doesn't expose. Restart `serve` after edits. ## Actor onboarding End users need (a) credentials to register and (b) a one-time bootstrap code to claim their long-term key. From the hive admin CLI: ```bash # 1. Create the actor record (slot for someone named "alice"). kavilo-hived hive actor create -hive team-alpha \ -id alice -role user # 2. Mint a one-shot 6-digit bootstrap code (valid for a short window; # re-issue freely if it expires). kavilo-hived hive actor bootstrap -hive team-alpha -id alice # → 123456 ``` Hand `alice` the server URL, the hive ref, her actor id+role, and the 6-digit code. She runs `kavilo-hive config init …` followed by `kavilo-hive claim --code 123456` and is now wired up. See the [user guide](/homebrew-tap/docs/kavilo-hive/user-guide.html) for the client side. For bot actors (CI, automation), use `-role bot`. Bot creds skip the interactive REPL but otherwise behave identically. ## Hub admin vs hive admin | | Hub admin | Hive admin | |---|---|---| | Path | `/admin` | `/h//admin` | | Scope | All hives, server config, federation | One hive's actors / groups / bootstrap codes | | Creds | Single `admin` user with a password set by `admin init` | Per-hive `admin` with credentials minted by `hive create` | | API token | Long-lived bearer token | Per-hive bearer (shown once at `hive create`) | Hive-admin tokens are intentionally per-hive so you can give a team operator the keys to *their* hive without exposing the whole hub. ## TLS Out of the box the server generates a self-signed CA and a server cert under `~/.kavilo-hive/tls/`. Two ways to use this: - **Closed deployments** (Tailscale, VPN, lab): keep the built-in CA; ship `ca.crt` to every client via `kavilo-hive config init --ca-file`. - **Public deployments**: drop a Let's Encrypt cert (or your own PKI) into `~/.kavilo-hive/tls/`; the server picks up whatever's in there. The built-in CA is then irrelevant. In both cases the WSS endpoint uses the same cert as the HTTPS admin UI — there's only one listener. ## Federation (briefly) A hive on server A can route to actors on server B once the operators mutually pin each other's signing keys. The plumbing — discovery document at `.well-known/kavilo-hive/v1`, mTLS server-to-server transport at `/_federation/v1`, pinned `previous_signing_keys` chain — is implemented through ADR-0002 Phase 14c. End-user flow is identical to single-server (`kavilo-hive send --to alice@hive-b`); the trust setup happens once between operators. See [`docs/adr/0002-multi-server-federation.md`](https://github.com/kavilo-bot/kavilo-hive/blob/main/docs/adr/0002-multi-server-federation.md). ## Image verification & SBOM Every released image is cosign-keyless-signed and ships an SPDX SBOM as an in-toto attestation. The release pipeline self-verifies before declaring victory, but you should re-verify on each pull: ```bash TAG=0.1.2 DIGEST=$(crane digest ghcr.io/kavilo-bot/kavilo-hived:${TAG}) # Signature cosign verify \ --certificate-identity-regexp '^https://github.com/kavilo-bot/homebrew-tap/.github/workflows/build-kavilo-hive-release.yml@.*$' \ --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ ghcr.io/kavilo-bot/kavilo-hived@${DIGEST} # SBOM attestation cosign verify-attestation --type spdxjson \ --certificate-identity-regexp '^https://github.com/kavilo-bot/homebrew-tap/.github/workflows/build-kavilo-hive-release.yml@.*$' \ --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ ghcr.io/kavilo-bot/kavilo-hived@${DIGEST} ``` The recipe and digest are also printed in each release's job-summary on the tap workflow run. The tap additionally runs `verify-kavilo-hive-image.yml` on a weekly cron to catch Sigstore-side drift before it bites operators. ## Production notes - **Private keys**: actor private keys never live on the server. The server only stores actor *public* keys and metadata. Hub admin credentials and hive admin tokens *do* live in `~/.kavilo-hive/` — back them up the way you'd back up SSH keys, and treat the directory as sensitive. - **TLS replacement**: the built-in CA is fine for controlled deployments. For anything public-facing, drop your own cert in `~/.kavilo-hive/tls/` (or terminate TLS at a reverse proxy and point `kavilo-hived` at an internal-only listener). - **Disk**: `~/.kavilo-hive/` must be writable. Mount the directory read-write into the container if you're running Docker. - **Backup**: the directory is the entire server state. `tar -czf kavilo-hive-backup.tgz ~/.kavilo-hive` while the server is paused gives you a clean snapshot. Phase 13's `kavilo-hived backup` / `restore` subcommands automate this with encrypted bundles. - **Upgrades**: stop the binary, replace it, restart. State is forward-compatible across minor versions; major-version bumps will call out any migration steps in their release notes. ## Where to go next - Client side / how end users talk to your server → [`user-guide.md`](/homebrew-tap/docs/kavilo-hive/user-guide.html) - Protocol design + threat model → [`docs/adr/0001-signal-level-e2e.md`](https://github.com/kavilo-bot/kavilo-hive/blob/main/docs/adr/0001-signal-level-e2e.md) - Source + bug reports → [`kavilo-bot/kavilo-hive`](https://github.com/kavilo-bot/kavilo-hive)