diff --git a/CHANGELOG.md b/CHANGELOG.md index f8d57af..d700f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## Dawarich — the third app through the new-app checklist (2026-10-01, evening) + +- **`templates/dawarich/`** — your own location history (instead of Google Timeline): `freikin/dawarich:1.15.3` (web + + sidekiq), `postgis/postgis:17-3.5-alpine`, `redis:7.4-alpine` — upstream's own compose, pinned. The image seeds + `demo@dawarich.app` / `safepassword`; `after_install` sets the generated password through `bin/rails runner` (ARGV), + and the install hold keeps strangers out until then (measured: the first public answer 2 s after). Reverse geocoding + off; the page says the browser loads map tiles from Dawarich's tile server. `SECRET_KEY_BASE` is a data_key (encrypted + columns). The public login name locks for 1 h after 10 wrong tries — kept as mealie (decision 57); the first step tells + the household to set its own e-mail. App-email by `smtp_mapping`; mail OFF boots (measured). +- First ladder step 1.15.2 -> 1.15.3 proven on the bench (swap 0) and on 9202; remove + restore read the point back and + the household's own password still signs in. +- `onboarding/dawarich.md`, fixture `Dawarich`, FIRST-ADMIN row, README tables, Hungarian freeze. + ## Karakeep — the second app through the new-app checklist (2026-10-01, evening) - **`templates/karakeep/`** — bookmarks, articles, notes (formerly Hoarder): `ghcr.io/karakeep-app/karakeep:0.33.2` + diff --git a/FIRST-ADMIN.md b/FIRST-ADMIN.md index 8223565..c50b962 100644 --- a/FIRST-ADMIN.md +++ b/FIRST-ADMIN.md @@ -42,6 +42,7 @@ asks the probe first where there is one. Open sign-up is closed by the box after | **claper** | 3 (+ open sign-up) | seeds `admin@claper.co / claper` | (b) `bin/claper rpc … update_user_password` | **FIXED** — catalog, decision 45 | **M 9202**: default fails, generated works, a restore keeps it | | code-server | 1 | `PASSWORD` generated, applied every start | – | fine | R | | crafty-controller | 1 | `CRAFTY_PASSWORD` → default.json | – | fine | R | +| **dawarich** | 3 | `rails db:seed` creates `demo@dawarich.app` / `safepassword` | (b) `bin/rails runner` sets the generated password (ARGV); the install hold keeps strangers out until it succeeds | **NEW 2026-10-01** — catalog, `onboarding/dawarich.md` | **M 9202** (drill catalog): the first public answer 2 s after the password was replaced; default refused, generated signs in (303), wrong refused; registration off (302/422); 10 wrong tries lock the public name for 1 h (Devise; per-minute throttles first) — the mealie precedent (decision 57), the first step tells the household to set its own e-mail; the phone API key keeps working (`felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/`) | | **docmost** | 4 | first registered user is admin | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call; no JSON status before/after → button | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) | | **emby** | 4 | setup wizard | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) | | **ghost** | 4 | `/ghost/` setup | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call; its status exists but is a list (`setup[0].status`) — the box reads only objects → button | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) | diff --git a/README.md b/README.md index feb5fb3..21f3e61 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,7 @@ block + the matching compose `${VAR}` lines. | Claper | PostgreSQL | 100M / 384M | yes | -- | present.* | | Code-Server | None (file) | 200M / 1024M | no | -- | code.* | | Crafty Controller | None (file) | 256M / 2048M | no | -- | minecraft.* | +| Dawarich | PostgreSQL 17 (PostGIS) + Redis | 500M / 2688M | no | -- | timeline.* | | Docmost | PostgreSQL + Redis | 200M / 768M | no | -- | docs.* | | Emby | None (file) | 512M / 2048M | no | `${HDD_PATH}/media/` | emby.* | | FileBrowser Quantum | None (file) | 50M / 256M | yes | `${HDD_PATH}/storage/filebrowser/` | files.* | @@ -351,6 +352,7 @@ block + the matching compose `${VAR}` lines. | Claper | yes | -- | SECRET_KEY_BASE, DB_PASSWORD | | Code-Server | yes | -- | PASSWORD | | Crafty Controller | yes | -- | -- | +| Dawarich | yes | -- | SECRET_KEY_BASE (data_key), DB_PASSWORD, ADMIN_PASSWORD (password) | | Docmost | yes | -- | APP_SECRET, DB_PASSWORD | | Emby | yes | yes | -- | | FileBrowser Quantum | yes | yes | -- | diff --git a/onboarding/dawarich.md b/onboarding/dawarich.md new file mode 100644 index 0000000..e4d3676 --- /dev/null +++ b/onboarding/dawarich.md @@ -0,0 +1,72 @@ +# Onboarding record — dawarich + +app: dawarich +opened: 2026-10-01 +template_at: the commit that publishes this record (freikin/dawarich 1.15.3 ×2, postgis/postgis:17-3.5-alpine, redis:7.4-alpine) + + + +0.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/A/A1-github-facts.txt — AGPL-3.0; upstream's own image, pulled +0.2 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/A/A1-github-facts.txt — 1.15.3 on 2026-09-30; 56 releases in 2026 (expect frequent update steps) +0.3 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/FIT.md — plain `x.y.z` tags; the app is amd64/arm/arm64, postgis/postgis amd64 only +0.4 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt ; felhom.eu/documentation/audits/new-apps-2026-10-01/shots/dawarich/1.png — reverse geocoding OFF (no geocoder set), telemetry removed upstream, no outbound connection at rest but the internal ones; the browser loads map tiles from Dawarich's tile server (the page says so); an OPT-IN "What's New" widget would load from my.chibichange.com — it asks, off by default +0.5 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/install.txt — first start needs nothing but the images; the map's tiles need the internet in the BROWSER +0.6 | n/a | Dawarich serves HTTP on port 3000 only; the database and Redis stay internal +0.7 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/install.txt — the phone apps' route (Overland batches with the API key) through traefik after the install hold opened: 201, read back; no key / wrong key 401 +0.8 | done | app-catalog-felhom.eu/templates/dawarich/.felhom.yml — tagline + use_cases: instead of Google Timeline +0.9 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/bench/dawarich/evidence/MV-dawarich/verdict.json — runs on the bench; APPLICATION_HOSTS names the public name but the app serves any listed host +1.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/G/gates-dawarich.txt — image-pins and image-resolvable exit 0 +1.2 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/A/compose/dawarich.yml — `postgis/postgis:17-3.5-alpine` and `redis:7.4-alpine`, as upstream's compose (decision 42's rule) +1.3 | done | app-catalog-felhom.eu/templates/dawarich/docker-compose.yml — PostgreSQL 17: the data mount at `/var/lib/postgresql/data` is right below 18; engine-major gate exit 0 +1.4 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/bench/dawarich/evidence/MV-dawarich/verdict.json ; felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/step.txt — 1.15.2 seeded, stepped INTO 1.15.3; the entrypoints migrate at every start (seen in the log); read back on both venues +1.5 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt — puma (production) and sidekiq; RAILS_ENV production +1.6 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/S/S1-dawarich-reads.txt — SECRET_KEY_BASE and DB_PASSWORD generated (upstream's defaults are `CHANGE_ME` / `password`); the admin password replaced by after_install +1.7 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/S/S1-dawarich-reads.txt — the entrypoints (`web-entrypoint.sh`, `sidekiq-entrypoint.sh`) migrate and seed at every start; nothing to switch +1.8 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt — an unknown page is a plain 404 +1.9 | done | app-catalog-felhom.eu/templates/dawarich/.felhom.yml — SECRET_KEY_BASE is `data_key: true`: Dawarich encrypts columns with keys derived from it; the restore recovered it (data_keys=1, box/dawarich/restore.txt) +2.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/G/cvp-dawarich-tail.txt — volume-persistence gate CLEAN +2.2 | done | app-catalog-felhom.eu/templates/dawarich/docker-compose.yml — named volumes (NVMe): database, public, storage, watched imports, redis +2.3 | n/a | needs_hdd false: no drive path to classify; the tier-1 unit holds the database dump and the volumes +2.4 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/install.txt — the app migrated, seeded and stored the point on first start +2.5 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/restore.txt — the update's per-app backup, remove keeping backups, restore: the point read back AND the household's own password signs in (303); the box's regenerated admin value is unused +2.6 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/restore.txt ; felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/remove.txt — both remove choices delete every volume; no drive data exists +2.7 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt — 124 MB database + 33 MB public on install (the country shapes); a year of points adds tens of MB +2.8 | n/a | the household uploads location files (imports), not files it opens again; the points read back (2.5) +3.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt — class 3, measured +3.2 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt — through traefik with the browser's https Origin: default refused (422), generated signs in (303), wrong refused +3.3 | n/a | no open first-run screen: the seeded account exists from the first start +3.4 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt — registration off for self-hosted: GET /users/sign_up 302, POST /users 422, still one user +3.5 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/throttle-and-window.txt — the install hold kept strangers out until after_install (16:09:24); the first public answer 2 s later +3.6 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/throttle-and-window.txt — per-minute throttles (5 per email, 20 per address — one address behind the tunnel, R-753) hold everyone out ~1 min; 10 wrong tries LOCK the public name for 1 h (Devise). Kept, as mealie (`09` decision 57); the first step tells the household to set its own e-mail; the phone apps' API key works while locked +3.7 | done | app-catalog-felhom.eu/templates/dawarich/.felhom.yml — `add_people`: the admin creates family accounts (Settings, Users); password and e-mail change in the account settings +3.8 | done | app-catalog-felhom.eu/templates/dawarich/.felhom.yml — `bin/rails runner … ARGV[0]`: the password is its own argument +3.9 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt ; felhom.eu/documentation/audits/new-apps-2026-10-01/shots/dawarich/1.png — browser sign-in over https through traefik (headless Chrome signed in) and the phone apps' API route +4.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/S/S1-dawarich-reads.txt — wget/curl/pgrep in the app image; pg_isready, redis-cli for the sidecars; 127.0.0.1 +4.2 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/G/gates-dawarich.txt — probe-matches-compose exit 0 +4.3 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/install.txt — all four healthy 153 s after the press (pulls + migrations + seed), 0 restarts; start_period 120 s for the app +4.4 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt — the app container stopped: `degraded` within 10 s, front door 404; back 25 s after start +4.5 | done | app-catalog-felhom.eu/templates/dawarich/docker-compose.yml — `container_name: dawarich`; sidecars `dawarich-sidekiq`, `dawarich-db`, `dawarich-redis` +5.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/bench/dawarich/mem-dawarich.csv — bench, swap 0, from birth: app 460 MiB (45 %), sidekiq 270 (26 %), db 148 (29 %), redis 12; 0 kills +5.2 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/bench/dawarich/evidence/MV-dawarich/verdict.json ; felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/remove.txt — 10-min watch: app 33 %, sidekiq 23 %; on the box with a 60-point track import the app peaked 49 %; 0 kills +5.3 | done | app-catalog-felhom.eu/templates/dawarich/.felhom.yml — mem_limit 2688M = 1024 + 1024 + 512 + 128 +5.4 | n/a | Dawarich is Ruby (puma + sidekiq), no self-sizing heap +5.5 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/S/S1-dawarich-reads.txt — pi_compatible false (postgis/postgis has no arm image); ~2 GB of images +6.1 | done | app-catalog-felhom.eu/scripts/upgrade_fixtures_box.py — `Dawarich`: a point through the phone apps' route, three negative controls +6.2 | done | app-catalog-felhom.eu/templates/dawarich/.felhom.yml — the first ladder step 1.15.2 -> 1.15.3 (app and sidekiq together), written by `upgrade-test.py --write-ladder` +6.3 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/undo/box/radicale/step.txt — the box's own undo, proven on a real failed step this session (Radicale); Dawarich's step ran the same guarded Update +6.4 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/bench/dawarich/evidence/MV-dawarich/verdict.json — no mark +6.5 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/FIT.md — plain `x.y.z`, very frequent (56 in 2026) +7.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/bench/dawarich-mail-off-boot.txt — `smtp_mapping` (STARTTLS, the shim's certificate accepted, no auth); a fresh install with mail OFF boots and its reset page answers. Mail ON not provable on 9202 (R-774) +8.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/G/gates-dawarich.txt — copy-i18n exit 0 (frozen with --add-app; the first run named a missing English twin, added) +8.2 | done | app-catalog-felhom.eu/templates/dawarich/.felhom.yml — tagline, use_cases, first_steps (incl. the e-mail change and the tile sentence), default_creds, add_people; followed on 9202 +8.3 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/S/S3-dawarich-assets.txt — logo + three screenshots answer 200; boxes get them with the next hub release (R-766) +8.4 | done | app-catalog-felhom.eu/README.md — both tables; FIRST-ADMIN row; category travel; catalog_since +8.5 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/S/S3-dawarich-assets.txt — the website's count holds +9.1 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/G/gates-dawarich.txt — every gate exit 0, the runtime volume gate included +9.2 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/install.txt ; felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/checks.txt — a fresh install from the drill catalog, as household and stranger +9.3 | done | felhom.eu/documentation/audits/new-apps-2026-10-01/README.md — every row done or n/a; open questions are rows (R-753 tunnel address, R-774 mail ON) +9.4 | done | app-catalog-felhom.eu/onboarding/dawarich.md — published in one commit with this record (the onboarding gate) diff --git a/scripts/copy_freeze/hu.json b/scripts/copy_freeze/hu.json index 3bf2296..3b88296 100644 --- a/scripts/copy_freeze/hu.json +++ b/scripts/copy_freeze/hu.json @@ -224,6 +224,29 @@ "description": "Minecraft szerver kezelő webes felülettel", "initial_credentials.note": "Kezdeti admin jelszó (a telepítéskor beállított). Az első belépés után változtasd meg a Crafty felületén — ez a kártya továbbra is a kezdeti jelszót mutatja." }, + "dawarich": { + "app_info.add_people": "A családtagod fiókját te hozod létre a Beállítások, Felhasználók oldalon; mindenki a saját telefonjáról küldi a helyét.", + "app_info.default_creds": "demo@dawarich.app / safepassword", + "app_info.first_steps[0]": "Nyisd meg a timeline.DOMAIN címet, és jelentkezz be: demo@dawarich.app és a Beállítások oldalon látható jelszó", + "app_info.first_steps[1]": "A fiókod beállításaiban cseréld le az e-mail-címet a sajátodra - a demo@dawarich.app név nyilvános", + "app_info.first_steps[2]": "A beállításokban másold ki az API-kulcsodat", + "app_info.first_steps[3]": "Telepítsd a Dawarich alkalmazást a telefonodra, és add meg: https://timeline.DOMAIN és az API-kulcsot", + "app_info.first_steps[4]": "A térkép csempéit a böngésződ a Dawarich térképszerveréről tölti le; a helyadataid a te szervereden maradnak", + "app_info.tagline": "Saját helyelőzmények - a telefonod a te szerveredre küldi, hol jártál", + "app_info.use_cases[0]": "A Google Idővonal helyett: a helyelőzményeid a saját szervereden", + "app_info.use_cases[1]": "Térkép, statisztika és utazások az összes helyről, ahol jártál", + "app_info.use_cases[2]": "Google Takeout, GPX és GeoJSON import a régi adataidhoz", + "app_info.use_cases[3]": "A Dawarich, az Overland vagy az OwnTracks alkalmazás küldi a telefonod helyét", + "deploy_fields[ADMIN_PASSWORD].description": "Az első bejelentkezéshez: demo@dawarich.app és ez a jelszó. A Beállításokban cseréld le az e-mail-címet a sajátodra.", + "deploy_fields[ADMIN_PASSWORD].label": "Bejelentkezési jelszó (demo@dawarich.app)", + "deploy_fields[DB_PASSWORD].label": "Adatbázis jelszó", + "deploy_fields[DOMAIN].description": "A szerver domain neve", + "deploy_fields[DOMAIN].label": "Domain", + "deploy_fields[SECRET_KEY_BASE].label": "Titkosítási kulcs", + "deploy_fields[SUBDOMAIN].description": "Az alkalmazás aldomainje", + "deploy_fields[SUBDOMAIN].label": "Aldomain", + "description": "Saját helyelőzmények térképen - a Google Idővonal helyett" + }, "docmost": { "app_info.first_steps[0]": "Nyisd meg a docs.DOMAIN címet a böngészőben", "app_info.first_steps[1]": "Az első regisztrált felhasználó automatikusan admin lesz", @@ -1205,6 +1228,7 @@ } }, "reasons": { + "dawarich": "new app 2026-10-01 through NEW-APP-CHECKLIST.md: te-form, no kérjük; reviewed by CC against the operator rules", "karakeep": "new app 2026-10-01 through NEW-APP-CHECKLIST.md: te-form, no kérjük; reviewed by CC against the operator rules", "radicale": "new app 2026-10-01 through NEW-APP-CHECKLIST.md: te-form, no kérjük; reviewed by CC against the operator rules" } diff --git a/scripts/upgrade_fixtures_box.py b/scripts/upgrade_fixtures_box.py index df98ccb..9ed4bf0 100644 --- a/scripts/upgrade_fixtures_box.py +++ b/scripts/upgrade_fixtures_box.py @@ -2003,6 +2003,69 @@ class Karakeep: return ok +# ============================================================================================= +class Dawarich: + """Dawarich (2026-10-01, new app through NEW-APP-CHECKLIST.md). THE FRONT DOOR is the route the phone apps use: + `POST /api/v1/overland/batches?api_key=` (Overland's GeoJSON — one point with a unique place and time), read back + with `GET /api/v1/points?api_key=…&start_at=…&end_at=…`. The API key is the account's own (the household copies it + from its settings page); the fixture reads it with the app's own CLI (`bin/rails runner` in the app's container — + R-156's allowed route, nothing planted). Negative controls on every readback: no key 401, a wrong key 401, a range + with nothing in it answers [].""" + sub = "timeline" + + @staticmethod + def _key(w): + out = w.guest("docker exec dawarich bin/rails runner 'puts User.order(:id).first.api_key' 2>/dev/null | tail -1") + k = (out or "").strip() + return k if re.fullmatch(r"[A-Za-z0-9_-]{20,}", k) else None + + def seed(self, w, sub, say): + if not w.wait_app(sub, "/api/v1/health", want=("200",), tries=90): + self.tried = "/api/v1/health never answered 200" + return None + key = self._key(w) + if not key: + self.tried = "no API key from the app's own CLI" + return None + lat = round(47.40 + secrets.randbelow(2000) / 10000.0, 4) + lon = round(19.00 + secrets.randbelow(2000) / 10000.0, 4) + ts = "2026-09-%02dT%02d:%02d:00Z" % (1 + secrets.randbelow(28), secrets.randbelow(24), secrets.randbelow(60)) + body = {"locations": [{"type": "Feature", "geometry": {"type": "Point", "coordinates": [lon, lat]}, + "properties": {"timestamp": ts, "altitude": 110, "speed": 0, "horizontal_accuracy": 5, + "device_id": "felhom-fixture"}}]} + rc, code, out = w.app_curl(sub, f"/api/v1/overland/batches?api_key={key}", "-H", "Content-Type: application/json", + data=json.dumps(body), method="POST") + say(f" dawarich: POST /api/v1/overland/batches http={code} ({lat},{lon} at {ts})") + if code not in ("200", "201"): + self.tried = f"overland batch -> {code} {(out or '')[:120]}" + return None + return {"lat": lat, "lon": lon, "ts": ts} + + def verify(self, w, sub, t, say): + if not w.wait_app(sub, "/api/v1/health", want=("200",), tries=90): + say(" dawarich: /api/v1/health never answered") + return False + key = self._key(w) + day = t["ts"][:10] + rng = f"start_at={day}T00:00:00Z&end_at={day}T23:59:59Z" + rc, c_none, _ = w.app_curl(sub, f"/api/v1/points?{rng}") + rc, c_wrong, _ = w.app_curl(sub, f"/api/v1/points?api_key=felhom-wrong-key&{rng}") + rc, c_empty, o_empty = w.app_curl(sub, f"/api/v1/points?api_key={key}&start_at=2001-01-01T00:00:00Z&end_at=2001-01-02T00:00:00Z") + if c_none != "401" or c_wrong != "401" or (o_empty or "").strip() != "[]": + say(f" dawarich: READBACK UNUSABLE — no key {c_none}, wrong key {c_wrong}, empty range {(o_empty or '')[:40]!r}") + return False + found = False + for _ in range(12): # the batch is stored by a background job + rc, code, out = w.app_curl(sub, f"/api/v1/points?api_key={key}&{rng}") + # the point's fields are read as text: the unique coordinates must both appear in the answer + found = code == "200" and str(t["lat"]) in (out or "") and str(t["lon"]) in (out or "") + if found: + break + time.sleep(5) + say(f" dawarich: readback http={code} found={found} (controls: no key {c_none}, wrong {c_wrong}, empty range [])") + return found + + FIXTURES = { "uptime-kuma": UptimeKuma(), "crafty-controller": Crafty(), @@ -2041,4 +2104,5 @@ FIXTURES = { "calcom": Calcom(), "radicale": Radicale(), "karakeep": Karakeep(), + "dawarich": Dawarich(), } diff --git a/templates/dawarich/.felhom.yml b/templates/dawarich/.felhom.yml new file mode 100644 index 0000000..e492aa6 --- /dev/null +++ b/templates/dawarich/.felhom.yml @@ -0,0 +1,144 @@ +# ============================================================================= +# .felhom.yml - App metadata for felhom-controller +# ============================================================================= +# Dawarich — freikin/dawarich. Onboarding record: onboarding/dawarich.md (NEW-APP-CHECKLIST.md). +# First admin: CLASS 3 — the image's `rails db:seed` creates `demo@dawarich.app` / `safepassword`. `after_install` sets +# the box's generated password through Rails (the password is ARGV); until it succeeds the box holds the app behind the +# gate (R-741). The login NAME stays public: Devise locks it for 1 hour after 10 wrong tries (measured, onboarding 3.6) — +# the mealie precedent (`09` decision 57: a 1-hour lock kept, no secret name); the first step tells the household to set +# its own e-mail address, which ends that. Phone apps post with the account's API key, which no lock touches. + +# --- Display info (shown on dashboard) --- +display_name: "Dawarich" +description: "Saját helyelőzmények térképen - a Google Idővonal helyett" +category: "travel" +subdomain: "timeline" +slug: "dawarich" +# catalog_since: the date THIS repo last changed this app's pinned images. Any commit that +# changes an image: line must set this to the same day (see CLAUDE.md). +catalog_since: "2026-10-01" + +# --- Resource hints (displayed on deploy screen) --- +resources: + mem_request: "500M" + mem_limit: "2688M" # dawarich 1024M + dawarich-sidekiq 1024M + dawarich-db 512M + dawarich-redis 128M = 2688M + pi_compatible: false + needs_hdd: false + +# --- Deploy wizard fields --- +deploy_fields: + - env_var: DOMAIN + label: "Domain" + type: domain + description: "A szerver domain neve" + locked_after_deploy: true + + - env_var: SUBDOMAIN + label: "Aldomain" + type: subdomain + default: "timeline" + required: true + locked_after_deploy: true + description: "Az alkalmazás aldomainje" + + - env_var: SECRET_KEY_BASE + label: "Titkosítási kulcs" + type: secret + generate: "hex:64" + locked_after_deploy: true + # Data-encrypting key: Rails derives the keys of Dawarich's encrypted columns from it (`encrypts` in + # instance_setting.rb, service_setting.rb, trip_source.rb — read 2026-10-01). Regenerated, those values are + # unreadable, so a restore must recover it and refuses rather than make a new one. + data_key: true + + - env_var: DB_PASSWORD + label: "Adatbázis jelszó" + type: secret + generate: "password:24" + locked_after_deploy: true + + - env_var: ADMIN_PASSWORD + label: "Bejelentkezési jelszó (demo@dawarich.app)" + type: password + generate: "password:24" + locked_after_deploy: true + description: "Az első bejelentkezéshez: demo@dawarich.app és ez a jelszó. A Beállításokban cseréld le az e-mail-címet a sajátodra." + +after_install: + service: dawarich + env: [ADMIN_PASSWORD] + command: ["bin/rails", "runner", "u = User.find_by!(email: 'demo@dawarich.app'); u.update!(password: ARGV[0], password_confirmation: ARGV[0]); puts 'FELHOM_AFTER_INSTALL_OK'", "${ADMIN_PASSWORD}"] + success: "FELHOM_AFTER_INSTALL_OK" + +# --- App-email (password reset) through the box's mail relay --- +smtp_mapping: + host_var: SMTP_SERVER + port_var: SMTP_PORT + from_var: SMTP_FROM + from_local: dawarich + +# --- Customer-facing info --- +app_info: + tagline: "Saját helyelőzmények - a telefonod a te szerveredre küldi, hol jártál" + default_creds: "demo@dawarich.app / safepassword" + add_people: "A családtagod fiókját te hozod létre a Beállítások, Felhasználók oldalon; mindenki a saját telefonjáról küldi a helyét." + docs_url: "https://dawarich.app/docs/intro" + use_cases: + - 'A Google Idővonal helyett: a helyelőzményeid a saját szervereden' + - 'Térkép, statisztika és utazások az összes helyről, ahol jártál' + - 'Google Takeout, GPX és GeoJSON import a régi adataidhoz' + - 'A Dawarich, az Overland vagy az OwnTracks alkalmazás küldi a telefonod helyét' + first_steps: + - 'Nyisd meg a timeline.DOMAIN címet, és jelentkezz be: demo@dawarich.app és a Beállítások oldalon látható jelszó' + - 'A fiókod beállításaiban cseréld le az e-mail-címet a sajátodra - a demo@dawarich.app név nyilvános' + - 'A beállításokban másold ki az API-kulcsodat' + - 'Telepítsd a Dawarich alkalmazást a telefonodra, és add meg: https://timeline.DOMAIN és az API-kulcsot' + - 'A térkép csempéit a böngésződ a Dawarich térképszerveréről tölti le; a helyadataid a te szervereden maradnak' + +# --- Controller health probe (dials what the compose healthcheck dials) --- +healthcheck: + checks: + - type: api + port: 3000 + path: "/api/v1/health" + expect: + status: 200 + +i18n: + en: + description: 'Your own location history on a map - instead of Google Timeline' + app_info: + tagline: 'Your own location history - your phone sends where you have been to your own server' + default_creds: 'demo@dawarich.app / safepassword' + add_people: "You create your family member's account in Settings, Users; everyone sends their location from their own phone." + use_cases: + - 'Instead of Google Timeline: your location history on your own server' + - 'A map, statistics and trips from every place you have been' + - 'Google Takeout, GPX and GeoJSON import for your old data' + - 'The Dawarich, Overland or OwnTracks app sends your phone location' + first_steps: + - 'Open timeline.DOMAIN and sign in: demo@dawarich.app and the password shown on the settings page' + - 'In your account settings, replace the e-mail address with your own - the demo@dawarich.app name is public' + - 'Copy your API key from the settings' + - 'Install the Dawarich app on your phone and enter: https://timeline.DOMAIN and the API key' + - 'Your browser loads the map tiles from the Dawarich map server; your location data stays on your server' + deploy_fields: + - env_var: DOMAIN + label: 'Domain' + description: 'The server domain name' + - env_var: SUBDOMAIN + label: 'Subdomain' + description: 'The subdomain this app answers on' + - env_var: SECRET_KEY_BASE + label: 'Encryption key' + - env_var: DB_PASSWORD + label: 'Database password' + - env_var: ADMIN_PASSWORD + label: 'Sign-in password (demo@dawarich.app)' + description: 'For the first sign-in: demo@dawarich.app and this password. Replace the e-mail address with your own in the settings.' + +# update_ladder — the test record: one tested step per line, oldest first (JSON flow mappings, +# `09-update-architecture.md` §6.4 part 4). WRITTEN BY scripts/upgrade-test.py, never by hand; +# gated by scripts/check-test-record.py. An image: move without a proven entry here is refused. +update_ladder: + - {"from": {"dawarich": "freikin/dawarich:1.15.2", "dawarich-sidekiq": "freikin/dawarich:1.15.2", "dawarich-db": "postgis/postgis:17-3.5-alpine", "dawarich-redis": "redis:7.4-alpine"}, "to": {"dawarich": "freikin/dawarich:1.15.3", "dawarich-sidekiq": "freikin/dawarich:1.15.3", "dawarich-db": "postgis/postgis:17-3.5-alpine", "dawarich-redis": "redis:7.4-alpine"}, "digest": {"dawarich": "sha256:589e3606b11b61c3fa2d9661832cb73645a9b93a9cc7d807a071e2bc6c584498", "dawarich-db": "sha256:894f570c0cf0664ed5576a8fd5d5bfb8fb1b19d592885b686c3a88c8bd90c41f", "dawarich-redis": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "dawarich-sidekiq": "sha256:589e3606b11b61c3fa2d9661832cb73645a9b93a9cc7d807a071e2bc6c584498"}, "verdict": "proven", "tested_at": "2026-10-01T16:20:55Z", "harness_version": 4, "evidence": "felhom.eu/documentation/audits/new-apps-2026-10-01/bench/dawarich/evidence/MV-dawarich/verdict.json", "box_evidence": "felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/step.txt", "memory_peak_pct": 33.0, "marks": {"files_may_change": false, "needs_person": null, "memory_tight": false}, "memory_basis": "anon", "memory_cgroup_peak_pct": 70.8} diff --git a/templates/dawarich/docker-compose.yml b/templates/dawarich/docker-compose.yml new file mode 100644 index 0000000..537b729 --- /dev/null +++ b/templates/dawarich/docker-compose.yml @@ -0,0 +1,192 @@ +# Dawarich - Saját helyelőzmények (a Google Idővonal helyett) +# Domain: ${SUBDOMAIN}.${DOMAIN} +# Database: PostgreSQL 17 + PostGIS 3.5 (the major upstream's own compose runs — `09` decision 42's rule) + Redis + Sidekiq +# RAM: ~900M (mem_limit: 2688M total — app 1024M + sidekiq 1024M + db 512M + redis 128M) | Pi-compatible: No (postgis/postgis is amd64-only) +# +# Environment variables: +# DOMAIN - Your domain (e.g., demo-felhom.eu) +# SECRET_KEY_BASE - Rails aláíró kulcs (generált) +# DB_PASSWORD - Adatbázis jelszó (generált; csak a belső hálón) +# ADMIN_PASSWORD - Az első bejelentkezés jelszava (generált; after_install állítja be) +# +# Mirrors upstream's docker/docker-compose.yml (app + sidekiq on the same image, postgis 17-3.5, redis 7.4), pinned. +# THE KNOWN LOGIN: the app's entrypoint runs `rails db:seed`, which creates `demo@dawarich.app` / `safepassword` when no +# user exists (db/seeds.rb). `after_install` replaces that password with the box's generated one, through Rails itself +# (the password is ARGV, never pasted into code); until it succeeds the box holds the app behind the gate (R-741). +# Reverse geocoding stays OFF (no PHOTON_API_HOST / NOMINATIM_API_HOST / GEOAPIFY / LOCATIONIQ set): no coordinate +# leaves the box. The map's tiles are fetched by the household's BROWSER from Dawarich's default tile server (FIT.md). + +services: + dawarich: + image: freikin/dawarich:1.15.3 + container_name: dawarich + restart: unless-stopped + entrypoint: web-entrypoint.sh + command: ["bin/rails", "server", "-p", "3000", "-b", "::"] + environment: + TZ: Europe/Budapest + TIME_ZONE: Europe/Budapest + RAILS_ENV: production + REDIS_URL: redis://dawarich-redis:6379 + DATABASE_HOST: dawarich-db + DATABASE_PORT: "5432" + DATABASE_USERNAME: dawarich + DATABASE_PASSWORD: ${DB_PASSWORD} + DATABASE_NAME: dawarich + APPLICATION_HOSTS: ${SUBDOMAIN}.${DOMAIN},localhost,127.0.0.1,::1 + APPLICATION_PROTOCOL: http + SECRET_KEY_BASE: ${SECRET_KEY_BASE} + RAILS_LOG_TO_STDOUT: "true" + SELF_HOSTED: "true" + STORE_GEODATA: "true" + WEB_CONCURRENCY: "1" + PROMETHEUS_EXPORTER_ENABLED: "false" + BACKGROUND_PROCESSING_CONCURRENCY: "3" + # App-email (smtp_mapping; STARTTLS to the shim on :2525, its self-signed certificate accepted by + # SMTP_OPENSSL_VERIFY_MODE=none; the shim takes no login, so SMTP_AUTHENTICATION=none). Empty SMTP_SERVER = mail OFF; + # measured 2026-10-01 that a fresh install with mail OFF boots (checklist 7.1). DOMAIN builds the links in its mails. + SMTP_SERVER: ${SMTP_SERVER:-} + SMTP_PORT: ${SMTP_PORT:-587} + SMTP_FROM: ${SMTP_FROM:-} + SMTP_AUTHENTICATION: "none" + SMTP_OPENSSL_VERIFY_MODE: "none" + DOMAIN: ${SUBDOMAIN}.${DOMAIN} + volumes: + - dawarich_public:/var/app/public + - dawarich_watched:/var/app/tmp/imports/watched + - dawarich_storage:/var/app/storage + networks: + - traefik-public + - dawarich-internal + depends_on: + dawarich-db: + condition: service_healthy + dawarich-redis: + condition: service_healthy + deploy: + resources: + limits: + memory: 1024M + healthcheck: + test: ["CMD-SHELL", "wget -qO - http://127.0.0.1:3000/api/v1/health | grep -q '\"status\"'"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 120s + labels: + - "traefik.enable=true" + - "traefik.http.routers.dawarich.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)" + - "traefik.http.routers.dawarich.entrypoints=websecure" + - "traefik.http.routers.dawarich.tls=true" + - "traefik.http.routers.dawarich.tls.certresolver=letsencrypt" + - "traefik.http.services.dawarich.loadbalancer.server.port=3000" + + dawarich-sidekiq: + image: freikin/dawarich:1.15.3 + container_name: dawarich-sidekiq + restart: unless-stopped + entrypoint: sidekiq-entrypoint.sh + command: ["sidekiq"] + environment: + TZ: Europe/Budapest + TIME_ZONE: Europe/Budapest + RAILS_ENV: production + REDIS_URL: redis://dawarich-redis:6379 + DATABASE_HOST: dawarich-db + DATABASE_PORT: "5432" + DATABASE_USERNAME: dawarich + DATABASE_PASSWORD: ${DB_PASSWORD} + DATABASE_NAME: dawarich + APPLICATION_HOSTS: ${SUBDOMAIN}.${DOMAIN},localhost,127.0.0.1,::1 + APPLICATION_PROTOCOL: http + SECRET_KEY_BASE: ${SECRET_KEY_BASE} + RAILS_LOG_TO_STDOUT: "true" + SELF_HOSTED: "true" + STORE_GEODATA: "true" + WEB_CONCURRENCY: "1" + PROMETHEUS_EXPORTER_ENABLED: "false" + BACKGROUND_PROCESSING_CONCURRENCY: "3" + # App-email (smtp_mapping; STARTTLS to the shim on :2525, its self-signed certificate accepted by + # SMTP_OPENSSL_VERIFY_MODE=none; the shim takes no login, so SMTP_AUTHENTICATION=none). Empty SMTP_SERVER = mail OFF; + # measured 2026-10-01 that a fresh install with mail OFF boots (checklist 7.1). DOMAIN builds the links in its mails. + SMTP_SERVER: ${SMTP_SERVER:-} + SMTP_PORT: ${SMTP_PORT:-587} + SMTP_FROM: ${SMTP_FROM:-} + SMTP_AUTHENTICATION: "none" + SMTP_OPENSSL_VERIFY_MODE: "none" + DOMAIN: ${SUBDOMAIN}.${DOMAIN} + volumes: + - dawarich_public:/var/app/public + - dawarich_watched:/var/app/tmp/imports/watched + - dawarich_storage:/var/app/storage + networks: + - dawarich-internal + depends_on: + dawarich: + condition: service_healthy + deploy: + resources: + limits: + memory: 1024M + healthcheck: + test: ["CMD-SHELL", "pgrep -f sidekiq"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 60s + + dawarich-db: + image: postgis/postgis:17-3.5-alpine + container_name: dawarich-db + restart: unless-stopped + shm_size: 1g + environment: + TZ: Europe/Budapest + POSTGRES_USER: dawarich + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_DB: dawarich + volumes: + - dawarich_db_data:/var/lib/postgresql/data + networks: + - dawarich-internal + deploy: + resources: + limits: + memory: 512M + healthcheck: + test: ["CMD-SHELL", "pg_isready -U dawarich -d dawarich"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + + dawarich-redis: + image: redis:7.4-alpine + container_name: dawarich-redis + restart: unless-stopped + command: ["redis-server", "--save", "900", "1", "--save", "300", "10", "--appendonly", "no"] + volumes: + - dawarich_redis:/data + networks: + - dawarich-internal + deploy: + resources: + limits: + memory: 128M + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + dawarich_db_data: + dawarich_redis: + dawarich_public: + dawarich_watched: + dawarich_storage: + +networks: + traefik-public: + external: true + dawarich-internal: