Files
frxd/DEPLOY.md
T

216 lines
8.4 KiB
Markdown

# Deploying FRX
Three roles: **member node** (`frxd serve`), **relay** (`frxd relay`), **registry** (`frxd registry`, run by the MA).
TLS is only a concern for servers. Members connect outbound and need no domain, port, or certificate.
## Member node (zero TLS work)
```
frxd init --name alice --id alice.frx.federatedsearch.org \
--registry https://ma.federatedsearch.org/registry.json --ma-key <ma-hex> \
--relay https://relay.federatedsearch.org --data-dir ./alice-data
frxd add ~/documents --name docs --shared --exposure full
frxd serve
```
The node connects outbound over HTTPS, verifies the registry with the pinned MA key, and holds an SSE stream per relay. Nothing inbound, no DNS, no certificates. Members at departmental level can start here.
## Joining the network (membership site + wizard)
The apex (`federatedsearch.org`) proxies the same membership page; `ma.` is the service host.
Identity registration stays with the MA; the wizard only consumes the credentials it issues.
MA operator — run the signup site:
```
frxd registry --dir ./ma init --zone frx.federatedsearch.org
frxd registry --dir ./ma serve --listen 127.0.0.1:7800
```
(put Caddy in front for a real domain). The page at `/` collects the registration form (short name, organization details) and queues it for MA review — `frxd registry --dir <dir> applications` lists applications and `frxd registry --dir <dir> approve <id> --registry-url <url>` creates the member, mints its account credential, and prints the credential block to hand over. The token is reusable: it authorizes key enrollment for every node the member runs (`registry token <id>` mints an additional one; `registry revoke-token <id>` revokes all after a leak). A single-use 24h invite (`registry invite <id>`) remains for constrained handoffs. The block is `id=... token=... registry=... ma_key=...`. Organization details (legal name, representative, contacts, payment) are recorded privately by the MA in `<registry dir>/applications.json` — contract data, never in the public signed snapshot.
New member:
```
frxd --onboarding
```
The wizard asks for a config path, accepts the pasted credential block (or field-by-field entry), generates keypairs, enrolls the new key with the MA (redeeming the single-use invite token), verifies the signed registry snapshot, takes relay defaults from the registry, optionally shares a directory, writes the config, and offers to start serving. If enrollment is unavailable (file-path registry), it prints the exact `registry add` command the operator must run.
## Relay with TLS (one line)
Run `frxd` on loopback and terminate TLS with Caddy:
```
caddy reverse-proxy --from relay.federatedsearch.org --to 127.0.0.1:7700
```
Caddyfile equivalent (apex is the public front door, `ma.` the membership service, `relay.` the relay, `git.` the code host — all on one host):
```
federatedsearch.org {
reverse_proxy 127.0.0.1:7800
}
ma.federatedsearch.org {
reverse_proxy 127.0.0.1:7800
}
relay.federatedsearch.org {
reverse_proxy 127.0.0.1:7700
}
git.federatedsearch.org {
reverse_proxy 127.0.0.1:3000
}
```
Relay command (peers and registry gated by the MA):
```
frxd relay --listen 127.0.0.1:7700 \
--url https://relay.federatedsearch.org \
--peer https://relay2.federatedsearch.org \
--registry https://ma.federatedsearch.org/registry.json --ma-key <ma-hex>
```
No domain or open ports? Tunnel it:
```
frxd relay --listen 127.0.0.1:7700 --allow-insecure
cloudflared tunnel --url http://127.0.0.1:7700
```
`systemd` unit example:
```ini
[Unit]
Description=FRX relay
After=network-online.target
[Service]
ExecStart=/usr/local/bin/frxd relay --listen 127.0.0.1:7700 \
--url https://relay.federatedsearch.org \
--registry https://ma.federatedsearch.org/registry.json --ma-key <ma-hex>
Restart=on-failure
DynamicUser=yes
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
[Install]
WantedBy=multi-user.target
```
The same unit shape works for `frxd serve` (add `--config /etc/frxd/frxd.toml`).
## Registry (MA)
```
frxd registry --dir /var/lib/frxd/registry init
frxd registry --dir /var/lib/frxd/registry add alice.frx.federatedsearch.org <key> --enc-key <enc>
frxd registry --dir /var/lib/frxd/registry serve --listen 127.0.0.1:7800
```
Put the same Caddy in front, or distribute `registry.json` out of band (it is signed, so the channel does not matter). The snapshot is versioned; nodes reject rollback and fail static during outages.
## Code hosting and downloads (Gitea)
The public source and release binaries live at `git.federatedsearch.org` (Gitea), so the
download step on the membership page stays on infrastructure the federation operates.
One-time install on the server (root):
```
VER=1.27.3
curl -fsSLO https://dl.gitea.com/gitea/$VER/gitea-$VER-linux-amd64{,.sha256}
sha256sum -c gitea-$VER-linux-amd64.sha256
install -m 0755 gitea-$VER-linux-amd64 /usr/local/bin/gitea
adduser --system --shell /bin/bash --gecos 'Gitea' --home /home/git --group git
mkdir -p /var/lib/gitea/{custom,data,log} /etc/gitea && chown -R git:git /var/lib/gitea /etc/gitea
```
`/etc/gitea/app.ini` essentials (rest defaults; secrets via `gitea generate secret`):
```ini
WORK_PATH = /var/lib/gitea
[database]
DB_TYPE = sqlite3
PATH = /var/lib/gitea/data/gitea.db
[server]
DOMAIN = git.federatedsearch.org
SSH_DOMAIN = git.federatedsearch.org
ROOT_URL = https://git.federatedsearch.org/
HTTP_ADDR = 127.0.0.1
HTTP_PORT = 3000
[security]
INSTALL_LOCK = true
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = false
```
systemd unit (`User=git`, `ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini`,
`WorkingDirectory=/var/lib/gitea`), then `gitea migrate --config /etc/gitea/app.ini` as the
`git` user and `systemctl enable --now gitea`. Git-over-SSH uses the host sshd via the `git`
user's Gitea-managed `authorized_keys`; HTTPS pushes can use an access token instead.
Admin bootstrap (as `git` user):
```
gitea admin user create --admin --username <you> --email <you>@federatedsearch.org --random-password --config /etc/gitea/app.ini
gitea admin user generate-access-token -u <you> -t bootstrap --scopes all --config /etc/gitea/app.ini
```
The org is `frx`, the repo `frxd` → clone URL
`https://git.federatedsearch.org/frx/frxd.git`. Membership stays closed (MA-approved
applications); repo reads are public.
Publishing a release (from the checkout):
```
git tag v0.1.0 && git push gitea v0.1.0
# build static binaries (see next section), then attach via the API:
curl -X POST https://git.federatedsearch.org/api/v1/repos/frx/frxd/releases \
-H "Authorization: token <token>" -H 'content-type: application/json' \
-d '{"tag_name":"v0.1.0","name":"v0.1.0"}'
curl -X POST https://git.federatedsearch.org/api/v1/repos/frx/frxd/releases/<id>/assets?name=frxd-linux-amd64 \
-H "Authorization: token <token>" -F attachment=@frxd-linux-amd64
```
Asset URLs follow `/frx/frxd/releases/download/<tag>/<file>` — the membership page pins
those. Bump the page when a release changes.
## Private networks and custom CAs
- `ca_cert = "/etc/ssl/private-ca.pem"` in `[node]`, or `--ca-cert` on the relay: adds a private/corporate root CA for relay and registry connections.
- `allow_insecure = true` / `--allow-insecure`: explicit opt-in for plain `http://` on a VPN/LAN. Without it, non-loopback `http://` endpoints are refused at startup.
- `http://127.0.0.1` is always allowed for development.
## Static binary
```
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
```
`[profile.release]` enables LTO and stripping. All dependencies are pure Rust, so the musl build has no system-library requirements. `ring` needs a musl C toolchain (`musl-tools`); without host sudo, build in a container instead:
```
docker run --rm -v "$PWD":/src -w /src rust:1-slim-bookworm bash -c \
"apt-get update -qq && apt-get install -y -qq musl-tools && rustup target add x86_64-unknown-linux-musl && cargo build --release --target x86_64-unknown-linux-musl"
```
Binaries land in `target/x86_64-unknown-linux-musl/release/`; rename to
`frxd-linux-amd64` / `frx-linux-amd64` for release assets, with `sha256sum` sidecar files.
## What TLS does and does not cover
Envelopes are signed and registry snapshots are MA-signed, so TLS is not what protects message authenticity or registry integrity. TLS protects traffic from network observers, authenticates the relay endpoint, and hides mailbox metadata. Unicast response bodies are already encrypted end-to-end to the recipient's X25519 key.