Commit graph

99 commits

Author SHA1 Message Date
Roland
19f4958fb5 zim: ship a default fallback Zim and honor the configured Zim directory
Two related gaps in how WROLPi serves Kiwix content:

1. A fresh WROLPi (or one whose zims are all corrupt/absent) showed a blank
   Kiwix.  Every image now ships a small built-in default Zim that Kiwix falls
   back to, telling the user to download real Zim files.

2. The startup scripts hardcoded the `/zims` directory, ignoring wrolpi.yaml's
   `zims_destination` (which the Python side already honors).

Changes:
- New wrolpi/scripts/read_config_value.sh: a zero-dependency (awk) reader so
  non-Python scripts can reliably read top-level wrolpi.yaml scalars.  Also a
  config_value() wrapper in wrolpi/scripts/lib.sh.
- New wrolpi/scripts/default_zim/: HTML source + a reproducible builder
  (ghcr.io/openzim/zim-tools) that outputs the committed wrolpi/blobs/default.zim.
- New wrolpi/blobs/: any committed blob here is copied to /opt/wrolpi-blobs by
  the pi-gen and debian-live builds; the Docker zim image bakes it in.
- start_kiwix_serve.sh honors zims_destination and falls back to the default
  Zim when no functioning zim is found.
- docker/zim/entrypoint.sh gains the same default-Zim fallback.  It needs no
  config read: it already scans the whole mounted media dir, and
  zims_destination is always a subdirectory of that mount.
- Changing zims_destination via the settings API now restarts Kiwix so it
  serves the new directory without a reboot.

Tests: new test_read_config_value.py, extended test_start_kiwix_serve.py
(configured dir + default-Zim fallback), and a root_api test for the restart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 19:09:57 -06:00
Roland
575d2f9744 image builds: reset repo to HEAD before stamping the app build
The pi-gen and Debian Live chroot hooks run `npm install`, which rewrites
the tracked app/package-lock.json, then build_app.sh stamps
build/.build-stamp from that dirty working tree.  On first boot repair.sh
runs `git reset --hard` (reverting the lock) *before* it recomputes the
stamp, so the baked stamp can never match -- and the device rebuilds the
React app every time, the exact OOM-prone step baking the build in (#536)
was meant to avoid.

Verified against WROLPi-v0.27.2: the shipped image had `M app/package-lock.json`,
baked stamp a9c6ef61..., post-reset stamp 2b908c6c... (guaranteed mismatch).

Mirror repair.sh in both image hooks: `git reset --hard HEAD` right after
npm install and before build_app.sh, so the baked stamp represents committed
source and matches what the device recomputes.  Only the lock is reverted;
build/, node_modules, and the venvs are untracked and survive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 13:39:50 -06:00
Roland
4cafb9f00e
Remove stale Postgres references after SQLite cutover.
Drop .pgpass handling, Postgres ownership repairs, and the docker db
container viewability rules. Update comments and pi-gen map docs for
SQLite/PMTiles. Help submodule documents the new database layout.
2026-07-18 13:28:54 -06:00
lrnselfreliance
899ab13964
Consolidate map-tool install into wrolpi/scripts/install_map_tools.sh (#538)
The pmtiles CLI + tippecanoe install was duplicated across three places
(scripts/install_protomaps.sh, the Debian Live hook, and pi-gen), so a version
bump or logic change had to be made in all of them in lockstep. Move it into a
single shared, version-guarded, SHA-pinned installer and have all three callers
invoke it.

- New wrolpi/scripts/install_map_tools.sh: installs pmtiles (per-arch binary,
  SHA-pinned for amd64 and arm64) and builds tippecanoe (source tarball,
  arch-independent SHA). Version-guarded, so an image that already baked in the
  pinned versions makes the on-device run a no-op until a version bumps.
- pi-gen 03-run-chroot.sh, the Debian hook, and install_protomaps.sh now just
  call the shared script. This also adds SHA pinning to the on-device pmtiles
  path (previously unpinned).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:55:06 -06:00
lrnselfreliance
f621e0bd5c
Bake tippecanoe into the Raspberry Pi image (#537)
The Debian Live image builds tippecanoe at image time, but pi-gen did not,
so a fresh Pi compiled it on first boot/upgrade via install_protomaps.sh --
a multi-minute, CPU-heavy C++ build on the weakest hardware WROLPi targets.
This is the same "Debian bakes it, pi-gen doesn't" gap recently fixed for
the React app build.

Build tippecanoe 2.79.0 in the stage2 chroot, mirroring the Debian hook
(SHA-pinned source tarball, which is arch-independent). Add the two missing
build deps (libsqlite3-dev, zlib1g-dev) to the package list; build-essential
was already present.

With tippecanoe present at the pinned version, install_protomaps.sh's version
guard skips the build on first boot and on upgrades, so the on-device compile
only recurs when the pinned version actually bumps.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:48:25 -06:00
lrnselfreliance
944d95c717
Bake the app build into images; rebuild on-device only when stale (#536)
* Bake the app build into images; rebuild on-device only when stale

Previously the Raspberry Pi image shipped without app/build/ and compiled
the React bundle on first boot via repair.sh -- a multi-minute, memory-hungry
step that can OOM a 2GB Pi.  The Debian Live ISO already baked the build in,
so the two paths had drifted.

Add scripts/build_app.sh as the single source of truth: it hashes the build
inputs (src/, public/, package.json, package-lock.json, tsconfig.json) and
runs `npm run build` (with GENERATE_SOURCEMAP=false) only when build/ is
missing or those inputs changed, then writes build/.build-stamp.

Wire all three call sites to it:
- pi-gen 03-run-chroot.sh now builds after npm install, baking build/ into
  the Pi image like the Debian hook already does.
- debian-live 9999-wrolpi.hook.chroot uses it so the baked build is stamped.
- repair.sh uses it, so first-boot/manual-repair/install/upgrade all become
  no-ops when the shipped build is current.

Result: fresh images ship a stamped build/, so a weak Pi never compiles on
boot or repair; an on-device rebuild happens only when an upgrade actually
changes app/.  build_app.sh never runs npm install, so repair.sh keeps its
offline guarantee.

* build_app.sh: rebuild when built output is incomplete, not just missing

Addresses review feedback: a matching stamp only proves the inputs are
unchanged, not that the output survived.  repair.sh exists to fix corruption,
so a build/ with index.html + stamp but a missing JS/CSS asset must still
rebuild.  Verify every file referenced by build/asset-manifest.json exists
(and that the manifest itself parses); rebuild otherwise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:19:25 -06:00
Roland
e01795ad7e
Debian 13 (trixie) rebase: docker, debian-live-config, and pi-gen builds.
All three build targets move from Debian 12/bookworm to Debian 13/trixie
for fresh installs; existing Debian 12 systems remain supported in place.

Docker: api/help on python:3.13-trixie, controller on 3.13-slim-trixie,
app on node:20-trixie (matches trixie's nodejs 20), samba/zim on
debian:13.  Full backend suite passes on Python 3.13 in the api container.

debian-live-config: --distribution trixie; postgres teardown for the
SQLite cutover (pg_dropcluster hook block removed, wrolpi-bootstrap.sh no
longer creates a cluster or calls the deleted initialize_api_db.sh — the
API creates wrolpi.db itself on first start).  README documents the
SQLite layout and drops Etcher recommendations.

pi-gen: upstream has no trixie branch — the arm64 branch now defaults to
RELEASE=trixie; KEYBOARD_KEYMAP=en_US is rejected by trixie's
console-setup (ckbcomp validates), use 'us'; stop stamping a Postgres
.pgpass into /etc/skel.

Package lists (both targets): postgresql-15/postgresql-doc/libpq-dev
deleted; libtiff5-dev renamed to libtiff-dev; software-properties-common
and cpufrequtils removed from trixie (Controller CPU throttle falls back
to sysfs, dead cpufreq-set sudoers rule removed); locales and gnupg added
(no longer arrive transitively under --apt-recommends false); sqlite3 CLI
added for reset_tags.sh.

help.sh: replace the Postgres service/port/socket/cluster/psql checks
(all false FAILEDs after the SQLite cutover) with SQLite checks:
wrolpi.db exists, PRAGMA quick_check, file_group table + row count, run
as the wrolpi user so root never creates the -shm sidecar.

Both build scripts take a WROLPI_BUILD_DIR override so development builds
can run beside the release pipeline's build trees; the sudo re-exec
passes it through explicitly (a plain sudo would drop it).

test_common.py: the truncate tests hard-coded sys.getsizeof results from
the 3.11 object layout (str/list shrank in 3.12); assert the contract
(fits budget, prefix, monotonic in budget) so they pass on 3.11 and 3.13.

Verified: trixie ISO (3.7G) and pi-gen desktop image (4.3G) both built
from scratch and passed their sanity checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:43:08 -06:00
Roland
02eb8d70e4
Caddy install: run gpg --dearmor with --batch --yes.
If the caddy keyring file already exists (left by a prior partial
install), gpg prompts to overwrite via /dev/tty, which fails under the
tty-less wrolpi-upgrade.service ("cannot open '/dev/tty'") and aborts
the upgrade. --batch --yes overwrites silently, making the caddy
install idempotent in all four scripts that share this pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 13:03:11 -06:00
lrnselfreliance
1acb148c90
Single file deno (#478)
* Singlefile runs under Deno 2.8.3; browser leftovers are reaped.

single-file-cli is designed for Deno.  Under node 18 (Debian 12/RPi OS) it
cannot reach the browser CDP on IPv6-enabled hosts ("localhost" resolves to
::1 first with no IPv4 fallback) and it never exits after a successful save,
blocking the download worker forever.

The native path and the Archive docker container now run single-file with
"deno run".  Deno is pinned at v2.8.3 everywhere because yt-dlp 2026.06.09
requires at least 2.3.0 (our node is Debian's v18, below yt-dlp's v22
minimum).  Deno >= 2.3 kills the browser single-file spawns (piped stderr
that single-file never reads), so the browser is run through
singlefile_browser_wrapper.sh which discards its stderr.

Commands are run in their own process group which is killed when they
finish, reaping the browser single-file leaves behind.  The Archive
downloader now has a ten minute timeout.  Install scripts install Deno
(previously only upgrade.sh and the image builds did).

* Singlefile writes to a file instead of dumping to stdout.

Deno >= 2.3 truncates large --dump-content output at exit; compressed
(SingleFileZ) singlefiles of large pages lost their ZIP tail, failed the
compression check in write_archive_files, and were destroyed by the HTML
prettifier (blank white archives).  single-file now writes its result to a
temporary file which WROLPi reads back.

As insurance, a singlefile marked compressed (<html data-sfz) whose ZIP is
unreadable is now stored verbatim instead of being prettified.

* Compressed Archive screenshots and readability images are no longer broken.

The index.html inside a compressed (SingleFileZ) singlefile references its
resources by relative ZIP paths.  Screenshots are now taken from the fully
extracted ZIP so those references resolve.  Readability images are inlined
as data URIs read from the ZIP (readability-extractor absolutizes relative
srcs against the page URL, so both forms are matched).

* Compressed Archives are created by converting the uncompressed capture.

The page is always captured uncompressed; readability, screenshot, and
title come from that canonical page (its images are inline data URIs, so
no ZIP extraction or image inlining is needed).  When compress_singlefile
is set, a second single-file run converts the captured file into a
compressed (SingleFileZ) self-extracting file.  The conversion is a local
file:// operation - the page is not fetched again, and single-file
preserves the original URL header when re-saving its own pages.  If the
conversion fails, the uncompressed singlefile is stored instead.

* Consolidating duplicate compressed-singlefile tests.

The write_archive_files tests (verbatim write, title fallback, broken-ZIP
guard, readability inlining) are combined into one test; the helper probes
and the setting-plumbing tests are each merged into one.  The do_singlefile
compression-failure scenario and the timeout constant are folded into the
main do_singlefile test, which shared their setup.

* Addressing review comments: removeprefix, PermissionError when killing.

* Bumping version to 0.24.1-beta.

* Install scripts assert the deno.zip hash to avoid malicious or invalid files.

The hashes are the official sha256sum assets of the Deno v2.8.3 release,
per architecture.

* Archive Dockerfile asserts the deno.zip hash to avoid malicious or invalid files.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 17:07:28 -06:00
Roland
68d8cd39c1
Including hdparm for drive handling.
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Controller Tests / controller-tests (push) Waiting to run
Jest Tests / jest-tests (push) Waiting to run
2026-06-01 19:31:17 -06:00
Roland
3929642167
pi-gen: clear leftover BUILD_DIR at startup (robust to killed builds)
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Controller Tests / controller-tests (push) Waiting to run
Jest Tests / jest-tests (push) Waiting to run
A SIGTERM/kill (e.g. `systemctl stop`) skips the EXIT-trap cleanup, leaving
/var/tmp/wrolpi-build-pi-gen behind. The next build's `mv pi-gen-bookworm-arm64
wrolpi-build-pi-gen` would then nest into the stale dir and fail. Remove the
leftover (and intermediate zip/unzip dir) at startup, mirroring the debian
build, stripping immutable flags off blob files first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:06:31 -06:00
Roland
5625f17b5b
pi-gen: set HOME before git config --global in the chroot (systemd fix)
When the release runs under the wrolpi-release systemd service, HOME is unset,
so `git config --global --add safe.directory /opt/wrolpi` in the pi-gen chroot
aborts with "fatal: $HOME not set" and the build fails at stage 3. Manual
shell builds worked only because the shell provided HOME. Export HOME=/root in
the chroot hook (self-contained), and also set Environment=HOME=/root in the
service unit template.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 12:15:32 -06:00
Roland
0e3564b704 Automating build and upload of WROLPi release images. 2026-05-30 14:23:08 -06:00
lrnselfreliance
8f0e4cd93f
Adding support for mounting NTFS drives in the Controller. (#440) 2026-05-15 19:25:43 -06:00
Roland
e0324ce0fc Migrating Controller from port 8087 to 80 so that it is more discoverable. Reformatting Controller dashboard to make header at the top. Reformatting navigation bar to handle the new links which prevented Admin from being visible in some screens. 2026-04-09 15:01:05 -06:00
Roland
b8a532c8d9
Key changes:
- Add MapLibre GL JS viewer with light/dark themes, terrain layer,
  and embedded static map page for coordinate sharing
- Add map pin support with persistent map_pins.yaml config
- Add map region subscriptions and downloader with progress tracking,
  sourcing pre-extracted PMTiles from a cold CDN
- Add map export to PNG and GPS coordinate copy via context menu
- Add MapViewer React component alongside reworked Map page
- Add map catalog and region-building script (build_map_regions.py)
- Remove OSM import/render scripts, leaflet files, Apache configs,
  renderd service, and osm-map-dumper Docker container
- Update Caddyfile to serve PMTiles and map static assets
- Update build/install/repair scripts for Protomaps dependencies
- Drop map_file table, add/drop map_subscription table (alembic)
- Adding button to skip primary drive mounting during onboarding.
2026-04-04 13:48:19 -06:00
Roland
ce0cdd3a2e Samba is installed and can be configured in the Controller. 2026-03-29 10:19:28 -06:00
Roland
aefc7b1def
Add Docs module for searching and organizing PDFs and EPUBs
Create a new Docs module that consolidates PDF and eBook handling into a
unified system with subjects and authors support. This replaces the
separate PDF/eBook modelers with a single Doc model backed by an
alembic migration (rename ebook to doc).

Key changes:
- New modules/docs package with models, API, extractors, and lib
- Subject and author metadata extraction with case-insensitive
  normalization for cleaner PDF metadata
- Frontend Docs pages with AuthorsPage, SubjectsPage using
  CollectionTable, and doc card deletion
- Global search integration for subjects and authors
- Generic collection item count support
- Frontend file card metadata adjusted to match collection sort
- Bump yt-dlp version
2026-03-20 14:17:06 -06:00
Roland
fbb71e012d Replace nginx with Caddy for TLS termination and reverse proxying.
- Replace nginx with Caddy as the web server for both Docker and native
  (RPi/Debian) deployments. Caddy handles HTTPS termination, reverse
  proxying to all services (API, React app, Controller, Kiwix, Help, Map),
  and serves the media/download file browsers.
- Generate a Root CA and signed leaf certificates for HTTPS. The Root CA
  uses Name Constraints to restrict signing to local addresses only
  (.local, localhost, and RFC 1918 private IP ranges), mitigating MITM
  risk if the CA key is compromised.
- Add an HTTP landing page that guides users through one-time CA
  certificate installation on iOS, Android, macOS, Windows, Linux, and
  Firefox, with auto-redirect when the cert is already trusted.
- Add trailing-slash redirects for /controller, /docs, /media, and
  /download so bare paths don't fall through to the React catch-all.
- Add a Root CA download link on the Settings page under Configs.
- Update help.sh diagnostics for IPv6 listener detection and HTTP/2
  status codes.
- Remove all nginx config files and the https_proxy Docker container.
- Bump wrolpi-help submodule with certificate documentation.
2026-03-07 18:48:44 -07:00
Roland
ebac4fdfa2
Removing redundant web app build during image builds. 2026-02-27 17:22:46 -07:00
Roland
ae72b9491d
Fetching latest release version when building images so they are named correctly.
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Controller Tests / controller-tests (push) Waiting to run
Jest Tests / jest-tests (push) Waiting to run
2026-02-26 17:45:39 -07:00
Roland
8fab32ef75
Copying WROLPi nginx config on first startup so Controller is accessible. 2026-02-26 17:38:36 -07:00
Roland
f375e4c955
Generating nginx certs on first startup. 2026-02-24 17:14:56 -07:00
Roland
c6393437af
Building pi and debian images using branch by default, allowing specifying branch with -b option.
Some checks failed
CodeQL / Analyze (push) Has been cancelled
Controller Tests / controller-tests (push) Has been cancelled
Jest Tests / jest-tests (push) Has been cancelled
2026-02-22 15:09:15 -07:00
Roland
c682b43f7c
Cleanup is now done in the cleanup function, remove this duplicate. 2026-02-22 14:32:11 -07:00
Roland
b8d3b5318b
Removing blob file protection during build cleanup.
Some checks failed
CodeQL / Analyze (push) Has been cancelled
Controller Tests / controller-tests (push) Has been cancelled
Jest Tests / jest-tests (push) Has been cancelled
2026-02-16 08:14:44 -07:00
Roland
adb8fefd67
Including Deno and curl-cffi which are now required for yt-dlp functionality. Installing both of these packages during upgrade. 2026-02-16 08:08:46 -07:00
Roland
50782f47cc
Checking that map dump is valid before building image. Protecting map
dump file in built image so that it cannot be modified.
2026-02-15 14:49:19 -07:00
Roland
56bdd0cf75
Starting controller on first startup.
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Controller Tests / controller-tests (push) Waiting to run
Jest Tests / jest-tests (push) Waiting to run
2026-02-14 17:38:12 -07:00
Roland
179f88d00b
Updating pi-gen build to use bookworm-arm64 branch.
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Controller Tests / controller-tests (push) Waiting to run
Jest Tests / jest-tests (push) Waiting to run
2026-02-11 17:38:04 -07:00
Roland
51b8972de9
Setting locale to avoid perl warnings. 2026-02-01 14:35:56 -07:00
Roland
b9158b60fe Creating Controller service which will control WROLPi services, admin, SMART, disks, and status. nginx will fallback to Controller UI when React UI is unavailable (such as during upgrades). 2025-12-15 15:48:40 -07:00
Roland
24a0646f99
Updating MOTD to suggest ip addresses on login.
Some checks failed
CodeQL / Analyze (push) Has been cancelled
2025-08-18 11:33:44 -06:00
Roland
23551d56ac
Installing and enabling iperf3 for terminal speed testing of the network. 2025-05-14 18:58:22 -06:00
Roland
7bd473666d
Trying pip install multiple times during pi-gen build to avoid pypi issues. 2025-03-21 18:13:43 -06:00
Roland
104d2ec6a7
Adding serve.json so epub.html is not rewritten by serve. Buildling app during pi-gen/debian image builds. 2025-03-13 19:34:28 -06:00
Roland
612682f3bf
Install gpsd on all systems. 2025-03-02 10:03:42 -07:00
Roland
531ec3d65e
Creating new /api/archive/upload which accepts a file from the SingleFile browser extension and converts it to an Archive. 2025-01-30 18:41:32 -07:00
Roland
b49cedd62a
Forcing utf8 for map database. 2025-01-22 08:33:33 -07:00
Roland
d8b3db9d63
Using iostat to detect busy disk issues, reporting them in the UI. Detecting under-voltage/over-current issues using dmesg and reporting them in the UI. 2025-01-10 11:33:21 -07:00
Roland
a9fbf7b384
Changing RPi WPA Country to avoid wifi warning. 2025-01-05 12:04:57 -07:00
Roland
11bdbc4dc7
Creating map db cluster in pi-gen build. Removing duplicate pgpass creation. 2025-01-04 10:51:14 -07:00
Roland
8e4d173791
Converting map blob to use "custom" postgres dump format so restore can use multiple threads. Renaming map db blob from gis-map.dump.gz to map-db-gis.dump. 2025-01-02 12:04:03 -07:00
Roland
2dd243230f
Adding two MOTD, one for before repair, one that is moved in after repair is successful. 2024-10-16 18:57:08 -06:00
Roland
b3380aeacb
Trimming video file names when downloading. Using yt-dlp error to get filename to help yt-dlp extract info json. 2024-10-16 18:56:53 -06:00
Roland
6c04e63637
Updating pi-gen readme to include branch instructions. 2024-10-16 18:56:47 -06:00
Roland
3d12c29cf2
Changing pi-gen build to use branch variable. 2024-10-16 18:56:46 -06:00
Roland
53d2402d32
Only files that have been tagged will be returned when new 'Any' tag button is used to filter file search results. 2024-10-16 18:55:56 -06:00
Roland
0227705179
Adding git safe directories to system skel. 2024-10-16 18:55:39 -06:00
Roland
00c4e6ece0
nginx needs to serve downloads from the media directory. 2024-10-02 17:10:05 -06:00