5 Commits

Author SHA1 Message Date
librelad
6fd6587714 feat(setup): selections above, drives below, and both roots choosable
Reworks the Storage step: two labelled choices with tooltips at the top —
LibrePortal and New apps — and the drive list underneath as reference.

The drive cards lose their checkboxes. Choosing a drive in a dropdown IS
the request to register it, so a separate tick was a second way to say the
same thing, and the way you end up with a drive ticked that nobody
selected. Cards are now informational plus Details.

Both dropdowns only render when there is a second drive; with one disk
both answers are forced and a pair of selects showing one option each is
furniture.

Moving LibrePortal's own tree cannot be a WebUI action. It re-bakes the
six root-owned helpers, the systemd unit and the WebUI's own bind-mounts
— real root, not the scoped sudo the manager holds. A helper that re-baked
the other helpers from a manager-supplied path would hand the manager
exactly the trust boundary those helpers exist to defend. So picking a
different disk for LibrePortal surfaces the root command to run rather
than pretending the wizard can do it; the payload carries the choice so
the finish screen can repeat it. libreportal-relocate follows.

Also drops "itself" from the installer's wording.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 06:39:25 +01:00
librelad
3034eaf4c7 feat(install): choose system and app-data disks independently; theme the dropdown
Two fixes.

The wizard's "new apps store their data on" dropdown was a bare native
<select>. The OS draws that popup and ignores our CSS, which is why it
came out as stock white chrome — the WebUI already solves this with
custom-select.js, which enhances any select.form-control into a themed
button and list. It just needed the class.

And the installer now asks for the two roots independently rather than
only app data. I had argued one question was simpler, and for a desktop
it is — the control plane is ~20 MB and moving it gains nothing. But on a
small board with an 8 GB eMMC and a USB SSD you want both moved, and
there was no way to say so without knowing the flags exist. Still one
disk list and two short questions; each is skipped if its flag was
already passed.

Fixed a bug the test caught immediately: _initAskDisk returns the chosen
path on stdout, and it was printing the prompt there too, so the question
text became part of the answer — the system root ended up named after its
own prompt. Prompts go to stderr now, stdout is the return channel.

Verified every combination under a pty: both default, apps only, both
moved, system only, and invalid-then-valid.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 03:48:55 +01:00
librelad
8d41b98497 feat(install): ask where app data goes, like an OS installer
--containers-dir has existed for a while and nobody running the curl|bash
installer ever learned it existed, so a box with a 4 TB disk beside a
small system SSD quietly put everything on the SSD. The installer now
asks, once, in the shape an OS installer asks it.

Deliberately narrow, because a question you answer badly is worse than no
question:

  * app data only. The control plane is ~20 MB and never grows; the thing
    worth placing is the data. Offering all three roots would be three
    questions nobody can answer at that moment.
  * a SUBDIRECTORY on the chosen disk, never its mount point — that keeps
    the "root only ever takes an empty directory" rule intact and leaves
    whatever is already on the disk alone.
  * candidates exclude anything on the same filesystem as /, since placing
    data there gains nothing.

It stays out of the way: skipped when unattended, when --containers-dir
was passed, when there is no TTY, and when the scan finds nothing else —
a prompt with one possible answer is not a question. Self-contained
(findmnt only), since scripts/ is not necessarily loadable that early.

Verified all six paths under a pty: default, valid pick, out-of-range,
non-numeric, no TTY, unattended, explicit flag, and no candidates.

Docs updated: the installer prompt is shown, and the section that said
locations are fixed after install now explains what actually is fixed
(the three roots) versus what isn't (per-app placement, storage add, and
CFG_STORAGE_DEFAULT).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 03:33:44 +01:00
librelad
c11052b753 fix(mastodon): make tag annotations substitutable
The tag manager reads `#LIBREPORTAL|<TAG>|<VALUE>` and takes <VALUE> as the
current literal to search for on that line, so it must equal the string in the
line body. Mastodon used `unconfigured` as the annotation value against bodies
like `PASSWORD_TAG_1_DATA` — nothing matched, nothing was ever substituted, and
the placeholder shipped as the live database password, SECRET_KEY_BASE, OTP
secret and VAPID keypair.

Nothing caught it either: `unconfigured` doesn't match `_DATA`, so
tagsManagerGetTagState reported the tags as configured, and the stale-tag gate
in dockerComposeUp (which tests the annotation value against
`^[A-Z][A-Z0-9_]*_DATA(_[0-9]+)?$`) let the app start.

Adopt the convention every other app already uses — body placeholder identical
to the annotation value, `<KIND>_DATA_<n>`. All 11 tags now substitute, the app
and postgres services agree on the same generated credentials, and an unfilled
tag is visible to the pre-start gate.

Existing 0.1.0 installs keep their literal credentials until re-installed, and
their Postgres was initialised with them, so a plain re-install desynchronises
the compose from the volume. Document both recovery paths in upgrade notes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 05:36:28 +01:00
librelad
30612a0d87 docs: organize docs/ into purpose folders with consistent naming
Sort docs/ into guide/ contributing/ architecture/ roadmap/ and rename
to consistent kebab-case (USER->guide/install-and-use, FOOTPRINT->
architecture/system-footprint, frontend-modularization->architecture/
webui-architecture, etc.). Add a docs/README.md index and a docs/
CONTRIBUTING.md pointer so the forge still surfaces the contributing
guide. Fix every reference (README, init.sh comments, frontend code
comments, and the USER<->DEVELOPMENT cross-links). History preserved
via git mv. Root stays README.md + CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-31 00:48:38 +01:00