Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

services/toad/INSTALL.md

Raw
Rendered preview

Installing Toad

Toad is a rootless deployment controller, not a self-installing privileged daemon. Its distributable host assets live in deploy/:

  • toad.container: control plane;
  • toad-ingress.container: HTTP ingress gateway;
  • toad.network: private application network;
  • gateway.Caddyfile: fail-closed gateway configuration;
  • setup-user.sh and start.sh: the current host integration reference.

Host contract

An administrator must provide:

  1. a dedicated toad account with a lingering systemd user manager and rootless Podman socket;
  2. /data/toad, owned only by that account;
  3. a shared ingress directory, writable by toad and connectable by the public reverse-proxy identity;
  4. persistent container_file_t labeling for the gateway configuration and ingress directory when SELinux is enforcing;
  5. an operator-owned enrollment file and ingress environment;
  6. a public reverse-proxy mount and route to the gateway socket.

These decisions cannot be encoded safely by a per-user Quadlet because they cross host users and security domains. An OS package may later encode account and directory creation through systemd-sysusers and systemd-tmpfiles.

Required files

Install the three Quadlets under the toad user's Quadlet directory. Install these operator-owned files:

/data/toad/enrollment.json
/data/toad/gateway.Caddyfile
/data/toad/ingress.env
/data/toad/nugu.css

enrollment.json may initially contain []. nugu.css is the optional dashboard palette selected with TOAD_THEME_CSS=/data/toad/nugu.css; Toad has a built-in fallback palette. ingress.env supplies the hostname accepted by the bundled gateway:

TOAD_WEB_HOST=toad.example.net

The gateway listens only on /data/toad-ingress/http.sock, creates no host TCP listener, and returns 404 for unknown hosts. The public reverse proxy mounts only /data/toad-ingress; it must never mount /data/toad, the Podman socket, the systemd user bus, or Toad's administrative socket.

Public Caddy handoff

The outer proxy terminates TLS and authentication, then forwards the original Host header over the shared socket:

toad.example.net {
	basic_auth {
		admin <bcrypt-hash>
	}
	reverse_proxy unix//run/toad-ingress/http.sock
}

Mount /data/toad-ingress into that proxy at /run/toad-ingress read-only. A read-only bind mount still permits connecting to a Unix socket; filesystem permissions and SELinux remain the security boundary.

The dashboard reports deployment state, live readiness, sanitized enrollment fields, container state, systemd user-unit properties, and Toad-owned operation logs. It deliberately does not expose raw Quadlets, environment values, host journal entries, or generic host metrics.

Operator CLI

Run the CLI inside the control-plane container through the service user's Podman scope. The reference installer places a root-only /usr/local/bin/toad wrapper on the host. No token is needed over the local Unix administration socket. Remote TCP consumers require a service-scoped token issued with toad token issue.

toad deploy <service> <digest> accepts only a manifest digest formatted sha256:<64 lowercase hex>. The enrolled service supplies its image repository, so pass Luci's successful digest field, not its reference field. Luci publication evidence does not invoke Toad or provide Toad credentials.

# Installing Toad

Toad is a rootless deployment controller, not a self-installing privileged daemon.
Its distributable host assets live in [`deploy/`](deploy/):

- `toad.container`: control plane;
- `toad-ingress.container`: HTTP ingress gateway;
- `toad.network`: private application network;
- `gateway.Caddyfile`: fail-closed gateway configuration;
- `setup-user.sh` and `start.sh`: the current host integration reference.

## Host contract

An administrator must provide:

1. a dedicated `toad` account with a lingering systemd user manager and rootless Podman socket;
2. `/data/toad`, owned only by that account;
3. a shared ingress directory, writable by `toad` and connectable by the public reverse-proxy identity;
4. persistent `container_file_t` labeling for the gateway configuration and ingress directory when SELinux is enforcing;
5. an operator-owned enrollment file and ingress environment;
6. a public reverse-proxy mount and route to the gateway socket.

These decisions cannot be encoded safely by a per-user Quadlet because they cross host users and security domains.
An OS package may later encode account and directory creation through `systemd-sysusers` and `systemd-tmpfiles`.

## Required files

Install the three Quadlets under the `toad` user's Quadlet directory.
Install these operator-owned files:

```text
/data/toad/enrollment.json
/data/toad/gateway.Caddyfile
/data/toad/ingress.env
/data/toad/nugu.css
```

`enrollment.json` may initially contain `[]`.
`nugu.css` is the optional dashboard palette selected with `TOAD_THEME_CSS=/data/toad/nugu.css`; Toad has a built-in fallback palette.
`ingress.env` supplies the hostname accepted by the bundled gateway:

```text
TOAD_WEB_HOST=toad.example.net
```

The gateway listens only on `/data/toad-ingress/http.sock`, creates no host TCP listener, and returns 404 for unknown hosts.
The public reverse proxy mounts only `/data/toad-ingress`; it must never mount `/data/toad`, the Podman socket, the systemd user bus, or Toad's administrative socket.

## Public Caddy handoff

The outer proxy terminates TLS and authentication, then forwards the original Host header over the shared socket:

```caddyfile
toad.example.net {
	basic_auth {
		admin <bcrypt-hash>
	}
	reverse_proxy unix//run/toad-ingress/http.sock
}
```

Mount `/data/toad-ingress` into that proxy at `/run/toad-ingress` read-only.
A read-only bind mount still permits connecting to a Unix socket; filesystem permissions and SELinux remain the security boundary.

The dashboard reports deployment state, live readiness, sanitized enrollment fields, container state, systemd user-unit properties, and Toad-owned operation logs.
It deliberately does not expose raw Quadlets, environment values, host journal entries, or generic host metrics.

## Operator CLI

Run the CLI inside the control-plane container through the service user's Podman scope.
The reference installer places a root-only `/usr/local/bin/toad` wrapper on the host.
No token is needed over the local Unix administration socket.
Remote TCP consumers require a service-scoped token issued with `toad token issue`.

`toad deploy <service> <digest>` accepts only a manifest digest formatted `sha256:<64 lowercase hex>`.
The enrolled service supplies its image repository, so pass Luci's successful `digest` field, not its `reference` field.
Luci publication evidence does not invoke Toad or provide Toad credentials.