Commit graph

228 commits

Author SHA1 Message Date
Ullrich Schäfer
57094323d2
SOPS+age secrets, split CD workflows, GitHub OAuth for Grafana
Secrets:
- Add .sops.yaml with age encryption config
- Add encrypted secrets.app.env (app secrets) and secrets.infra.env (Grafana OAuth)
- CD decrypts at deploy time with AGE_SECRET_KEY — all other secrets
  move out of GitHub Actions into version-controlled encrypted files

Split CD:
- cd-apps.yml: triggered by apps/packages changes, builds Docker images, deploys apps
- cd-infra.yml: triggered by infrastructure/ changes, copies configs, restarts services
- Remove monolithic cd.yml

Grafana auth:
- GitHub OAuth (trails-cool org), disable login form
- Remove Caddy basic_auth block and all GRAFANA_* env vars/secrets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:28:04 +01:00
Ullrich Schäfer
206f6ac941
Merge pull request #95 from trails-cool/fix-grafana-cd-password
Sync Grafana admin password on deploy
2026-03-27 15:13:37 +01:00
Ullrich Schäfer
b01624b31a
Sync Grafana admin password on each deploy
GF_SECURITY_ADMIN_PASSWORD only works on first boot. Reset the admin
password via grafana-cli after each deploy to stay in sync with secrets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:53:54 +01:00
Ullrich Schäfer
65f30b952a
Merge pull request #94 from trails-cool/fix-caddy-basicauth
Fix deprecated basicauth directive in Caddyfile
2026-03-27 14:35:03 +01:00
Ullrich Schäfer
73edb1bfed
Use basic_auth instead of deprecated basicauth in Caddyfile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:32:57 +01:00
Ullrich Schäfer
a8ec0a9509
Merge pull request #93 from trails-cool/fix-grafana-datasource-uids
Add explicit UIDs to Grafana datasources
2026-03-27 13:10:35 +00:00
Ullrich Schäfer
83243cea31
Add explicit UIDs to Grafana datasources
Alert rules reference datasourceUid: prometheus/loki which requires
explicit UIDs in the provisioning config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:08:21 +01:00
Ullrich Schäfer
5b07e68364
Merge pull request #92 from trails-cool/fix-grafana-alerts
Fix Grafana alert email config
2026-03-27 13:06:07 +00:00
Ullrich Schäfer
c47426213b
Fix Grafana alert provisioning: hardcode email address
Grafana provisioning YAML doesn't support env var substitution.
Replace ${ALERT_EMAIL} with hardcoded admin@trails.cool.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:03:48 +01:00
Ullrich Schäfer
4751176da9
Merge pull request #91 from trails-cool/fix-caddy-env
Fix Caddy env vars and Loki compactor config
2026-03-27 13:55:49 +01:00
Ullrich Schäfer
ed33fcf9ae
Deploy monitoring configs (Prometheus, Loki, Grafana) via CD
Add prometheus.yml, loki-config.yml, and grafana provisioning/dashboards
to the SCP step so they're copied to the server on each deploy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:55:09 +01:00
Ullrich Schäfer
2ecfe81c08
Pass Grafana env vars to Caddy container, fix Loki compactor config
- Add DOMAIN, GRAFANA_USER, GRAFANA_PASSWORD_HASH as environment vars
  on the Caddy container so Caddyfile can reference them
- Fix Loki compactor: add delete_request_store required when retention is enabled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:53:56 +01:00
Ullrich Schäfer
1e970ae475
Merge pull request #90 from trails-cool/internal-wildcard-dns
Use *.internal.trails.cool wildcard for internal services
2026-03-27 12:22:10 +00:00
Ullrich Schäfer
559f53d8fc
Use *.internal.trails.cool wildcard for internal services
Add wildcard DNS record for *.internal.trails.cool (A + AAAA) so
internal services don't leak hostnames in individual DNS entries.
Move Grafana from grafana.trails.cool to grafana.internal.trails.cool.

Future internal services just need a Caddy block — no DNS changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:19:47 +01:00
Ullrich Schäfer
bff9ff04a4
Merge pull request #89 from trails-cool/fix-grafana-deploy
Wire Grafana secrets through CD pipeline
2026-03-27 07:41:54 +00:00
Ullrich Schäfer
e105382c8f
Wire Grafana secrets through CD pipeline
Pass GRAFANA_USER, GRAFANA_PASSWORD, and GRAFANA_PASSWORD_HASH
from GitHub Actions secrets to the deploy script.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 08:39:25 +01:00
Ullrich Schäfer
d9b7a150ca
Merge pull request #88 from trails-cool/observability
Add observability: health, logging, metrics, Grafana
2026-03-27 06:59:05 +00:00
Ullrich Schäfer
49aadd04a9
Add observability: health endpoints, structured logging, metrics, Grafana stack
Health endpoints:
- /api/health (Journal) and /health (Planner) with DB connectivity check
- Docker healthchecks updated to use app health endpoints

Structured logging:
- Pino with JSON output in production, pretty-print in dev
- Request logging middleware in Planner (method, path, status, duration)
- Replaced console.log/error with structured logger in email and auth flows

Prometheus metrics:
- prom-client with default Node.js metrics + custom histograms/gauges
- /metrics endpoints on both apps
- http_request_duration, planner_active_sessions, brouter_request_duration

Monitoring stack:
- Prometheus, Loki, Grafana containers in docker-compose
- Grafana provisioned with datasources, dashboards, and alert rules
- Caddy access logging (JSON to stdout for Loki)
- grafana.trails.cool with basic auth via Caddy

Dashboards and alerting:
- Overview: request rate, error rate, latency p50/p95/p99
- Planner: active sessions, connected clients, BRouter latency
- Infrastructure: memory, CPU, event loop lag
- Alerts: disk >80%, app down 2min, error rate >5%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:59:44 +01:00
Ullrich Schäfer
ce964cae96
Merge pull request #87 from trails-cool/planner-route-interactions
Update OpenSpec artifacts for route interactions
2026-03-26 21:38:52 +00:00
Ullrich Schäfer
ba044610b7
Add route interactions: click-to-split, drag-to-reshape, colored route rendering
- Enrich BRouter response with per-point 3D coordinates and segment boundary
  tracking (EnrichedRoute interface)
- ColoredRoute component: plain, elevation gradient (green→yellow→red), and
  surface color modes with invisible wide polyline for click targeting
- Click-to-split: click on route polyline inserts waypoint at nearest point,
  mapped to correct segment via boundary indices
- MidpointHandles: draggable CircleMarkers at route segment midpoints for
  reshaping, hidden below zoom 12, opaque on hover
- Color mode toggle (select) synced via Yjs routeData
- i18n keys for color mode labels (en + de)
- Unit tests for segment boundary tracking (13 tests)
- E2E tests for enriched route response and color mode toggle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:36:42 +01:00
Ullrich Schäfer
5aef99b5ce
Merge pull request #86 from trails-cool/planner-route-interactions
Add route interactions: split, reshape, and colored rendering
2026-03-26 22:18:27 +01:00
Ullrich Schäfer
b080a15fb1
Add route interactions: click-to-split, drag-to-reshape, colored route rendering
- Enrich BRouter response with per-point 3D coordinates and segment boundary
  tracking (EnrichedRoute interface)
- ColoredRoute component: plain, elevation gradient (green→yellow→red), and
  surface color modes with invisible wide polyline for click targeting
- Click-to-split: click on route polyline inserts waypoint at nearest point,
  mapped to correct segment via boundary indices
- MidpointHandles: draggable CircleMarkers at route segment midpoints for
  reshaping, hidden below zoom 12, opaque on hover
- Color mode toggle (select) synced via Yjs routeData
- i18n keys for color mode labels (en + de)
- Unit tests for segment boundary tracking (13 tests)
- E2E tests for enriched route response and color mode toggle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:15:55 +01:00
Ullrich Schäfer
14a39dc7cc
Merge pull request #84 from trails-cool/merge-queue
Replace automerge action with GitHub merge queue
2026-03-26 18:28:23 +00:00
Ullrich Schäfer
ed4757382a
Use forked gitleaks-action with merge_group support
The upstream gitleaks/gitleaks-action@v2 errors on merge_group events.
Use our fork which includes https://github.com/gitleaks/gitleaks-action/pull/186.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:26:18 +01:00
Ullrich Schäfer
cce2ddea5c
Fix: gh pr merge --auto requires --merge flag
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:09:08 +01:00
Ullrich Schäfer
aed99ba423
Merge pull request #85 from trails-cool/fix-merge-queue-docs
Fix CLAUDE.md merge queue docs to use --auto
2026-03-26 19:02:46 +01:00
Ullrich Schäfer
eccc274652
Fix CLAUDE.md: use gh pr merge --auto, not --merge-queue
The --merge-queue flag doesn't exist in gh CLI. The correct flag is
--auto, which routes through the merge queue when it's enabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:02:27 +01:00
Ullrich Schäfer
746b09276c
Replace automerge action with GitHub native merge queue
The automerge GitHub Action (pascalgn/automerge-action) is removed in
favor of GitHub's built-in merge queue, which is now enabled on the
main branch ruleset.

- Delete automerge.yml workflow
- Add merge_group trigger to CI so it runs on queue candidates
- Remove workflow_run trigger for Automerge from CD
- Update CLAUDE.md to document gh pr merge --merge-queue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:40:11 +01:00
github-actions[bot]
fe3621aab3
Merge pull request #83 from trails-cool/emails 2026-03-26 17:27:26 +00:00
Ullrich Schäfer
346d72a759
Fix SMTP_URL and SMTP_FROM not passed to containers during deploy
The CD workflow exported SENTRY_RELEASE but not the SMTP env vars,
so docker-compose substituted them as empty strings and emails
silently failed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:24:17 +01:00
github-actions[bot]
ef333a2105
Merge pull request #82 from trails-cool/transactional-emails-planner-features 2026-03-26 00:03:23 +00:00
Ullrich Schäfer
0a8dd0b766
Add transactional emails (SMTP) and planner features (no-go areas, notes, crash recovery)
Transactional emails:
- Add nodemailer SMTP email module with dev-mode console logging
- Magic link template and welcome template with HTML + plain text
- Wire sendMagicLink into login flow, sendWelcome into registration
- Update privacy page and deploy docs for SMTP configuration

Planner features:
- No-go areas: draw polygons on map (leaflet-geoman), synced via Yjs,
  passed to BRouter as nogos parameter, route recomputes on change
- Session notes: collaborative Y.Text textarea in sidebar tab
- Crash recovery: periodic localStorage save of Yjs state, restore on reconnect
- Rate limit session creation (10/IP/hour) in /new route

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 01:00:42 +01:00
github-actions[bot]
05b5f6febf
Merge pull request #81 from trails-cool/archive-completed-changes 2026-03-25 23:45:24 +00:00
Ullrich Schäfer
538a69e1c5
Archive app-navigation and planner-multiplayer-awareness, sync specs
Both changes fully implemented. Sync delta specs to main:
- map-display: add Planner home page nav and cursor rendering requirements
- planner-session: add session participant awareness requirement

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 00:42:55 +01:00
github-actions[bot]
589cb4ab15
Merge pull request #80 from trails-cool/security-hardening-verification 2026-03-25 23:34:14 +00:00
Ullrich Schäfer
b6908fb288
Fix picomatch audit vulnerability and complete security hardening verification
Override picomatch to 4.0.4 to resolve high-severity ReDoS (GHSA-c2c7-rcm5-vvqj),
verify all security hardening tasks (gitleaks, pnpm audit, Docker non-root,
Caddy headers, scanner blocking), sync specs, and archive the change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 00:31:27 +01:00
github-actions[bot]
433886772f
Merge pull request #79 from trails-cool/fix-i18n 2026-03-25 23:18:22 +00:00
Ullrich Schäfer
3f7499ff06 Fix i18n: move init to root.tsx and downgrade to stable versions
Move initI18n() from entry.client.tsx to root.tsx in both Planner and
Journal so i18n is initialized before any component renders. Downgrade
react-i18next to 16.6.1 and i18next to 25.10.4 to avoid hydration
mismatch issues introduced in newer versions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 23:18:15 +00:00
Ullrich Schäfer
30ae68dd69
Merge pull request #78 from trails-cool/fix-i18n-sync-init
Fix i18n: synchronous init before hydration
2026-03-25 22:58:58 +01:00
Ullrich Schäfer
18ac8a1ce3
Fix i18n: use initImmediate false for sync initialization
i18n.init() is async by default. With initImmediate: false, it
initializes synchronously so translations are ready before React
hydrates. Without this, useTranslation() runs before i18n is ready,
showing raw translation keys.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:58:51 +01:00
Ullrich Schäfer
59ef2a4b5e
Merge pull request #77 from trails-cool/fix-i18n-entry-client
Fix i18n: init before hydration in entry.client
2026-03-25 22:52:26 +01:00
Ullrich Schäfer
d9ef69c0a0
Fix i18n: init in entry.client.tsx before hydration
With custom entry.client.tsx, initI18n() in root.tsx runs too late —
the client hydrates before i18n is initialized, showing raw translation
keys. Move initI18n() to entry.client.tsx where it runs before
startTransition/hydrateRoot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:52:20 +01:00
Ullrich Schäfer
ebe4ccd838
Merge pull request #76 from trails-cool/fix-node25-corepack
Fix Docker builds: corepack removed in Node 25
2026-03-25 22:43:00 +01:00
Ullrich Schäfer
871ed81869
Fix Docker builds: corepack removed in Node 25
Node 25 no longer ships corepack. Replace corepack enable/prepare with
npm install -g pnpm.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:42:54 +01:00
Ullrich Schäfer
462da3ac5d
Merge pull request #75 from trails-cool/fix-csp-blob-worker
Fix CSP: allow blob: for Yjs web worker
2026-03-25 22:40:11 +01:00
Ullrich Schäfer
5d542a9391
Fix CSP: allow blob: for Yjs web worker
y-websocket creates a Web Worker from a blob URL. The CSP script-src
needs blob: and an explicit worker-src directive to allow this.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:40:05 +01:00
Ullrich Schäfer
a5248872c7
Merge pull request #64 from trails-cool/dependabot/docker/apps/planner/node-25-slim
Bump node from 24-slim to 25-slim in /apps/planner
2026-03-25 13:21:32 +01:00
Ullrich Schäfer
17328833f4
Merge pull request #65 from trails-cool/dependabot/docker/apps/journal/node-25-slim
Bump node from 24-slim to 25-slim in /apps/journal
2026-03-25 13:21:17 +01:00
Ullrich Schäfer
fb13f7b8ef
Merge pull request #66 from trails-cool/dependabot/github_actions/actions/cache-5
Bump actions/cache from 4 to 5
2026-03-25 13:21:09 +01:00
Ullrich Schäfer
35c0c20310
Merge pull request #67 from trails-cool/dependabot/github_actions/pnpm/action-setup-5
Bump pnpm/action-setup from 4 to 5
2026-03-25 13:21:03 +01:00