Skip to main content

max / alloy_tui

8.8 KB · 94 lines History Blame Raw
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