docs+openspec: prior-art research (Organic Maps, Endurain, wanderer) and 15 proposals
Add docs/inspirations.md as the durable record of the 2026-07-05/06 prior-art research — per-project learnings with source paths, canonical credit lines, and the changes each spawned — and extend the acknowledgment lists in philosophy.md/architecture.md (Organic Maps, Endurain, wanderer). New OpenSpec changes (proposal/design/specs/tasks each): - Organic Maps: elevation-profile-hardening, gpx-parser-robustness, hiking-time-estimate, poi-index, hiking-foot-profile - Endurain: account-export, activity-duplicate-review, fit-parsing-hardening, activity-locations, self-hosting-guide, activity-privacy-controls - wanderer: federation-hardening, link-share-tokens - credits-page (user-visible acknowledgments) Updated in-flight changes with wanderer prior-art sections: route-federation, route-discovery. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
be4f7e4ae8
commit
5dd4968626
80 changed files with 2600 additions and 2 deletions
2
openspec/changes/self-hosting-guide/.openspec.yaml
Normal file
2
openspec/changes/self-hosting-guide/.openspec.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-07-06
|
||||
57
openspec/changes/self-hosting-guide/design.md
Normal file
57
openspec/changes/self-hosting-guide/design.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
## Context
|
||||
|
||||
The repo's deployment story is flagship-shaped: `infrastructure/docker-compose.yml` assumes SOPS-encrypted env files, Caddy with production domains, the monitoring stack, and the two-host BRouter topology. `docs/deployment.md` and `docs/server-hardening.md` document *that* deployment. The env-variable surface of the two apps is discoverable only by reading `apps/*/.env.example` (journal) and code. Endurain's operator experience (three compose variants, exhaustive env table in `docs/getting-started/advanced-started.md`, Caddy/NPM recipes, secrets variant) is the reference.
|
||||
|
||||
The `poi-index` change (proposed) adds an optional data pipeline with an explicit self-hoster story ("works with empty POI table; regional extract configurable") — this guide is where that story gets told.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- A competent operator goes from `git clone` to a working instance (journal + planner + routing) in under an hour with only the guide.
|
||||
- The env contract is documented and CI-guaranteed not to rot.
|
||||
- Flagship deployment remains untouched and separate.
|
||||
|
||||
**Non-Goals:**
|
||||
- Kubernetes, Ansible, managed cloud recipes; automated backups; federation moderation/ops guides; one-click installers (Proxmox scripts etc. — community territory).
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. A separate example compose, not a parametrized flagship compose
|
||||
|
||||
`infrastructure/examples/docker-compose.selfhost.yml` is written for operators: 4 services (journal, planner, postgres+postgis, brouter), one host, plain `.env`, named volumes, healthchecks, pinned image tags, no monitoring. Rationale: making the flagship compose serve both audiences couples every production change to operator UX; a small example file with a CI boot test is cheaper to keep honest than a matrix of overrides.
|
||||
|
||||
### 2. The env table is the contract
|
||||
|
||||
`docs/self-hosting.md` gets a single table: variable, required/optional, default, description — sourced by auditing `apps/journal/.env.example`, `apps/planner` env reads, and `packages/db`/`jobs` config. Writing it doubles as an audit: any variable whose default assumes flagship values (domains, vSwitch IPs) gets a sane default or a documented requirement. BRouter segment coverage (`download-segments.sh` region choice) is documented as the operator's first decision.
|
||||
|
||||
### 3. Secrets: plain `.env` primary, Docker secrets documented, no defaults ever
|
||||
|
||||
Small operators get `.env` (with a "chmod 600, not in git" note); the guide shows the Docker file-based-secrets variant for the two values that matter most (DB password, app secret keys) without shipping a third compose file. SOPS stays flagship-only.
|
||||
|
||||
Two rules adopted from the wanderer review (`docs/inspirations.md` — they ship real default keys in their committed compose, and we won't): the example compose and `.env.example` contain **placeholders + generation commands only** (`openssl rand -hex 32`), never working values; and the journal gains a **boot-time hard failure** when a required secret is missing or still a placeholder — refusing to start with a log line linking to the guide beats running silently insecure. (The boot check is a small app change carried by this change.)
|
||||
|
||||
### 3b. Backup guidance distinguishes real state from rebuildable state
|
||||
|
||||
The backup section names exactly what must be backed up (PostgreSQL dump + the media store) and what is deliberately excluded because it's rebuildable (BRouter segments — re-downloaded; the POI index — re-imported per `poi-index`; Prometheus/Loki data — observability history, operator's choice). wanderer documents the same rebuildable-cache principle for its search index. The restore procedure states the version rule explicitly: restore a dump into the **same app version that produced it**, then upgrade — schema migrations run forward on boot, not backward (wanderer's restore-only-within-minor-version caveat, made into guidance instead of a surprise).
|
||||
|
||||
### 4. CI smoke test keeps the example honest
|
||||
|
||||
A workflow job (PR-triggered on `infrastructure/examples/**`, `docs/self-hosting.md`, app Dockerfiles; plus weekly cron) boots the example compose with generated env, waits for health, asserts journal health endpoint + planner page + a BRouter route with the CI segment. Reuses the existing journal-image smoke-test pattern (#556).
|
||||
|
||||
### 5. BYO provider credentials
|
||||
|
||||
Connected services (Wahoo now; Garmin/COROS later) require per-instance developer apps. The guide documents: where to register, which redirect URI to configure (`https://<domain>/api/sync/connect/<provider>`), which env vars hold the credentials, and that HTTPS is required by providers. This also becomes the canonical answer for staging/preview questions.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Example drifts from real app requirements] → the CI boot test is the mitigation; env-table drift is caught by the same audit test where feasible (fail if `.env.example` variables are undocumented).
|
||||
- [Two compose files to maintain] → deliberate; the example is small and CI-covered.
|
||||
- [Operators run without monitoring/backups] → backup section is prominent and prescriptive (nightly `pg_dump` + media volume copy); monitoring is explicitly optional with a pointer to the flagship stack as reference.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Docs + example + CI only; no production impact. Rollback = revert.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- None blocking. Whether to publish prebuilt images to a public registry (operators currently build from source or reuse the CI images) is a distribution question to settle during implementation — the guide documents whichever is true at merge time.
|
||||
28
openspec/changes/self-hosting-guide/proposal.md
Normal file
28
openspec/changes/self-hosting-guide/proposal.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
## Why
|
||||
|
||||
trails.cool's identity is "federated, self-hostable" — but the repo only documents the flagship deployment (SOPS-encrypted secrets, two-host topology, full monitoring stack). A third party wanting to run an instance has no entry point: no minimal compose file, no environment-variable reference, no backup or upgrade guidance. Endurain (reviewed 2026-07-06) shows the bar for a self-hosted fitness app: three compose variants, a ~45-variable documented env table, reverse-proxy recipes, and Docker-secrets support. Federation is only real if other people can actually stand up instances.
|
||||
|
||||
## What Changes
|
||||
|
||||
- New **`docs/self-hosting.md`**: prerequisites, quick start, the environment-variable reference table (every variable with default / required / description), reverse-proxy guidance (Caddy example), upgrade procedure (image tags + drizzle migrations), backup & restore (Postgres dump + media store + what *not* to back up), and scaling notes.
|
||||
- New **minimal operator compose** at `infrastructure/examples/docker-compose.selfhost.yml`: journal + planner + PostgreSQL/PostGIS + BRouter, plain `.env` file (no SOPS), single host, no monitoring stack — with an accompanying `.env.example` documenting the minimal variable set.
|
||||
- A **secrets variant** documented inline (Docker file-based secrets for DB password and app keys), following Endurain's pattern.
|
||||
- **BYO provider credentials** documented: how a self-hosted instance registers its own Wahoo (and later Garmin/COROS) developer app and configures redirect URIs — the connected-services features are per-instance, not flagship-only.
|
||||
- CI smoke check: the example compose must boot and pass health checks so it can't rot (extends the existing journal image smoke-test approach).
|
||||
- Not in scope: Kubernetes/Helm, managed-DB recipes, automated backup tooling, and multi-instance federation operations guides (allowlists/moderation — future).
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `self-hosting`: The supported third-party deployment surface — minimal compose, documented env contract, upgrade/backup procedures, and the CI guarantee that the example deployment boots.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
<!-- none — infrastructure spec describes the flagship; the operator example is a parallel, additive surface -->
|
||||
|
||||
## Impact
|
||||
|
||||
- `docs/self-hosting.md` (new), linked from README and `docs/architecture.md`.
|
||||
- `infrastructure/examples/docker-compose.selfhost.yml` + `.env.example` (new).
|
||||
- `.github/workflows`: compose smoke job (boot, wait healthy, hit health endpoints, planner loads).
|
||||
- Possible small app fixes surfaced by the exercise (env defaults that assume flagship values) — folded in as found.
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Minimal operator deployment example
|
||||
The repository SHALL provide a self-contained example Docker Compose deployment (journal, planner, PostgreSQL/PostGIS, BRouter) configured via a plain env file, with healthchecks and pinned images, independent of the flagship infrastructure (no SOPS, no monitoring stack, single host).
|
||||
|
||||
#### Scenario: Fresh instance boots
|
||||
- **WHEN** an operator copies the example compose and `.env.example`, fills the required variables, and runs `docker compose up`
|
||||
- **THEN** all services become healthy and the journal and planner are reachable
|
||||
|
||||
### Requirement: Documented environment contract
|
||||
`docs/self-hosting.md` SHALL document every environment variable the apps read — required/optional, default, and description — plus reverse-proxy guidance, the BRouter segment-region decision, and BYO provider-credential setup (Wahoo and future providers) including redirect URIs.
|
||||
|
||||
#### Scenario: Env vars discoverable without reading code
|
||||
- **WHEN** an operator needs to configure SMTP or provider credentials
|
||||
- **THEN** the variable names, formats, and where to obtain values are in the guide
|
||||
|
||||
#### Scenario: Undocumented variable fails CI
|
||||
- **WHEN** a new variable is added to an app's `.env.example` without a guide entry
|
||||
- **THEN** the documentation check fails
|
||||
|
||||
### Requirement: No default secrets, fail-fast on misconfiguration
|
||||
The example deployment SHALL ship no working secret values (placeholders + generation commands only), and the journal SHALL refuse to start when a required secret is missing or still a placeholder, logging a message that links to the self-hosting guide.
|
||||
|
||||
#### Scenario: Placeholder secret blocks startup
|
||||
- **WHEN** an operator starts the journal with the `.env.example` placeholder secret unchanged
|
||||
- **THEN** the process exits with a clear error naming the variable and linking to the guide
|
||||
|
||||
### Requirement: Upgrade and backup procedures
|
||||
The guide SHALL document upgrading (image bump; schema migrations applied via the documented mechanism) and backup/restore: nightly PostgreSQL dump, media storage copy, and restore steps — including what not to back up (raw DB volume copies while running).
|
||||
|
||||
#### Scenario: Operator restores from backup
|
||||
- **WHEN** an operator follows the restore procedure with a dump + media copy
|
||||
- **THEN** the instance comes back with all user data intact
|
||||
|
||||
### Requirement: CI-verified example deployment
|
||||
CI SHALL boot the example compose (on changes to it, the guide, or app images, and on a weekly schedule), wait for health, and verify the journal health endpoint, a planner page load, and a BRouter route computation.
|
||||
|
||||
#### Scenario: Broken example blocks merge
|
||||
- **WHEN** an app change breaks the example deployment
|
||||
- **THEN** the smoke job fails on the PR
|
||||
27
openspec/changes/self-hosting-guide/tasks.md
Normal file
27
openspec/changes/self-hosting-guide/tasks.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
## 1. Env audit
|
||||
|
||||
- [ ] 1.1 Enumerate every env variable read by journal, planner, db, jobs (grep `process.env` + `.env.example`s); classify required/optional/default; fix any default that assumes flagship values (domains, internal IPs)
|
||||
- [ ] 1.2 Draft the env reference table from the audit
|
||||
|
||||
## 2. Example deployment
|
||||
|
||||
- [ ] 2.1 Write `infrastructure/examples/docker-compose.selfhost.yml` (journal, planner, postgres+postgis, brouter; healthchecks; named volumes; pinned tags) + `infrastructure/examples/.env.example`
|
||||
- [ ] 2.2 Verify from scratch on a clean machine/VM: fill env, `up`, create account, plan a route, save to journal
|
||||
- [ ] 2.3 Document the Docker file-based-secrets variant inline (DB password, app secrets)
|
||||
- [ ] 2.4 Ensure example compose + `.env.example` contain placeholders and generation commands only (`openssl rand -hex 32`), never working secret values
|
||||
- [ ] 2.5 Add a boot-time check to the journal: refuse to start when a required secret is missing or a known placeholder, with a log line linking to the self-hosting guide; unit test both paths
|
||||
|
||||
## 3. Guide
|
||||
|
||||
- [ ] 3.1 Write `docs/self-hosting.md`: prerequisites, quick start, env table, reverse proxy (Caddy example), BRouter segment-region choice, BYO provider credentials (Wahoo redirect URI + HTTPS requirement), upgrade procedure, backup & restore, optional POI index note (per `poi-index` change), scaling/limits
|
||||
- [ ] 3.1b Backup section: enumerate backup targets (pg_dump + media) vs rebuildable-and-excluded state (BRouter segments, POI index, monitoring data); state the restore-version rule (restore into the producing app version, then upgrade)
|
||||
- [ ] 3.2 Link from README and `docs/architecture.md`; note the flagship docs (`docs/deployment.md`) are the reference production deployment, not the operator path
|
||||
|
||||
## 4. CI
|
||||
|
||||
- [ ] 4.1 Add a compose smoke workflow: boot example with generated env, wait healthy, curl journal health + planner page, request a BRouter route with the CI segment; trigger on example/guide/Dockerfile changes + weekly cron
|
||||
- [ ] 4.2 Add the docs-drift check: every `.env.example` variable must appear in the guide's table
|
||||
|
||||
## 5. Verification
|
||||
|
||||
- [ ] 5.1 Run `pnpm typecheck && pnpm lint && pnpm test` (unchanged app code paths) and the new smoke workflow on the PR
|
||||
Loading…
Add table
Add a link
Reference in a new issue