| 1 |
# Deploying the developer edition on Earth |
| 2 |
|
| 3 |
Companion to `spec.md` §17.5. Status: **installed and running** since 2026-09-27 as the systemd service `oxygenhub` under the `oxygenhub` system user, data in `/home/oxygenhub/data`, HTTP on 7300 and HTTPS on 7343 with a self-signed certificate. `oxygen`, `oxygen-vscode` and `oxygenhub` are hosted under `andymccall/` and each checkout has an `earth` remote. |
| 4 |
|
| 5 |
## The host |
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
| Name | `earth` (`earth.andymccall.xyz`, IPv6 ULA; `192.168.1.6` on the LAN) | |
| 10 |
| OS | Ubuntu 26.04.1 LTS, x86_64, 4 cores, 31 GiB RAM | |
| 11 |
| Disk | `/` 251 GB (95 GB free), `/home` 1 TB (944 GB free) | |
| 12 |
| Already running | sshd :22, a Python service :6400, `wren-nest` :7842, retroplay-manager in Docker (:8080, :6670) with Postgres and a tetriblox server, two GitHub Actions runners (`doomsdayonecom`), Docker, containerd | |
| 13 |
| Not installed | Rust toolchain, sqlite3 CLI, nginx or caddy, certbot | |
| 14 |
| Access | `ssh -i ~/.ssh/earth_claude claude@earth`; `claude` has passwordless sudo | |
| 15 |
|
| 16 |
Ports 7300 (HTTP) and 7343 (HTTPS) are free and are the hub's. |
| 17 |
|
| 18 |
## Steps |
| 19 |
|
| 20 |
1. **Toolchain** (as `claude`): `curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal`, then `sudo apt-get install -y build-essential pkg-config git`. |
| 21 |
2. **Source**: from the development machine, `rsync -a --delete --exclude target --exclude node_modules ~/development/oxygen ~/development/oxygenhub claude@earth:~/src/`. Oxygen is never pushed to Git; rsync is the bootstrap, and after the hub runs, `oxy push` to the hub itself is the ongoing path. |
| 22 |
3. **Build**: `cd ~/src/oxygenhub && cargo build --release`. |
| 23 |
4. **System user and data**: `sudo useradd --system --home /home/oxygenhub --create-home oxygenhub`, `sudo install -o oxygenhub -g oxygenhub -m 0755 target/release/oxygenhub /usr/local/bin/`, `sudo -u oxygenhub oxygenhub init --data /home/oxygenhub/data --admin andymccall` (prints the first admin's one-time password). |
| 24 |
5. **Service**: `/etc/systemd/system/oxygenhub.service` running `oxygenhub serve --data /home/oxygenhub/data` as `oxygenhub`, `Restart=on-failure`, `ProtectSystem=strict`, `ReadWritePaths=/home/oxygenhub/data`, `NoNewPrivileges=yes`; `sudo systemctl enable --now oxygenhub`. |
| 25 |
6. **TLS**: the developer edition generates a self-signed certificate at `init`. Download the certificate from `https://earth.andymccall.xyz:7343/.well-known/oxyhub-ca.pem` (or over plain HTTP on port 7300) and point clients at it with `oxy config remote.<name>.ca <file>` or `OXY_CA`. |
| 26 |
7. **First repositories**: on the development machine, `oxy remote add earth https://oxygenhub.io/andymccall/oxygen` in each of `oxygen`, `oxygen-vscode`, `oxygenhub`, then `oxy push earth`. |
| 27 |
8. **Updates**: rsync, build, `sudo install`, `sudo systemctl restart oxygenhub`. `make deploy` in the hub wraps steps 2, 3 and 8. |
| 28 |
9. **Public address** (2026-09-30): a Cloudflare tunnel on Earth serves the hub as `https://oxygenhub.io`, with a publicly trusted certificate, and `base_url` in `hub.toml` is that address, so every link the hub writes uses it. Checkouts push there with no CA set. The Flow runner on Earth still reaches the hub on its local address with the self-signed certificate of step 6. |
| 29 |
|
| 30 |
## Where things are on Earth |
| 31 |
|
| 32 |
|
| 33 |
|
| 34 |
| Binaries | `/usr/local/bin/oxygenhub`, `/usr/local/bin/oxy` | |
| 35 |
| Sources | `/home/claude/src/oxygen`, `/home/claude/src/oxygenhub` (rsync targets; the hub itself is the canonical remote) | |
| 36 |
| Data | `/home/oxygenhub/data` (`hub.toml`, `hub.db`, `keys/`, `repos/andymccall/*`) | |
| 37 |
| Push token | `/home/oxygenhub/push-token` (also in `hub.toml`); each local checkout has it as `remote.earth.token` | |
| 38 |
| Certificate | `/home/oxygenhub/data/keys/tls.crt`, served at `/.well-known/oxyhub-ca.pem`; local copy `~/.config/oxy/earth-ca.pem` set as `remote.earth.ca` | |
| 39 |
| Service | `sudo systemctl status oxygenhub`, `sudo journalctl -u oxygenhub` | |