From 3ba5efb025b3c50abd69649ee0ccbd341435298c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Wed, 11 Nov 2026 22:27:22 +0100 Subject: [PATCH] chore(scripts): drop private host names from the demo tooling The demo migration was named after the host it targets, and its README, the v1.5 demo seed and a contributing note named the maintainer's servers, a container id, an app id and two account ids. The script already takes every host, container and account from its environment, so the prose now says source and target and lists the variables instead. It is renamed to migrate-demo-account.sh; the password guard follows the new name. --- CONTRIBUTING.md | 2 +- scripts/README.md | 2 +- .../demo-published-password-guard.test.ts | 8 +- ...ADME.md => migrate-demo-account.README.md} | 85 ++++++----- ...demo-edge01.sh => migrate-demo-account.sh} | 144 +++++++++--------- scripts/seed-demo-v15.sql | 6 +- 6 files changed, 124 insertions(+), 123 deletions(-) rename scripts/{migrate-demo-edge01.README.md => migrate-demo-account.README.md} (72%) rename scripts/{migrate-demo-edge01.sh => migrate-demo-account.sh} (88%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f3bf2d402..157eb8949 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -185,7 +185,7 @@ If you want to introduce a locale that isn't shipped (e.g. `sv`, `cs`): ### Long-term -Fink covers browser editing today. It edits the files in this repository directly and keeps no copy of the translations anywhere else. If the community outgrows it, a self-hosted [Weblate](https://weblate.org/) instance remains the fallback. Tracking issue: `v1.6 — Weblate self-hosted on edge-01`. +Fink covers browser editing today. It edits the files in this repository directly and keeps no copy of the translations anywhere else. If the community outgrows it, a self-hosted [Weblate](https://weblate.org/) instance remains the fallback. ## Code of Conduct diff --git a/scripts/README.md b/scripts/README.md index 34949a93e..4f7b49c39 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -37,7 +37,7 @@ typecheck. - **`repair-google-health-sleep.ts`**: re-syncs every Google Health connection so nights stored twice before v1.28.18 collapse to one set; dry-run by default, `--run` to execute. - **`i18n-key-tool.mjs`**, **`split-description.mjs`**: edit one key across every `messages/*.json` (show, set, drop) and move an over-long card description's tail into its own key; **`measure-description-lengths.mjs`** measures the Settings and Admin description strings against the 120-character guideline. - **`audit-column-readers.ts`**: lists write-only schema columns and open `@pending-consumer` markers; a review tool, not a gate. -- **`migrate-demo-edge01.sh`**: copies the demo account from one instance to a separate demo instance; see its README. +- **`migrate-demo-account.sh`**: copies the demo account from one instance to a separate demo instance; see its README. - **`__tests__/`**: tests for the scripts above. ## Conventions diff --git a/scripts/__tests__/demo-published-password-guard.test.ts b/scripts/__tests__/demo-published-password-guard.test.ts index e9167a684..eac5f20c4 100644 --- a/scripts/__tests__/demo-published-password-guard.test.ts +++ b/scripts/__tests__/demo-published-password-guard.test.ts @@ -8,7 +8,7 @@ import { describe, expect, it } from "vitest"; * demo.healthlog.dev. Its Argon2id hash lives in two places that must agree: * * - scripts/seed-demo.ts writes it when a demo tenant is seeded - * - scripts/migrate-demo-edge01.sh overwrites it after a migration load + * - scripts/migrate-demo-account.sh overwrites it after a migration load * * Public issue #805: the migration used to carry the SOURCE account's hash * across instead, so once the operator rotated their own demo password the @@ -18,7 +18,7 @@ import { describe, expect, it } from "vitest"; const REPO_ROOT = join(__dirname, "..", ".."); const SEED = join(REPO_ROOT, "scripts", "seed-demo.ts"); -const MIGRATE = join(REPO_ROOT, "scripts", "migrate-demo-edge01.sh"); +const MIGRATE = join(REPO_ROOT, "scripts", "migrate-demo-account.sh"); /** Every Argon2id hash literal in a file, in source order. */ function argon2Hashes(path: string): string[] { @@ -27,7 +27,7 @@ function argon2Hashes(path: string): string[] { } describe("published demo password", () => { - it("is the same hash in the seed script and the edge01 migration", () => { + it("is the same hash in the seed script and the demo migration", () => { const seeded = argon2Hashes(SEED); const migrated = argon2Hashes(MIGRATE); @@ -55,7 +55,7 @@ describe("published demo password", () => { // ...and it must be proven, not merely attempted. Without the check a // mangled hash would reach a visitor's login screen instead of the script. expect(script).toContain( - "ABORT/FAIL: demo password_hash on edge01 is not the published literal", + "ABORT/FAIL: demo password_hash on the target is not the published literal", ); }); diff --git a/scripts/migrate-demo-edge01.README.md b/scripts/migrate-demo-account.README.md similarity index 72% rename from scripts/migrate-demo-edge01.README.md rename to scripts/migrate-demo-account.README.md index f21a6414d..dfb6a17dd 100644 --- a/scripts/migrate-demo-edge01.README.md +++ b/scripts/migrate-demo-account.README.md @@ -1,28 +1,30 @@ -# migrate-demo-edge01.sh +# migrate-demo-account.sh -Copies **exactly one** user — the demo account -`usr_demo_cf31025295714ece8d91f5af13afd76d` — from the production apps01 -Postgres to the **separate** edge01 demo Postgres. No other user's rows are -transferred under any circumstance. +Copies **exactly one** user, the demo account named by `DEMO_USER_ID`, from a +source instance's Postgres to the **separate** Postgres of a demo instance. No +other user's rows are transferred under any circumstance. > Review the script first, then run it deliberately. It performs destructive, -> demo-scoped deletes on the edge01 demo DB, then loads the apps01 demo slice. -> It never writes to apps01 (source is read-only). +> demo-scoped deletes on the target demo DB, then loads the demo slice from the +> source. It never writes to the source (read-only). ## How it connects -- **Source (apps01):** SSH alias `apps-01`, container `db-pg8wggwogo8c4gc4ks0kk4ss` - (stable), psql `healthlog`/`healthlog`. -- **Target (edge01):** SSH alias `edge-01`. The demo Postgres is a separate - Coolify resource whose container name is **not hardcoded** — the script - resolves it at runtime via `docker ps | grep -iE 'postgres|^db-'` and aborts - if it cannot pick a single match (override with `DST_DB_CONTAINER=db-xxxx`). - The demo **app** uuid is `ck8cs4osswg8w440gskw08w8`; match its linked - Postgres resource, not the docs/landing DB. -- **Edge demo user id is resolved at runtime** by `username='demo'` (it may - differ from the apps01 id). The wipe phase is scoped to that resolved id; the - load then brings the apps01 row in, so afterwards the edge01 demo user - carries the apps01 canonical id. +Everything instance-specific comes from the environment: + +- `DEMO_USER_ID`: the user id to copy. `PROTECT_USER_ID`: an id that must + never appear on the target (the operator's own account). +- `SRC_SSH` and `SRC_DB_CONTAINER_PATTERN`: the source host's ssh alias and a + `grep -E` pattern for its database container; psql `healthlog`/`healthlog`. +- `DST_SSH`: the target host's ssh alias. The demo Postgres container is + **not hardcoded**: the script resolves it at runtime via + `docker ps | grep -iE 'postgres|^db-'` and aborts if it cannot pick a single + match (override with `DST_DB_CONTAINER=db-xxxx`). Match the demo app's own + Postgres, not a docs or landing-page database on the same host. +- **The target demo user id is resolved at runtime** by `username='demo'` (it + may differ from the source id). The wipe phase is scoped to that resolved id; + the load then brings the source row in, so afterwards the target demo user + carries the source's canonical id. Each table is transferred with a scoped, server-side `\copy ... TO STDOUT` piped host-to-host into `\copy ... FROM STDIN`, using **explicit column lists @@ -97,9 +99,9 @@ acknowledgement). `*_oauth_states` / `*_connect_tickets`, telegram scheduled-deletions + reminder messages, clinician_share_links, provider_health) — tied to live credentials. -- **Encrypted-at-rest payloads we can't decrypt on edge01:** +- **Encrypted-at-rest payloads the target cannot decrypt:** `insight_narratives` and all `coach_*` (coach_conversations, coach_messages, - coach_facts, coach_usage). The edge01 instance does not hold the prod + coach_facts, coach_usage). The target does not hold the source's encryption key, so the AES-256-GCM ciphertext would be unreadable. The same reason drives NULLing `cycle_day_logs.sensitive_encrypted` / `notes_encrypted` while still carrying that table's plaintext cycle fields. @@ -109,16 +111,16 @@ acknowledgement). import_jobs, host_metrics, feedback, recommendation_feedback, and all rollup tables (measurement_rollups, mood_entry_rollups, strain_trimp_cache, cycle_predictions) — these - rebuild on edge01. + rebuild on the target. - **No rows:** workouts / workout_routes / workout_samples (demo has none). - **Instance / global:** app_settings; global catalogues (mood_tags, - cycle_symptoms) — these pre-exist on edge01 and the carried link rows + cycle_symptoms) pre-exist on the target and the carried link rows reference the global ids only. ## Encryption note -The edge01 demo instance is assumed **not** to share the apps01 encryption -key. Every column whose value is AES-256-GCM ciphertext under the prod key is +The demo instance is assumed **not** to share the source's encryption key. +Every column whose value is AES-256-GCM ciphertext under the source key is either NULLed (secrets on `users`, `cycle_day_logs.*_encrypted`) or skipped entirely (`insight_narratives`, `coach_messages.encryptedContent`). Carrying that ciphertext would produce undecryptable rows that the app's fail-closed @@ -128,38 +130,37 @@ loader would reject. The demo user is loaded with `role='ADMIN'` so the admin surfaces are viewable in the demo. This is **only safe because DEMO_MODE must stay ON** on -edge01 — the proxy layer (`src/proxy.ts`) blocks every mutating request in +the target: the proxy layer (`src/proxy.ts`) blocks every mutating request in demo mode, so an admin demo session can browse admin pages but cannot change -anything. If DEMO_MODE were ever off on edge01, this admin demo account would +anything. If DEMO_MODE were ever off on the target, this admin demo account would be a write-capable admin. Keep DEMO_MODE enabled. ## Safety mechanisms built into the script - `set -euo pipefail` and `ON_ERROR_STOP=1` on every psql call. -- **Pre-flight guard (apps01):** for each carried set, a query that must +- **Pre-flight guard (source):** for each carried set, a query that must return `0` foreign-owned rows — aborts on any non-zero. Includes a check that the demo owns **0 custom mood_tags** (so every carried - `mood_entry_tag_link` points at a global tag that pre-exists on edge01) plus + `mood_entry_tag_link` points at a global tag that pre-exists on the target) plus a defensive "non-global tag" link count. -- **Scoped wipe-first (edge01):** FK-child-first deletes, every one scoped to - the runtime-resolved edge01 demo id. No unscoped delete anywhere. Refuses to - proceed if `username='demo'` on edge01 resolves to the operator id. -- **Post-load verification (edge01):** per-table demo row counts; a hard - assertion that the operator id `cmlupy4tn000001rpzx1pxvz7` appears in zero - rows; a check that user_id-bearing carried tables hold only the demo id; and +- **Scoped wipe-first (target):** FK-child-first deletes, every one scoped to + the runtime-resolved target demo id. No unscoped delete anywhere. Refuses to + proceed if `username='demo'` on the target resolves to the operator id. +- **Post-load verification (target):** per-table demo row counts; a hard + assertion that `PROTECT_USER_ID` appears in zero rows; a check that user_id-bearing carried tables hold only the demo id; and a confirmation the demo user exists with `role=ADMIN`. ## Caveats / things to confirm before running - **`FOREIGN_HITS` assertion** counts _all_ non-demo rows in the carried - tables on edge01, not just what this run inserted. On a clean - single-demo-user DB it is `0`. If edge01 legitimately hosts other demo-only + tables on the target, not just what this run inserted. On a clean + single-demo-user DB it is `0`. If the target legitimately hosts other demo-only accounts, treat a non-zero value as a warning to inspect, not an automatic failure (the script prints it as a WARNING, not an abort). -- Confirm edge01 psql credentials are `healthlog`/`healthlog` (the script +- Confirm the target's psql credentials are `healthlog`/`healthlog` (the script verifies connectivity before any write; adjust `PG_USER` / `PG_DB` if the demo Postgres resource uses different ones). -- The script assumes edge01's schema is at the same (or a compatible) Prisma - migration level as apps01. A column present on apps01 but missing on edge01 - fails the `\copy` loudly rather than silently corrupting data — run - migrations on edge01 first if the two have drifted. +- The script assumes the target's schema is at the same (or a compatible) + Prisma migration level as the source. A column present on the source but + missing on the target fails the `\copy` loudly rather than silently + corrupting data; run migrations on the target first if the two have drifted. diff --git a/scripts/migrate-demo-edge01.sh b/scripts/migrate-demo-account.sh similarity index 88% rename from scripts/migrate-demo-edge01.sh rename to scripts/migrate-demo-account.sh index d623229f0..df958fc05 100755 --- a/scripts/migrate-demo-edge01.sh +++ b/scripts/migrate-demo-account.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash # -# migrate-demo-edge01.sh — copy EXACTLY ONE user (the demo account) from the -# production apps01 Postgres to the SEPARATE edge01 demo Postgres. +# migrate-demo-account.sh: copy EXACTLY ONE user (the demo account) from the +# source Postgres to the SEPARATE target demo Postgres. # # ============================================================================ # WHAT THIS SCRIPT DOES (and the isolation guarantees it makes) # ============================================================================ # # It transfers a single, demo-scoped slice of health data so the public demo -# instance on edge01 shows a realistic-looking account WITHOUT carrying any +# instance on the target shows a realistic-looking account WITHOUT carrying any # real-operator data. The ONLY user copied is: # # $DEMO_USER_ID (the single demo account, supplied via env) @@ -26,7 +26,7 @@ # 3. No secret / credential column is transferred. The users SELECT NULLs # every encrypted / token / integration-secret column explicitly. The # cycle-day-log SELECT NULLs the two app-encrypted columns (they are -# keyed to the prod encryption key, which is NOT present on edge01). +# keyed to the prod encryption key, which is NOT present on the target). # # The transfer mechanism per table is a scoped server-side COPY piped # host-to-host: @@ -42,12 +42,12 @@ # - an added/dropped column on either side fails loudly rather than # silently shifting data into the wrong column. # -# Tables load in FK-parent-first order. The edge01 demo rows are wiped first -# (FK-child-first), scoped to the edge01 demo user id resolved at runtime. +# Tables load in FK-parent-first order. The target demo rows are wiped first +# (FK-child-first), scoped to the target demo user id resolved at runtime. # # THIS SCRIPT IS FOR REVIEW. Read it, then run it deliberately. It performs -# DESTRUCTIVE deletes on the edge01 demo DB (scoped to the demo user) followed -# by inserts. It NEVER writes to apps01 (source is read-only). +# DESTRUCTIVE deletes on the target demo DB (scoped to the demo user) followed +# by inserts. It NEVER writes to the source (source is read-only). # # ---------------------------------------------------------------------------- # EXCLUDED ENTIRELY (intentionally NOT copied) @@ -59,16 +59,16 @@ # fitbit_connections, all *_oauth_states / *_connect_tickets, # telegram_scheduled_deletions, telegram_reminder_messages, # clinician_share_links, provider_health. -# - Encrypted-at-rest payloads we cannot decrypt on edge01: +# - Encrypted-at-rest payloads we cannot decrypt on the target: # insight_narratives, coach_messages (coach_* entirely). # - Coach: coach_conversations, coach_messages, coach_facts, coach_usage. # - Ops / transient / regenerable: idempotency_keys, rate_limits, # data_backups, import_jobs, host_metrics, feedback, # recommendation_feedback, all rollup tables (measurement_rollups, -# mood_entry_rollups, strain_trimp_cache, cycle_predictions) — these REGENERATE on edge01. +# mood_entry_rollups, strain_trimp_cache, cycle_predictions) — these REGENERATE on the target. # - workouts / workout_routes / workout_samples — demo has none. # - app_settings — instance-level, not user data. -# - Global catalogues (mood_tags, cycle_symptoms) — pre-exist on edge01; +# - Global catalogues (mood_tags, cycle_symptoms) — pre-exist on the target; # carried link rows reference these GLOBAL ids only (see guards below). # set -euo pipefail @@ -129,21 +129,21 @@ psql_src() { # so psql_dst is defined after resolution. echo "==============================================================" -echo " HealthLog demo-user migration apps01 -> edge01" +echo " HealthLog demo-user migration source -> target" echo " demo user : $DEMO_USER_ID" echo " src host : $SRC_SSH (container $SRC_DB_CONTAINER)" echo " dst host : $DST_SSH (container resolved below)" echo "==============================================================" # ============================================================================ -# RESOLVE the edge01 demo DB container (NOT hardcoded — it is a separate +# RESOLVE the target demo DB container (NOT hardcoded — it is a separate # Coolify Postgres resource whose container name can change on redeploy). # ============================================================================ echo if [[ -n "${DST_DB_CONTAINER:-}" ]]; then echo "## Using DST_DB_CONTAINER from env: $DST_DB_CONTAINER" else - echo "## Resolving edge01 demo Postgres container ..." + echo "## Resolving target demo Postgres container ..." DST_DB_CONTAINER="$(ssh "$DST_SSH" "docker ps --format '{{.Names}}' | grep -iE 'postgres|^db-' || true")" echo "candidate containers on $DST_SSH:" echo "$DST_DB_CONTAINER" | sed 's/^/ /' @@ -159,7 +159,7 @@ else DST_DB_CONTAINER="$(printf '%s\n' "$DST_DB_CONTAINER" | head -n1)" fi readonly DST_DB_CONTAINER -echo "resolved edge01 demo DB container: $DST_DB_CONTAINER" +echo "resolved target demo DB container: $DST_DB_CONTAINER" # Confirm the psql user/db actually work on the resolved container before we # touch anything (fail fast on a wrong container guess). @@ -167,31 +167,31 @@ psql_dst() { # Usage: psql_dst "" ssh "$DST_SSH" "docker exec -i $DST_DB_CONTAINER psql -X -v ON_ERROR_STOP=1 -U $PG_USER -d $PG_DB $*" } -echo "## Verifying edge01 DB connectivity ..." +echo "## Verifying target DB connectivity ..." psql_dst "-c 'SELECT current_database(), current_user;'" # ============================================================================ -# RESOLVE the edge01 demo user id (it MAY differ from the apps01 id!). -# We resolve by username='demo'. After this migration the edge01 demo user -# will carry the apps01 canonical id (we delete the old row + load the apps01 -# row). Scope EVERY wipe to whatever the edge01 id is right now. +# RESOLVE the target demo user id (it MAY differ from the source id!). +# We resolve by username='demo'. After this migration the target demo user +# will carry the source canonical id (we delete the old row + load the source +# row). Scope EVERY wipe to whatever the target id is right now. # ============================================================================ echo -echo "## Resolving edge01 demo user id (by username='demo') ..." -EDGE_DEMO_ID="$(psql_dst "-tA -c \"SELECT id FROM users WHERE username = 'demo';\"" | tr -d '[:space:]')" -if [[ -z "$EDGE_DEMO_ID" ]]; then - echo "note: no existing demo user on edge01 (username='demo') — wipe phase is a no-op." +echo "## Resolving target demo user id (by username='demo') ..." +TARGET_DEMO_ID="$(psql_dst "-tA -c \"SELECT id FROM users WHERE username = 'demo';\"" | tr -d '[:space:]')" +if [[ -z "$TARGET_DEMO_ID" ]]; then + echo "note: no existing demo user on the target (username='demo') — wipe phase is a no-op." else - echo "edge01 existing demo user id: $EDGE_DEMO_ID" - if [[ "$EDGE_DEMO_ID" == "$REAL_USER_ID" ]]; then - echo "ABORT: edge01 username='demo' resolves to the REAL operator id. Refusing." + echo "existing target demo user id: $TARGET_DEMO_ID" + if [[ "$TARGET_DEMO_ID" == "$REAL_USER_ID" ]]; then + echo "ABORT: target username='demo' resolves to the REAL operator id. Refusing." exit 1 fi fi -readonly EDGE_DEMO_ID +readonly TARGET_DEMO_ID # ============================================================================ -# PRE-FLIGHT GUARD (on apps01 source): assert every carried set is demo-only. +# PRE-FLIGHT GUARD (on the source): assert every carried set is demo-only. # Each query MUST return 0. A non-zero result means a scope predicate is wrong # and we would leak another user's rows — ABORT immediately. # ============================================================================ @@ -243,13 +243,13 @@ preflight "mood_entry_tag_links(join)" \ # GUARD: mood_entry_tag_links must reference ONLY global mood_tags (user_id IS # NULL on the tag). The demo is asserted to have 0 custom mood_tags; if a link -# referenced a per-user tag id, the FK would break on edge01 (that tag row is +# referenced a per-user tag id, the FK would break on the target (that tag row is # not carried). Custom-tag count MUST be 0. DEMO_CUSTOM_MOODTAGS="$(psql_src "-tA -c \"SELECT count(*) FROM mood_tags WHERE user_id = '$DEMO_USER_ID';\"" | tr -d '[:space:]')" echo " demo custom mood_tags = $DEMO_CUSTOM_MOODTAGS (must be 0 — all links point to GLOBAL tags)" if [[ "$DEMO_CUSTOM_MOODTAGS" != "0" ]]; then echo "ABORT: demo user owns $DEMO_CUSTOM_MOODTAGS custom mood_tags. mood_entry_tag_links" - echo " would reference non-global tag ids that are NOT carried -> FK break on edge01." + echo " would reference non-global tag ids that are NOT carried -> FK break on the target." echo " Carry mood_tags (user-scoped) before mood_entry_tag_links, or skip the links." exit 1 fi @@ -260,55 +260,55 @@ preflight "mood_entry_tag_links(non-global tag)" \ echo "## PRE-FLIGHT guard passed — every carried set is demo-only." # ============================================================================ -# WIPE-FIRST on edge01 (idempotent, demo-scoped ONLY). FK-child-first order. -# Every DELETE is scoped to the resolved edge01 demo user id ($EDGE_DEMO_ID) +# WIPE-FIRST on the target (idempotent, demo-scoped ONLY). FK-child-first order. +# Every DELETE is scoped to the resolved target demo user id ($TARGET_DEMO_ID) # or via a join back to it. NEVER an unscoped DELETE. Skipped entirely if no # existing demo user was found. # ============================================================================ echo -if [[ -n "$EDGE_DEMO_ID" ]]; then - echo "## WIPE edge01 demo rows (scoped to $EDGE_DEMO_ID), FK-child-first ..." - # NOTE: edge01 FKs are ON DELETE CASCADE for most child rows; the explicit +if [[ -n "$TARGET_DEMO_ID" ]]; then + echo "## WIPE target demo rows (scoped to $TARGET_DEMO_ID), FK-child-first ..." + # NOTE: target FKs are ON DELETE CASCADE for most child rows; the explicit # child-first deletes below are belt-and-braces (some child tables hang off # medications/mood_entries, not the user) and keep the operation transparent. psql_dst < '$DEMO_USER_ID'); \"" | tr -d '[:space:]')" echo " non-demo user_id rows across carried tables = $FOREIGN_HITS" -echo " NOTE: this counts EVERY non-demo row in these tables on edge01, not just" +echo " NOTE: this counts EVERY non-demo row in these tables on the target, not just" echo " what this script loaded. On a clean demo DB it should be 0. If the" -echo " edge01 demo DB legitimately holds other demo-only accounts, scope" +echo " target demo DB legitimately holds other demo-only accounts, scope" echo " this assertion to the rows this run inserted before treating >0 as fatal." if [[ "$FOREIGN_HITS" != "0" ]]; then - echo "WARNING: $FOREIGN_HITS non-demo rows exist in carried tables on edge01." + echo "WARNING: $FOREIGN_HITS non-demo rows exist in carried tables on the target." echo " Inspect before serving the demo (see note above)." fi echo -echo "HARD assertion 3: the demo user exists on edge01 with role=ADMIN and the apps01 id." +echo "HARD assertion 3: the demo user exists on the target with role=ADMIN and the source id." psql_dst "-c \"SELECT id, username, role, telegram_enabled FROM users WHERE id = '$DEMO_USER_ID';\"" echo echo "==============================================================" echo " DONE. Review the counts above. Demo user is role=ADMIN; the" -echo " app's DEMO_MODE must remain ON on edge01 so all mutations are" +echo " app's DEMO_MODE must remain ON on the target so all mutations are" echo " blocked at the proxy layer while admin pages stay viewable." echo "==============================================================" diff --git a/scripts/seed-demo-v15.sql b/scripts/seed-demo-v15.sql index a15ca2411..960a8d5ac 100644 --- a/scripts/seed-demo-v15.sql +++ b/scripts/seed-demo-v15.sql @@ -1,7 +1,7 @@ -- v1.5.0 demo seed — sliding-window, NOW()-anchored, full iOS+Withings story. -- -- The pre-v1.5 demo seed shipped only WEIGHT / BLOOD_PRESSURE / PULSE / --- BODY_FAT / SLEEP / STEPS — and the demo DB on edge-01 had drifted to +-- BODY_FAT / SLEEP / STEPS, and the demo DB had drifted to -- a static snapshot whose last reading was ten days old. For the iOS -- public-beta cut we need a fresh picture: data through today, every -- type the dashboard tiles can render, a source mix that shows the @@ -17,8 +17,8 @@ -- falls back to live SQL (the slim rollup-tier reader) and the boot -- backfill repopulates on the next entrypoint sweep. -- --- Usage on edge-01: --- docker exec -i agos0oo88gsgg88kcg4swosw \ +-- Usage on the demo host: +-- docker exec -i \ -- psql -U healthlog -d healthlog < seed-demo-v15.sql BEGIN;