bb4ed3502d
P5-01 — Documentation site under docs/book/ rendered with mdBook
(downloaded via Makefile, same static-binary pattern as Tailwind).
Structured chapters: getting started, concepts, operations,
security, reference. `make docs` / `make docs-watch`. Generated
output gitignored.
P5-02 — CONTRIBUTING.md rewritten from placeholder to a full
guide. CODE_OF_CONDUCT.md adapted from Contributor Covenant for a
single-maintainer project. .gitea/issue_template/{bug,feature}.md
and PULL_REQUEST_TEMPLATE.md.
P5-04 — Six README screenshots captured live from a fresh server
bootstrap (login, empty dashboard, add-host, alerts, settings,
audit log). README rewritten to centre the screenshot grid and
link out to the docs site.
P5-05 — SECURITY.md with disclosure policy (3-day ack, 30-day
default window), scope in/out, threat-model summary, operator
hardening checklist. Mirrored as a docs-site chapter.
P5-06 — End-to-end test harness. e2e/compose.e2e.yml brings up
server + sibling Linux agent (alpine + restic) + restic/rest-server.
Agent uses announce-and-approve so Playwright can drive the full
operator flow: bootstrap → login → accept pending → backup →
verify terminal status. Second spec scrapes /metrics to assert
the P6-04 endpoint surface. .gitea/workflows/e2e.yml runs on every
PR; local how-to in docs/e2e.md.
114 lines
3.9 KiB
Markdown
114 lines
3.9 KiB
Markdown
# Enrolling your first host
|
|
|
|
The control plane only knows about hosts you've explicitly
|
|
enrolled. Two paths exist:
|
|
|
|
1. **Token-based enrolment** — admin generates a token, pastes it
|
|
into an install command on the host. The host appears immediately,
|
|
already mapped to the desired repo.
|
|
2. **Announce-and-approve** — the agent runs without a token,
|
|
"announces" itself to the server, and a human in the UI accepts
|
|
the announcement.
|
|
|
|
Token-based is the default and what most operators want; the
|
|
announce flow exists for the case where you can't easily paste a
|
|
secret onto the host (auto-imaged endpoints, scripted bring-ups
|
|
from a config repo).
|
|
|
|
## Token-based enrolment
|
|
|
|
### From the UI
|
|
|
|
1. Click **+ Add host** on the dashboard.
|
|
2. Fill in the hostname, the restic repo URL, and the repo
|
|
credentials. The credentials are AEAD-encrypted at the server
|
|
immediately; what you paste is what the agent receives.
|
|
3. Optionally pick the initial source paths — these become the
|
|
first source group on the host.
|
|
4. Submit. The server mints a one-time token and shows you a copy-
|
|
pasteable install snippet.
|
|
|
|
### On the host (Linux)
|
|
|
|
```sh
|
|
curl -fsSL https://restic.example.com/install/install.sh | \
|
|
sudo RM_SERVER=https://restic.example.com \
|
|
RM_ENROL_TOKEN=<token> \
|
|
bash
|
|
```
|
|
|
|
The script:
|
|
|
|
1. Detects architecture (`amd64` or `arm64`).
|
|
2. Downloads the agent binary from `/agent/binary?os=…&arch=…`.
|
|
3. Drops the systemd unit at
|
|
`/etc/systemd/system/restic-manager-agent.service`.
|
|
4. Runs the agent in `-enrol` mode, which posts the token and
|
|
stores the persistent bearer it gets back.
|
|
5. Enables and starts the unit.
|
|
|
|
Within seconds the host should appear on the dashboard as
|
|
**online**.
|
|
|
|
### On the host (Windows)
|
|
|
|
```pwsh
|
|
$env:RM_SERVER = "https://restic.example.com"
|
|
$env:RM_ENROL_TOKEN = "<token>"
|
|
iwr -useb $env:RM_SERVER/install/install.ps1 | iex
|
|
```
|
|
|
|
Equivalent shape: registers a Windows service via the SCM
|
|
(see P2-16 for details), runs `-enrol`, starts the service.
|
|
|
|
## Recovering a lost token
|
|
|
|
Tokens are single-use and short-lived (1h). If you closed the tab
|
|
before pasting the install command, head to the **Add host** page —
|
|
outstanding tokens are listed there with a **Regenerate** button.
|
|
Regenerating revokes the old token's hash and mints a fresh raw
|
|
token while preserving the original repo credentials and initial
|
|
paths. (NS-02 in `tasks.md` if you want the design rationale.)
|
|
|
|
## Announce-and-approve
|
|
|
|
If the host can reach the server but you don't want to paste a
|
|
secret on it, run the agent in `-announce` mode:
|
|
|
|
```sh
|
|
restic-manager-agent -announce \
|
|
-server https://restic.example.com \
|
|
-hostname myhost
|
|
```
|
|
|
|
The host appears in the **Pending hosts** panel on the dashboard
|
|
with its hostname, OS, arch, and the source IP that announced it.
|
|
Click **Accept**, fill in the repo URL + credentials, and the
|
|
server pushes the bearer over the still-open WebSocket. No
|
|
back-and-forth round trip.
|
|
|
|
If you don't accept within an hour the announcement is swept.
|
|
|
|
## What happens on the agent
|
|
|
|
After enrolment, the agent:
|
|
|
|
1. Connects via WebSocket to `/ws/agent` with its bearer token.
|
|
2. Sends a `hello` envelope with its OS, arch, agent version,
|
|
restic version, and protocol version.
|
|
3. Receives a `config.update` carrying its encrypted repo
|
|
credentials and any source-group paths.
|
|
4. Sits idle, sending a heartbeat every 30s. Operator-driven
|
|
"Run now" actions arrive as `command.run` envelopes; scheduled
|
|
jobs are driven by the agent's local cron.
|
|
|
|
## Auto-init of the repository
|
|
|
|
The first time a backup runs, the agent invokes `restic init`
|
|
against the repo you configured at enrolment. If the repo already
|
|
exists (`config file already exists`) the agent treats it as a
|
|
success and proceeds. The host's repo status (`unknown` →
|
|
`ready` / `init_failed`) is surfaced under the vitals strip on
|
|
the host detail page; if init fails, save fresh credentials in
|
|
the **Repo** tab to retry.
|