# Continuity 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. This document collects the picks that carry that pitch. Companion to [STACK.md](STACK.md) and [CONSOLE.md](CONSOLE.md). ## Thesis 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. 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. ## Tailscale **Pick: Tailscale client, shipped in the base image, disabled at first boot until the user enrolls.** - The client (`tailscaled` + `tailscale`) is open-source and works with either the tailscale.com control plane or a self-hosted Headscale server. - The control plane choice is exposed at enrollment time as a "Server" field, defaulting to tailscale.com. Users pointing at Headscale set `--login-server=`, 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. - 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. **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. **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. ## Syncthing **Pick: Syncthing user daemon, shipped in the base image, disabled at first boot until the user enrolls.** - Runs as a systemd user service (`syncthing.service`). Not root, not system-wide. - The daemon starts after `alloy sync` enrollment or an explicit `systemctl --user enable --now syncthing`. - Not enabled on-by-default with no folders configured; a running daemon with nothing to do is unjustified surface. - Web UI on `127.0.0.1:8384` remains reachable (Alloy does not disable it) but is not the recommended interface. `alloy sync` is. **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. ## First-boot flow At first login, one screen with two rows: ``` +---------------------------------------------------------+ | Alloy first-boot | | | | Network identity [ Enroll Tailscale ] [ Skip ] | | | | File sync [ Enroll Syncthing ] [ Skip ] | | | | [ Done ] | +---------------------------------------------------------+ ``` 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. **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. ## Console subcommands `alloy mesh` and `alloy sync` live under Alloy Console. Full spec in [CONSOLE.md](CONSOLE.md); scope summary here so this document stands alone: - **`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. - **`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. ## Non-goals - **Secrets sync.** Bitwarden, pass, KeePassXC, etc. Too personal, and the security model varies too much to have a defensible default. Users bring their own. - **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. - **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. - **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. ## Honest costs - **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. - **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. - **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. ## Backup **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. **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. 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. 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). **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. ## Open questions - [ ] MagicDNS interaction with Fedora's `systemd-resolved`: verify no config conflict on a fresh Silverblue install with Tailscale enrolled. - [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. - [ ] 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. - [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.