The Planner's save-to-journal callback makes a cross-origin request to
trails.cool from planner.trails.cool. Add the Journal's origin to the
Planner's connect-src CSP directive.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Uses Playwright's virtual WebAuthn authenticator (CDP) to test:
- Register with passkey + sign in (full round-trip)
- Login fails with no credential (friendly error message)
- Duplicate email rejection
- Duplicate username rejection
Would have caught the credential encoding bug.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bug: @simplewebauthn v13 changed credential.id from Uint8Array to
base64url string. Buffer.from(string) without encoding stored the
ASCII text instead of decoded bytes. Authentication then failed to
match because it correctly decoded with "base64url".
Fix: Buffer.from(credential.id, "base64url") in both registration
and add-passkey flows.
Also: catch WebAuthn "not allowed" errors and show a friendly message
instead of the raw browser error.
Existing passkeys must be re-registered after deploy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@trails-cool/db doesn't export a db instance — only createDb and withDb.
The health check was importing a non-existent export, always failing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removes replayIntegration and replay sample rates from both apps.
Quota was already exhausted.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Returns SENTRY_RELEASE (set at build time) as the version field,
making it easy to verify which commit is deployed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The route files existed but weren't registered in the explicit routes.ts,
so React Router never compiled them into the build.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The threshold expressions were missing the 'expression' field referencing
the query refId, causing 'no variable specified for refId C' errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Panels:
- Stat cards: total users, routes, activities, active planner sessions
- Time series: cumulative user growth, route growth
- Bar charts: signups/day, routes/day, planner sessions/day
- Table: top 20 routes by distance
All queries use the grafana_reader PostgreSQL datasource.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create grafana_reader role with SELECT-only access on all schemas
- Init script runs on postgres first boot (docker-entrypoint-initdb.d)
- Grafana PostgreSQL datasource provisioned with read-only credentials
- Enables SQL queries in dashboards (user count, routes, etc.)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove commit message [restart-all] detection — doesn't work with
GitHub merge commits. Keep only the explicit workflow_dispatch input.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exporters:
- postgres_exporter: DB connections, transactions, cache hit ratio, query stats
- node_exporter: host CPU, memory, disk, network
- cAdvisor: per-container CPU and memory usage
PostgreSQL:
- Enable pg_stat_statements for query-level performance tracking
- Track index scans vs sequential scans, cache hit ratio
Dashboard (service-health.json):
- DB: connections, size, transactions/s, slow queries, cache hit ratio, index usage
- Host: disk gauge, CPU, memory, network I/O, disk I/O
- BRouter: request latency p50/p95/p99, container CPU + memory
- All containers: CPU and memory comparison
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
node:25-slim doesn't include CA certificates, causing sentry-cli
SSL errors: "unable to get local issuer certificate". Also removes
debug logging from previous troubleshooting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The token file may have a trailing newline that causes sentry-cli
API requests to fail with "API request failed".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Disable Sentry Vite plugin telemetry (telemetry: false)
- Pass SENTRY_RELEASE as build-arg (not secret) since it's just the git sha
- Keep only SENTRY_AUTH_TOKEN as Docker secret
The release version was showing as *** because Docker secrets get masked
by GitHub Actions. Git sha is not sensitive — safe as a build arg.
The sourcemap warning from react-router is a known upstream issue (ships
without sourcemaps) and is harmless.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pnpm approve-builds in Dockerfile ran after install, so the approval
wasn't picked up. Use onlyBuiltDependencies in package.json instead —
this is read during install and allows build scripts to run.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Approve @sentry/cli and esbuild build scripts so postinstall runs
- Use --mount=type=secret instead of ARG for SENTRY_AUTH_TOKEN
(fixes Docker warning about secrets in build args)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The decrypted secrets.app.env contains values with angle brackets
(SMTP_FROM) that bash interprets as redirects. The build job only
needs SENTRY_AUTH_TOKEN, so extract it with grep instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Write decrypted env files to infrastructure/ instead of /tmp/ so
strip_components: 1 correctly places them on the server.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>