chore: scrub instance details and personal data from committed artefacts

Blood-pressure fixtures in the in-target integration suite were copied
from live readings; they are now synthetic with the same in/out split so
every assertion holds. Hard-coded operator hostnames move behind env vars
(DEPLOY_LOGS_URL, HEALTHLOG_PROD_URL), the codex client identifies via
the public repository URL, the OpenAPI servers entry points at a neutral
example host, and remaining workflow, docs and comment references to the
operator's instance or working notes are generalised. The compose
whitelist also gains DEPLOY_WEBHOOK_SECRET, which was missing and made
the deploy webhook unusable on compose installs.
This commit is contained in:
Marc-André Bombeck committed 2026-06-10 11:00:26 +02:00
1 parent b8c2b2e8c5
commit 76012bf8e2
17 files changed
+145 -105

No files matched your search

@@ -6,7 +6,7 @@ import { NextRequest } from "next/server";
* needed to harden against first-visit MITM and to scope the
* Withings backend host to its surface only.
*
* - HSTS now carries `; preload` so `healthlog.bombeck.io` is
* - HSTS now carries `; preload` so the served host is
* eligible for the Chromium HSTS preload list.
* - The `wbsapi.withings.net` `connect-src` entry only ships on
* the Withings surfaces (`/settings/integrations/withings/*`
@@ -21,8 +21,8 @@ import { NextResponse } from "next/server";
* it to devices, so the origin only needs to respond reliably; Apple's
* one-hour CDN TTL pairs with the `Cache-Control` directive below.
*
* Both `healthlog.bombeck.io` (maintainer prod) and `demo.healthlog.dev`
* (demo) share this handler — the response is host-independent.
* Every host serving this app (an operator's instance, the public demo)
* shares this handler — the response is host-independent.
*/
const AASA_APP_ID = "S8WDX4W5KX.dev.healthlog.app";
+5 -2
View File
@@ -9,7 +9,7 @@ import { dispatchLocalisedNotification } from "@/lib/notifications/dispatch-loca
import { prisma } from "@/lib/db";
/**
* Deploy-status webhook (phase C2 / v1.4.15).
* Deploy-status webhook (v1.4.15).
*
* Coolify (Settings → Notifications → Webhook) calls this endpoint after
* every deploy attempt. We verify the request via a shared secret in the
@@ -135,7 +135,10 @@ async function notifyAdminsOfFailure(event: NormalizedEvent): Promise<void> {
application: event.applicationName,
error: event.error ?? "",
deployment: event.deploymentUuid ?? "",
logsUrl: "https://apps-01.bombeck.io",
// Optional: operators can point this at their deployment
// dashboard (e.g. the Coolify UI). Falls back to an empty
// string so the body template still composes cleanly.
logsUrl: process.env.DEPLOY_LOGS_URL ?? "",
},
metadata: {
source: "deploy-webhook",
+14 -19
View File
@@ -8,7 +8,7 @@ import Link from "next/link";
*
* 1. **Apple App Store Connect** requires a publicly reachable Privacy-
* Policy URL before the iOS native app's first submission. The URL
* registered with ASC is `https://healthlog.bombeck.io/privacy`.
* registered with ASC is this route on the maintainer's instance.
* 2. **GDPR / DSGVO Art. 13 / 14 + Art. 15-22** — data-subject rights
* enumerated with concrete in-app routes the user can hit.
* 3. **EU MDR 2017/745 + MDCG 2021-24** — the boundary "HealthLog is
@@ -342,14 +342,12 @@ export default function PrivacyPage() {
>
github.com/MBombeck/HealthLog
</a>{" "}
veröffentlicht. Diese Erklärung beschreibt, wie die unter{" "}
veröffentlicht. Diese Erklärung beschreibt, wie die
Instanz, über die Sie diese Seite abrufen, und die
zugehörige iOS-Anwendung <em>HealthLog for iOS</em>{" "}
(Bundle-ID{" "}
<code className="bg-muted rounded px-1 py-0.5 text-xs">
healthlog.bombeck.io
</code>{" "}
betriebene Instanz und die zugehörige iOS-Anwendung{" "}
<em>HealthLog for iOS</em> (Bundle-ID{" "}
<code className="bg-muted rounded px-1 py-0.5 text-xs">
io.bombeck.healthlog
dev.healthlog.app
</code>
) personenbezogene Daten verarbeiten.
</p>
@@ -359,7 +357,7 @@ export default function PrivacyPage() {
Deutschland). Eigenständig betriebene, selbst-gehostete
Instanzen unterliegen der Datenschutzerklärung ihres
jeweiligen Betreibers; dieses Dokument gilt ausschließlich
für die genannte Domain.
für diese Instanz.
</p>
<p>
Für Fragen oder Datenschutz-Anfragen siehe Abschnitt 11
@@ -381,14 +379,11 @@ export default function PrivacyPage() {
github.com/MBombeck/HealthLog
</a>{" "}
under the GNU Affero General Public License v3.0. This
policy describes how the hosted instance at{" "}
policy describes how the instance serving this page and
the companion iOS application <em>HealthLog for iOS</em>{" "}
(bundle identifier{" "}
<code className="bg-muted rounded px-1 py-0.5 text-xs">
healthlog.bombeck.io
</code>{" "}
and the companion iOS application{" "}
<em>HealthLog for iOS</em> (bundle identifier{" "}
<code className="bg-muted rounded px-1 py-0.5 text-xs">
io.bombeck.healthlog
dev.healthlog.app
</code>
) handle personal data.
</p>
@@ -397,7 +392,7 @@ export default function PrivacyPage() {
this instance (an individual based in Germany). Self-
hosted deployments controlled by a different operator are
governed by that operator&apos;s own privacy policy; this
document applies only to the hostname above.
document applies only to this instance.
</p>
<p>
For questions or data-subject requests, see section 11
@@ -1211,8 +1206,8 @@ export default function PrivacyPage() {
/>
<SubProcessor
name="Cloudflare, Inc."
roleDe="Autoritative DNS-Auflösung für die bombeck.io-Zonen. Die Anwendung selbst steht nicht hinter dem Cloudflare-Proxy."
roleEn="Authoritative DNS for the bombeck.io zones. The application itself is not behind the Cloudflare proxy; only the DNS resolution path is."
roleDe="Autoritative DNS-Auflösung für die Domain dieser Instanz. Die Anwendung selbst steht nicht hinter dem Cloudflare-Proxy."
roleEn="Authoritative DNS for this instance's domain. The application itself is not behind the Cloudflare proxy; only the DNS resolution path is."
dataDe="Quell-IP und User-Agent zum Zeitpunkt der Auflösung."
dataEn="Source IP address and user-agent at resolution time."
locationDe="USA; globales Anycast-Netz von Cloudflare."
@@ -58,8 +58,8 @@ describe("<AdminShell>", () => {
// The 13-section strip is ~1700 CSS px wide. Without
// `no-scrollbar` (defined in `globals.css`) Chromium/WebKit paint
// an always-on horizontal scrollbar at the top of every admin
// page. Production probe at https://healthlog.bombeck.io/admin/
// api-tokens at Pixel-5 viewport pins this as the *only* element
// page. A live probe of /admin/api-tokens at a Pixel-5 viewport
// pins this as the *only* element
// with `overflow-x:auto AND scrollWidth > clientWidth`, so the
// class is the load-bearing fix.
const html = renderShell({ active: "api-tokens" });
@@ -9,7 +9,7 @@ import { useTranslations } from "@/lib/i18n/context";
* v1.4.25 W9e — notice strip for AI-initial locales.
*
* HealthLog ships German + English as maintainer-curated translations
* (the maintainer, Marc, owns both bodies). French, Spanish, Italian
* (the maintainer owns both bodies). French, Spanish, Italian
* and Polish ship as AI-initial translations — they pass the
* locale-integrity test (full key parity, no empty values, no TODOs)
* but the prose has not been hand-reviewed for register, regional
+1 -1
View File
@@ -92,7 +92,7 @@ function isSlugRejection(status: number, bodyExcerpt: string): boolean {
}
const ORIGINATOR = "healthlog";
const USER_AGENT = "HealthLog/1.0 (+https://healthlog.bombeck.io)";
const USER_AGENT = "HealthLog/1.0 (+https://github.com/MBombeck/HealthLog)";
interface CodexClientConfig {
accessToken: string;
+4 -1
View File
@@ -52,7 +52,10 @@ const openApiBase: Pick<
contact: { name: "HealthLog", url: "https://healthlog.dev" },
},
servers: [
{ url: "https://healthlog.bombeck.io", description: "Production" },
{
url: "https://healthlog.example.com",
description: "Your self-hosted instance",
},
{ url: "http://localhost:3000", description: "Local dev" },
],
tags: [
+3 -2
View File
@@ -65,8 +65,9 @@ const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", {
* Format a Date as `YYYY-MM-DD` in `Europe/Berlin`. The seven Insight
* status helpers (`bmi`, `blood-pressure`, `weight`, `pulse`, `mood`,
* `medication-compliance`, `general`) all key their per-day cache on
* this — Marc's primary tenant runs in Berlin and the cache action
* row reads identically across helpers when the formatter lives here.
* this — the maintainer's primary tenant runs in Berlin and the cache
* action row reads identically across helpers when the formatter lives
* here.
*
* For per-user surfaces, prefer `userDayKey(date, tz)` from `./format`.
* This helper exists for the cache-key path where the day-bucket is