| 1 |
# Continuity |
| 2 |
|
| 3 |
State continuity across machines is a first-class Alloy value. An Alloy install should be *rejoinable*, not just installable: flash the image, sign in to two things, and your files and your network appear. That is a different pitch from "runs your programs fast," and it is the right one for a personal machine that will be reinstalled, replaced, or added-to over time. |
| 4 |
|
| 5 |
This document collects the picks that carry that pitch. Companion to [STACK.md](STACK.md) and [CONSOLE.md](CONSOLE.md). |
| 6 |
|
| 7 |
## Thesis |
| 8 |
|
| 9 |
The machine is disposable. The state is not. What survives a reinstall or a hardware upgrade should not depend on the user remembering to copy files, re-establish keys, or reconfigure a mesh. Alloy ships the two adopted tools that make that true (network identity and file synchronization) as base-image citizens, gated behind an explicit enrollment gesture and controlled through the Alloy Console. |
| 10 |
|
| 11 |
Everything in this document is a curated adoption of existing tools. Alloy authors nothing in this space. The Alloy contribution is: the tools are present at first boot, they enroll in one screen, and they are controlled through the same design-system surface as everything else. |
| 12 |
|
| 13 |
## Tailscale |
| 14 |
|
| 15 |
**Pick: Tailscale client, shipped in the base image, disabled at first boot until the user enrolls.** |
| 16 |
|
| 17 |
- The client (`tailscaled` + `tailscale`) is open-source and works with either the tailscale.com control plane or a self-hosted Headscale server. |
| 18 |
- The control plane choice is exposed at enrollment time as a "Server" field, defaulting to tailscale.com. Users pointing at Headscale set `--login-server=<url>`, which Alloy handles as a first-class field in `alloy mesh` rather than a footnote. The console reads the control plane back and names it in the view title when it is self-hosted, so "which control plane am I on" is answerable without dropping to a shell. |
| 19 |
- Enabled via `alloy mesh` (see below) or plain `sudo tailscale up`. Not enrolled by default. An OS that phones home before the user says yes is out of character for Alloy. |
| 20 |
|
| 21 |
**Not shipping the Headscale server itself.** That is fleet-admin work, not client-OS work. Users self-hosting Headscale run it on their infrastructure of choice; Alloy points at it. |
| 22 |
|
| 23 |
**Not building an alternative.** WireGuard's userland ecosystem has several candidates (headscale-tools, netbird, innernet). None reach Tailscale's install-and-forget property. Alloy is not competing on this axis; it is adopting. |
| 24 |
|
| 25 |
## Syncthing |
| 26 |
|
| 27 |
**Pick: Syncthing user daemon, shipped in the base image, disabled at first boot until the user enrolls.** |
| 28 |
|
| 29 |
- Runs as a systemd user service (`syncthing.service`). Not root, not system-wide. |
| 30 |
- The daemon starts after `alloy sync` enrollment or an explicit `systemctl --user enable --now syncthing`. |
| 31 |
- Not enabled on-by-default with no folders configured; a running daemon with nothing to do is unjustified surface. |
| 32 |
- Web UI on `127.0.0.1:8384` remains reachable (Alloy does not disable it) but is not the recommended interface. `alloy sync` is. |
| 33 |
|
| 34 |
**Not shipping alternatives.** rsync-based sync is more mechanical and less mesh-shaped than Alloy wants; Resilio is proprietary; NextCloud/Seafile are servers, not sync tools. Syncthing is the honest pick for peer-to-peer file continuity. |
| 35 |
|
| 36 |
## First-boot flow |
| 37 |
|
| 38 |
At first login, one screen with two rows: |
| 39 |
|
| 40 |
``` |
| 41 |
+---------------------------------------------------------+ |
| 42 |
| Alloy first-boot | |
| 43 |
| | |
| 44 |
| Network identity [ Enroll Tailscale ] [ Skip ] | |
| 45 |
| | |
| 46 |
| File sync [ Enroll Syncthing ] [ Skip ] | |
| 47 |
| | |
| 48 |
| [ Done ] | |
| 49 |
+---------------------------------------------------------+ |
| 50 |
``` |
| 51 |
|
| 52 |
Skip is fine and reversible: later, the same enrollments live under `alloy mesh` and `alloy sync`. This is not a wizard, and Alloy does not grow one. Two rows, two actions, done. |
| 53 |
|
| 54 |
**Deferred to v1.** The first-boot flow itself is v1 scope. v0.5 users start the daemons manually via the console subcommands. Shipping the subcommands first, and the wizard after, is the correct ordering: the wizard is a thin shim over the enrollment actions the subcommands already expose. |
| 55 |
|
| 56 |
## Console subcommands |
| 57 |
|
| 58 |
`alloy mesh` and `alloy sync` live under Alloy Console. Full spec in [CONSOLE.md](CONSOLE.md); scope summary here so this document stands alone: |
| 59 |
|
| 60 |
- **`alloy mesh`** (was `alloy tail`; the old verb remains an alias). A ratatui front over Tailscale, named for what it is rather than who makes it, since Headscale users drive the same client. Replaces `tailscale status` as the daily-use surface. **Shipped:** peer list with online status and last-seen, this machine first, exit-node selection and clearing, and the control plane named in the title when it is self-hosted. **Still to come:** MagicDNS lookup, share/unshare, and the enrollment flow. |
| 61 |
- **`alloy sync`**: ratatui front over Syncthing's REST API. Folder list with per-folder sync state, device list with connection state, add/remove folder, add/remove device, pause/resume. Does not try to replicate the Syncthing web UI's full feature surface, only the operations users perform. The web UI remains available for edge cases. |
| 62 |
|
| 63 |
## Non-goals |
| 64 |
|
| 65 |
- **Secrets sync.** Bitwarden, pass, KeePassXC, etc. Too personal, and the security model varies too much to have a defensible default. Users bring their own. |
| 66 |
- **Dotfiles sync as an opinionated setup.** Syncthing *can* carry `~/.config`, but Alloy does not ship a preconfigured dotfile-sync arrangement. Provide the daemon; let the user decide what to sync. |
| 67 |
- **Backup (partial):** See [Backup](#backup) below. Alloy ships restic in the image but declines to prescribe a policy. Sync is mesh + latest-wins; backup is encrypted-at-rest + versioned. Different trust models; the two must not conflate. |
| 68 |
- **Cross-user or multi-account state.** Alloy is a personal-machine story. Multi-user file sharing is a Syncthing feature the user can configure themselves; Alloy does not prescribe. |
| 69 |
|
| 70 |
## Honest costs |
| 71 |
|
| 72 |
- **Go in the base image.** Both tools are Go binaries. Alloy is Rust-forward but not Rust-exclusive (see [STACK.md](STACK.md) for other adopted C/Go tools). Naming the cost so it is not a surprise later. |
| 73 |
- **Two daemons at rest post-enrollment.** `tailscaled` (~30-50 MB RSS typical) and `syncthing` (~50-150 MB RSS depending on folder count). Acceptable for the value delivered; would be indefensible if unused, hence the opt-in gating. |
| 74 |
- **Tailscale control plane is proprietary.** Users who require a fully open control plane use Headscale. Alloy exposes the choice explicitly rather than pretending it does not exist. |
| 75 |
|
| 76 |
## Backup |
| 77 |
|
| 78 |
**restic.** Go, CLI-first, encrypted, deduplicating, snapshot-based. Broad backend support (local, SFTP, S3, B2, rclone-anything). Stable repo format. Widest tutorial coverage of any modern backup tool. In Fedora repos. |
| 79 |
|
| 80 |
**Shipped in the base image. No default policy.** Alloy provides the tool; the user provides the folders, schedule, retention, and destination. Sync (Tailscale, Syncthing) has defensible defaults because enroll-and-it-works is the whole model; backup does not: "where do you back up to" is answerable only per-user. |
| 81 |
|
| 82 |
The forcing observation: when a user's laptop dies, they have their sync mesh (Syncthing pulled files back) and their network identity (Tailscale re-enrolls). What they don't have without configuring restic is *point-in-time recovery*, the "I deleted this file three weeks ago and just noticed" case that only versioned backup solves. |
| 83 |
|
| 84 |
Rejected: borg (Python; historical repo-format transitions), kopia (GUI-first; adds complexity Alloy doesn't need), duplicity (older, less-featured), rclone (sync, not backup; no snapshot history or dedup). |
| 85 |
|
| 86 |
**Deferred: `alloy backup` subcommand.** A ratatui front for `restic snapshots` / `restic backup` / `restic restore` fits the Alloy Console pattern (see [CONSOLE.md](CONSOLE.md)). v2+ candidate, worth building once someone is using restic daily and wants a nicer surface. |
| 87 |
|
| 88 |
## Open questions |
| 89 |
|
| 90 |
- [ ] MagicDNS interaction with Fedora's `systemd-resolved`: verify no config conflict on a fresh Silverblue install with Tailscale enrolled. |
| 91 |
- [x] **Syncthing's `discosrv` and `relaysrv`: defaults for v1.** Public infrastructure is fine for the enroll-and-it-works model. Expose configuration in `alloy sync` only if requests appear from users self-hosting the discovery / relay side. |
| 92 |
- [ ] Headscale login-server field in `alloy mesh` enrollment: preserve across `tailscale down` / `tailscale up` cycles automatically, or require re-entry? Automatic is the correct default; verify Tailscale client behavior supports it cleanly. |
| 93 |
- [x] **First-boot screen appears once.** If the user skips both Tailscale and Syncthing, `alloy mesh` and `alloy sync` are the enrollment paths after that; the first-boot flow does not re-appear. |
| 94 |
|