Install and Build
Joe is available two ways: as published release binaries for the supported platform
set, or built from source. Both produce the same single self-contained artifact —
the server daemon, every subcommand, and the web UI compiled into one joe binary.
A published release carries the built archives and a checksums.txt file, and nothing
else. There is no signing, no install script, and no package-manager artifact — no
Homebrew tap, no Scoop bucket, no distribution package. Downloading an archive and
verifying it against the checksums file by hand is the whole of the install path.
This page is the procedure: obtain the binary, run the daemon, and configure the identity it requires. If you just want a guided first run from nothing to one answer, follow the Quickstart instead — it is the on-rails version of what follows. For why identity is mandatory and what observation mode protects, see Concepts. For the full configuration surface, see Configuration.
Download a released binary
This is the shortest path to a running joe, and it needs neither a Go toolchain nor
Node.js — only a shell and a SHA-256 utility, both of which your system already has.
1. Fetch the archive for your platform. Open the repository’s GitHub Releases
page and download the archive matching your operating system and CPU architecture from
the release’s asset list, along with the checksums.txt file published beside it. The
platforms a release covers are exactly the builds.goos and builds.goarch matrix
declared in .goreleaser.yaml in the repository root — read that file rather than
trusting a list restated here, and let the Releases page’s own asset list be the
authority on what a given release actually shipped.
2. Verify the download against the published checksums. Run this from the directory
holding both the archive and checksums.txt. On Linux:
sha256sum --ignore-missing --check checksums.txtOn macOS:
shasum --algorithm 256 --ignore-missing --check checksums.txt--ignore-missing is what lets you verify the one archive you downloaded against a
checksums file that lists every published asset. Each verified file prints OK. If any
file prints FAILED, stop — do not extract or run it.
3. Extract and run. The archives are gzipped tarballs:
tar -xzf <the archive you downloaded>
./joe --helpA downloaded release binary reports real injected build identity — its version,
commit, and build time are stamped in at release time, so GET /api/v1/version and the
joe_build_info metric identify precisely which release is running. This is the
practical difference from a plain go build, described below.
Build from source
Building from source is a first-class path, not a fallback. Reach for it when you are
contributing to Joe, when you want to run an untagged commit from main, or when your
platform is not in the release matrix.
Prerequisites for building
These apply to the build path only — the download path above needs none of them.
- Go 1.25 or newer — to compile the binary.
- Node.js and npm — to build the web UI, which is compiled and embedded into the
binary.
make buildrunsnpm ci && npm run buildfor you; without a working npm the build stops before it links the binary. - git — to clone the source and to stamp build identity (
make buildreads the current commit and tag).
Building
From the repository root:
make buildThis produces a ./joe binary in the repository root. make build first builds the
production web UI and stages it into the embed directory, then compiles ./cmd/joe
with the UI embedded and build identity (version, commit, build time) injected into
the binary. The result is a single self-contained binary: server daemon plus all
subcommands, with the UI served from inside it.
A plain go build ./... also compiles, but it does not embed a freshly built UI
or inject build identity — such a binary reports the unset dev build defaults. Use
make build for anything you intend to run.
Run the daemon
A bare invocation starts the server — that is Joe’s default behavior:
./joeThe daemon listens on localhost:7777 by default (the HTTP API and the embedded web
UI). Prometheus metrics are served separately on port 9090, not on the API port.
Joe reads its configuration file from, in order of precedence:
- the
--config <path>flag, - the
JOE_CONFIGenvironment variable, ~/.joe/config.yaml(the default).
A missing config file is not an error — Joe boots on built-in defaults. What Joe will not do is boot without an identity configuration, covered next.
The subcommands (joe mcp, joe slack, and the rest) ride alongside the server in the
same binary but are dispatched ahead of it; joe mcp and joe slack are clients of a
running daemon, not part of server boot.
Identity is mandatory
Joe refuses to start without a usable identity configuration. There is no auth-disabled or anonymous runtime mode to fall into — this refusal is what makes “Joe running implies Joe governed” structural rather than a matter of discipline (see the governed-safety invariant and principals and identity).
A usable identity configuration means at least one of the following is present:
- one or more service accounts (non-human principals), or
- a complete OIDC issuer (human principals).
Configure at least one before you run, or boot will exit with a refusal.
Non-human principals: service-account bearer keys
Joe’s shipped machine clients are the MCP server and the Slack bot; they — along with
any other external caller an operator mints a service-account key for, such as a CI job,
a script, or curl — authenticate with a service-account bearer key presented as
Authorization: Bearer <key>. Joe
resolves the key to a svc:<name> principal. Define service accounts in config:
server:
service_accounts:
- name: ci
key: a-long-random-secretA request bearing that key authenticates as principal svc:ci. Duplicate or empty
keys are a boot failure rather than a silent ambiguity.
As a shorthand, the JOE_API_KEY environment variable sets the key of a reserved
server service account (principal svc:server), creating it if absent. Setting
JOE_API_KEY alone satisfies the identity requirement, which is the smallest possible
identity configuration — the Quickstart uses exactly this.
Human principals: OIDC
People sign in through a single configured OIDC issuer using a real authorization-code
flow with PKCE; a verified login mints a user: principal. Configure the issuer under
auth.oidc:
auth:
oidc:
issuer: https://your-idp.example.com
client_id: joe
client_secret: your-client-secret
redirect_url: https://joe.example.com/api/v1/auth/callbackThe login, callback, and logout endpoints are registered only when an issuer is configured. OIDC discovery is lazy, so an identity-provider outage at startup is not a boot failure — only new logins fail until it recovers.
Admin bootstrap
The very first administrator is bootstrapped through OIDC. Set auth.admin_email to a
verified email address; when that person completes an OIDC login, Joe grants them admin
once (idempotent and audited):
auth:
admin_email: you@example.comThis bootstrap is OIDC-only: a service-account-only install has no self-escalation
path, so if you need the admin REST surface (zones, read posture, promotion), configure
OIDC and admin_email. Once one admin exists, further admins are granted through the
admin API by an existing admin.
Observation mode and the write floor
Joe boots with the write floor up by default: the floor comes up read-only when
JOE_MODE is unset and, explicitly, with JOE_MODE=observation. Every attempt to
mutate a managed system is denied before any other gate is consulted, and the floor is
resolved once at boot and cannot be lowered while the process runs. This is the shipped
read-only posture — Joe can read and reason about your infrastructure but cannot change
it. A governed full-capabilities mode is forthcoming: JOE_MODE=full is refused at boot
pending implementation, and an unrecognized value is refused fail-closed. When full mode
lands, moving to a writable posture will be a deliberate restart with different boot
inputs, never a live transition. See
observation mode and the write floor
for the full model.
Where to go next
- A guided, guaranteed-success first run → Quickstart
- Every configuration key, default, and environment variable → Configuration
- Connecting Joe to your systems → Components
- Running, observing, and recovering Joe in production → Operations
- Why Joe is governed by construction → Concepts