diff --git a/.gitattributes b/.gitattributes index c6af598..e231bc8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,6 +4,7 @@ scripts/unused export-ignore scripts/release export-ignore site export-ignore +docs export-ignore .claude export-ignore .gitignore export-ignore .gitattributes export-ignore diff --git a/README.md b/README.md index a1da469..e18aa06 100755 --- a/README.md +++ b/README.md @@ -34,11 +34,22 @@ telemetry. See [our Promise](PROMISE.md) for exactly what that means. ## Quick start ```bash -git clone https://gitea.scottwebstar.co.uk/Webstar/LibrePortal.git -cd LibrePortal -./init.sh +curl -fsSL https://get.libreportal.org/install.sh | sudo bash ``` +This installs a versioned, checksum-verified release (Debian/Ubuntu, root). Put +data on separate disks with `--system-dir=` / `--containers-dir=` / `--backups-dir=`. + +> The `get.libreportal.org` host is still being set up — until it's live, build a +> release and install from it locally (see the docs below). + +## Documentation + +- **[docs/USER.md](docs/USER.md)** — install, place data on separate disks/drives, + update, back up, uninstall. +- **[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)** — run a dev copy, cut stable/edge + releases, and test them before publishing. + ## LibrePortal Connect (optional) Self-hosting is free and complete. If you'd rather not fiddle with the tricky diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000..d3bc92e --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,128 @@ +# LibrePortal — Development & Releases + +How to run a development copy, cut stable/edge releases, and test them before they +go out. For installing/using LibrePortal, see [USER.md](USER.md). + +## Mental model (read this first) + +**Install modes** — `CFG_INSTALL_MODE` decides where the code comes from: + +| Mode | Source | Use | +|---|---|---| +| `release` (default) | a checksum-verified `.tar.gz` over HTTPS | end users / stable | +| `git` | `git clone` of the repo | contributors tracking a branch | +| `local` | a copy of a local folder | hacking on the code on the box | + +**Three roots** (each relocatable at install, then fixed): +`` (manager-owned: configs/db/logs/install) · `` (container +user: app data) · `` (container user: repos). Defaults `/libreportal-*`. + +**Two users:** the **manager** (`sudo_user_name`, default `libreportal`) owns the +control plane and runs the runtime; the **container user** (`CFG_DOCKER_INSTALL_USER`, +default `dockerinstall`) owns app data + runs rootless Docker. Genuine-root actions +go through fixed, root-owned helpers in `/usr/local/lib/libreportal/` (paths + +manager name are *baked* into them at install — never read from runtime config). + +**Key files:** +- `init.sh` — the installer (self-contained; creates users/folders/helpers, bakes things). +- `install.sh` — the thin bootstrap (download+verify+extract a release, then run init.sh). +- `scripts/source/paths.sh` — resolves the three roots + manager user. +- `scripts/source/fetch.sh` — `lpFetchRelease` / `lpVersionGt` (runtime fetch + version compare). +- `scripts/release/make_release.sh` — builds release artifacts. +- `VERSION` — the single source of the version number. + +## Run a development copy + +From a clone of the repo, on a **throwaway Debian/Ubuntu host** (install is +destructive — it creates system users and dirs): + +**Local mode** (install from the working tree — best for hacking): +```bash +sudo ./init.sh --random-password --local init +# custom locations work in dev too: +sudo ./init.sh --random-password --local --system-dir=/srv/lp --manager-user=lpadmin init +``` + +**Git mode** (track a branch): +```bash +# via the bootstrap: +sudo ./install.sh --git-url=https://example.com/you/LibrePortal.git \ + --git-user=USER --git-token=TOKEN +# or directly: ./init.sh init true git +``` + +**Iterating:** re-run the installer to redeploy after changes (local mode re-copies +the tree). To wipe and start over: +```bash +sudo ./init.sh uninstall # removes the three roots + users + footprint +sudo ./init.sh --skip-docker-images uninstall # keep the Docker layer for a fast reinstall +``` + +> This repo's CI/hook setup may auto-deploy on commit (commit on a branch → +> auto-merge → redeploy). That's environment-specific; the commands above are the +> portable way to stand up and refresh a dev box. + +## Cut a release (stable or edge) + +1. **Bump the version** in `VERSION` (semver, e.g. `0.2.0` → `0.3.0`). Commit it. +2. **Build the artifact** (uses `git archive`, so it ships only committed files and + honours `.gitattributes export-ignore` — `scripts/unused`, `site`, `docs`, + `.claude`, the release tooling, etc. never ship): + ```bash + scripts/release/make_release.sh stable # or: edge [git-ref] + ``` + Produces, under `dist//`: + - `libreportal-.tar.gz` — the release + - `libreportal-.tar.gz.sha256` — its checksum + - `latest.json` — `{ version, channel, url, sha256, notes }` (the channel pointer) +3. **Publish** by copying `dist//*` to the host so they're served at + `https://get.libreportal.org//…` (Phase E — the `getlibreportal` + container; not built yet). `latest.json` is what makes a version "the latest". + +**Channels:** `stable` is the default users get; `edge` is for early/testing +builds. Same tooling, different ``. To promote an edge build to stable, +rebuild with `make_release.sh stable` at that ref (or copy its artifacts into the +`stable/` path and update `stable/latest.json`). + +## Test a release locally before publishing + +No hosting needed — serve `dist/` and point an install at it: + +```bash +# build, then serve the artifacts +scripts/release/make_release.sh stable +( cd dist && python3 -m http.server 8000 ) + +# on a throwaway host, install from your local server: +LP_RELEASE_BASE_URL=http://:8000 \ + sudo ./install.sh --channel=stable --system-dir=/libreportal-system +``` + +`LP_RELEASE_BASE_URL` overrides the release host everywhere (installer, updater, +recovery). Quick non-destructive checks: + +```bash +# fetch + verify + stage only (no install): +LP_RELEASE_BASE_URL=http://:8000 ./install.sh --dry-run +# tamper with the tarball and confirm it's refused: +echo x >> dist/stable/libreportal-*.tar.gz +LP_RELEASE_BASE_URL=http://:8000 ./install.sh --dry-run # => CHECKSUM MISMATCH +``` + +## How updates work (so you can reason about them) + +In `release` mode the WebUI badge + `libreportal update apply` compare the local +`VERSION` against the channel's `latest.json` (`lpVersionGt`); if newer, they +`lpFetchRelease` the new tarball (verified) and redeploy. Because the install tree +is *code only* (configs/logs/backups live in the other roots), the update just +replaces it — no backup/restore dance. `git`/`local` modes keep their existing +git-based update path. + +## Conventions + +- **Versioning:** semver in `VERSION`. Bump before building; `latest.json` carries it. +- **New runtime script?** Add it under `scripts//…` and run + `scripts/source/files/generate_arrays.sh run` so it's sourced (build/standalone + tooling under `scripts/release` and `scripts/system` is intentionally excluded). +- **Don't** make the OS footprint (`/etc/*`, `/usr/local/*`) relocatable — it's + fixed by design for the privilege model. diff --git a/docs/USER.md b/docs/USER.md new file mode 100644 index 0000000..70d2138 --- /dev/null +++ b/docs/USER.md @@ -0,0 +1,97 @@ +# LibrePortal — Install & Use + +How to install, place, update, and remove LibrePortal. For building releases or +running a dev copy, see [DEVELOPMENT.md](DEVELOPMENT.md). + +> **Note:** the `get.libreportal.org` host isn't live yet. Until it is, install +> from a local release artifact or a git/local checkout — see +> [DEVELOPMENT.md](DEVELOPMENT.md). The commands below are the intended public flow. + +## Requirements + +- A **Debian** or **Ubuntu** host. +- **root** (run with `sudo`). +- `curl` (or `wget`) and `tar`. + +## Install + +```bash +curl -fsSL https://get.libreportal.org/install.sh | sudo bash +``` + +This downloads a versioned, **checksum-verified** release tarball (no git, no +login), installs LibrePortal, and prints the WebUI address + a generated password +(also saved to the install log). To choose the password yourself add +`--password=…`. + +### Put data where you want it (separate disks, external drives) + +LibrePortal uses **three independent roots**, each can be its own path/disk: + +| Flag (default) | Holds | Owner | +|---|---|---| +| `--system-dir=/libreportal-system` | configs, database, logs, install | the manager user | +| `--containers-dir=/libreportal-containers` | live app data | the container user | +| `--backups-dir=/libreportal-backups` | backup repositories | the container user | + +```bash +curl -fsSL https://get.libreportal.org/install.sh | sudo bash -s -- \ + --system-dir=/mnt/ssd/libreportal \ + --containers-dir=/mnt/ssd/libreportal-apps \ + --backups-dir=/mnt/bigdisk/libreportal-backups +``` + +Notes: +- Defaults are top-level dirs on purpose — they avoid the permission/encryption + pitfalls of living inside a user's home. To put `containers`/`backups` inside + `/home/` you must add `--allow-home` (it needs the container user to + traverse that home — a small privacy trade-off). +- Other flags: `--manager-user=NAME` (default `libreportal`), + `--channel=stable|edge` (default `stable`), `--version=X.Y.Z` (pin a version). + +## Where things end up + +``` +/ configs/ logs/ install/ database.db ssl/ ssh/ +/ one folder per installed app (+ the libreportal WebUI) +/ one folder per backup location +``` + +The locations are chosen at install and fixed afterward (changing them is a +deliberate reinstall, not a setting — this is part of the security model). + +## Update + +LibrePortal checks its channel for a newer version and shows a badge in the WebUI +when one is available. Apply it from the dashboard, or on the host: + +```bash +libreportal update apply # update now if a newer version exists +libreportal update check # just re-check the channel +``` + +Updates download + verify the new release tarball and redeploy. Your data, +configs, and backups are untouched (they live outside the replaced install tree). + +## Backups on an external / removable drive + +Point a backup location at the drive's mount path. For a removable disk, set +**Require Mounted Drive** on the location (config key +`CFG_BACKUP_LOC__REQUIRE_MOUNT=true`): LibrePortal then **refuses to back up +when the drive isn't mounted**, so an unplugged disk never silently fills your +system disk. Use a Linux filesystem (ext4/xfs/btrfs) — FAT/exFAT/NTFS can't hold +the required ownership and will warn. + +## Uninstall + +```bash +sudo /libreportal-system/install/init.sh uninstall +``` + +This removes the three roots, the system users, and the small out-of-tree +footprint (`/usr/local/lib/libreportal`, the `/etc` integration files). Add +`--skip-docker-images` to keep the rootless Docker layer + image cache for a fast +reinstall. + +> ⚠️ Uninstall permanently deletes all app data, configs, and the database. Take a +> backup first if you want to keep anything.