Skip to content

Deploy

One agent instance runs one project, typically on a dedicated VM. There are two install paths: the older compile-on-the-VM make deploy (for developers and the dogfood host), and the release-binary steelframe agent install (for operators without a compiler).

The agent reads these from the process environment:

VariableDefaultDescription
STEELFRAME_HOST0.0.0.0Bind address; set 127.0.0.1 behind nginx
STEELFRAME_PORT7700Bind port
STEELFRAME_DB_URLfile:steelframe.dbTurso/libsql database URL
STEELFRAME_MASTER_KEYMaster key for credential encryption and session signing
STEELFRAME_TMPSandbox root (disk-backed on a systemd unit)
STEELFRAME_LOG_FILELog file; self-heal scans it for panics

Everything else (repo, labels, timeouts, App id, coding runtime) lives in the DB config table and is edited via the TUI, GUI, or API.

On the VM, as the steelframe user (create it first if needed):

Terminal window
cp infra/secrets.mk.example infra/secrets.mk && $EDITOR infra/secrets.mk
make deploy

make deploy is idempotent — the same command handles first-time setup and updates.

infra/secrets.mk holds everything per-VM (gitignored):

HOST := agent.example.com
STEELFRAME_MASTER_KEY := …
OAUTH2_PROXY_CLIENT_ID := …
OAUTH2_PROXY_CLIENT_SECRET := …
OAUTH2_PROXY_COOKIE_SECRET := …

The OAuth credentials come from a GitHub OAuth App with callback https://<HOST>/oauth2/callback.

The orchestrator runs three sub-makes:

Sub-makeWhat it installs
infra/steelframeHost tools (gcx), RustFS sidecar, the agent binary, the systemd unit
infra/oauth2-proxyoauth2-proxy binary + unit in front of the agent
infra/nginxReverse proxy + TLS for HOST

infra/steelframe targets:

  • deployinstall-tools install-rustfs install-bin install-unit restart status.
  • deploy-source — the same without install-tools, for a source-profile host with no Grafana/gcx.
  • install-rustfs — installs the RustFS sidecar (session-memory WAL on 127.0.0.1:9000, console 127.0.0.1:9001, data at /opt/steelframe/data/rustfs).
  • install-unit — renders steelframe.service.tpl with the master key and installs it to /etc/systemd/system/.
  • status — fails the deploy if the unit is not active or crash-looping.

The unit runs as user steelframe, WorkingDirectory=/opt/steelframe, DB at /opt/steelframe/data/steelframe.db, Delegate=yes, Restart=always.

On a host with the release binary and no compiler:

Terminal window
steelframe setup
sudo STEELFRAME_MASTER_KEY=… steelframe agent install

steelframe setup installs host tools (git, gh, gcx, kubectl, sqlite3, rustfs) and the Cloudflare security-audit skill and one coding runtime, then persists coding_runtime to the DB.

steelframe agent install:

  • Refuses to run without STEELFRAME_MASTER_KEY.
  • Creates the steelframe user and /opt/steelframe (data, tmp, logs).
  • Installs the binary to /opt/steelframe/steelframe.
  • Writes /opt/steelframe/agent.env (mode 0600) with the master key and env.
  • Renders a unit with ExecStartPre=-/opt/steelframe/steelframe update, ExecStart=/opt/steelframe/steelframe agent run, and Restart=always.
  • Configures and starts the RustFS sidecar.

TLS and SSO are still make -C infra deploy (nginx + oauth2-proxy); agent install does not replace certbot or oauth2-proxy.

Production GitHub operations should use a dedicated GitHub App so commits and PRs use a consistent bot identity. Without an App, ambient gh auth on the agent host is used, which is fine for local development but not production.

  • make deploy must run as the steelframe user; it fails if HOST or any secret is missing.
  • Secrets stay in infra/secrets.mk (gitignored) or /opt/steelframe/agent.env (mode 0600); never commit them.
  • The agent binds 127.0.0.1:7700 and is reached through nginx. On the dogfood host the tree is a user unit at ~/opt/steelframe.
  • delivery_profile=source skips production_monitor; use deploy-source so gcx is not required.
  • RustFS must be up before the agent; the unit declares After=/Wants= on steelframe-rustfs.service.