Files
infrapuzzle/docs/adr/0005-personal-data-isolation-and-authentik-boundary.md
T

8.9 KiB

status, date, decision-makers, consulted, informed
status date decision-makers consulted informed
accepted 2026-10-11
Moritz Graf
Antigravity

ADR 0005: Personal Data Isolation, Authentik Authentication Boundaries, and Hosting Segregation

Context and Problem Statement

On 2026-10-11, during the development of mobile-first PWA dashboards for baby monitoring (Mila Graf) and personal nutrition/Garmin energy balance (Moritz Graf), personal health, sleep, diaper, and nutrition data was temporarily made available at unauthenticated root URLs:

  • https://hosting.haumdaucher.de/mila.html
  • https://hosting.haumdaucher.de/moritz.html

While ADR 0004 established the conceptual routing design (/family/* protected by Traefik ForwardAuth, public recipes at /recipes/*), a gap analysis revealed critical systemic vulnerabilities:

  1. Unapplied Kubernetes Ingress: The ForwardAuth Ingress (minio-hosting-family) declared in k8s/minio/hosting-ingress.yaml was never applied to the Kubernetes cluster.
  2. Missing Authentik Identity Provider: Authentik had no Proxy Provider or Application configured for hosting.haumdaucher.de, returning HTTP 404 on ForwardAuth checks.
  3. Dual-Publishing Anti-Pattern: Dashboard generator scripts (update_website.py, update_moritz_website.py) mirrored files to root (/mila.html, /moritz.html) for convenience, completely bypassing the planned /family/ path boundary.
  4. Lack of Automated Ingress & Storage Auditing: No automated test or CI/Cron audit prevented personal data from being uploaded to public prefixes.

How do we establish a permanent, verifiable, and automated security boundary that guarantees personal and family data can never be exposed publicly without authentication?


Decision Drivers

  • Zero Public Exposure for Personal Data: Health, sleep, nutrition, body, and family data must NEVER be accessible without authentication.
  • Defense-in-Depth: Security must not rely on a single layer (e.g. secret URLs or developer memory). Protection must be enforced at identity provider, reverse proxy, storage bucket, and generator script levels.
  • Declarative Infrastructure as Code (ADR 0003): All Authentik providers, policies, groups, and Kubernetes Ingresses must be versioned in Git and applied via Terraform/Kubectl.
  • Automated Guardrails & Fast Feedback: Continuous audits must detect and quarantine any unauthorized files outside protected paths.
  • Seamless PWA Experience: The PWA capabilities (manifest, service worker, offline shell) must function within the authenticated cookie session without breaking ForwardAuth.

Decision Outcome

Chosen Strategy: Multi-Layer Boundary Enforcement with ForwardAuth, Strict Storage Segregation, and Continuous Auditing.

1. Storage & URL Path Segmentation Architecture

Internet Request to https://hosting.haumdaucher.de
                    │
                    ▼
          ┌───────────────────┐
          │ Traefik Ingress   │
          └─────────┬─────────┘
                    │
         Path Inspection & Priority
                    │
   ┌────────────────┴────────────────┐
   │                                 │
Path: /family/*                   Path: /* (Fallback)
Priority: 100                     Priority: 10
   │                                 │
   ▼                                 ▼
┌─────────────────────────┐       ┌─────────────────────────┐
│ Authentik ForwardAuth   │       │ Public Ingress          │
│ • Host: hosting.haumd...│       │ • ONLY /recipes/*       │
│ • Allowed: 'family' &   │       │ • ONLY /showcase/*      │
│   'infra-admins' groups │       │ • NO personal files     │
└────────────┬────────────┘       └────────────┬────────────┘
             │                                 │
     HTTP 200 (Authed)                         │
             │                                 │
             ▼                                 ▼
   ┌──────────────────┐               ┌──────────────────┐
   │ MinIO Bucket     │               │ MinIO Bucket     │
   │ key: family/*    │               │ key: public/*    │
   │ (Protected Data) │               │ (Public Static)  │
   └──────────────────┘               └──────────────────┘

Rule 1: Canonical Protected Prefix /family/

  • All dashboards containing personal, familial, or physiological metrics MUST reside under /family/:
    • https://hosting.haumdaucher.de/family/mila.html (Baby sleep, feeds, diapers)
    • https://hosting.haumdaucher.de/family/moritz.html (Personal nutrition, Garmin deficits, health)
    • https://hosting.haumdaucher.de/family/nutrition.html (Family nutrition overview)
    • https://hosting.haumdaucher.de/family/sleep.html (Sleep analytics)
  • Associated PWA assets (manifests, service workers) MUST also be scoped within /family/ (e.g. /family/mila.webmanifest, /family/sw.js).

Rule 2: Root Prefix / Strictly Restricted

  • The root of hosting.haumdaucher.de is restricted to public static assets (/recipes/*, /showcase/*).
  • Publishing scripts MUST NEVER write personal dashboards to the bucket root.

2. Declarative Authentik ForwardAuth Configuration

Authentik configuration is codified in terraform/authentik.tf:

  1. Proxy Provider (authentik_provider_proxy.hosting_family):
    • External Host: https://hosting.haumdaucher.de
    • Mode: forward_single
    • Bound to authentik_outpost.embedded
    • Session duration: 30 days (access_token_validity = "days=30")
  2. Application (authentik_application.hosting_family):
    • Slug: hosting-family
    • Protocol Provider: authentik_provider_proxy.hosting_family.id
  3. Role-Based Policy Bindings:
    • Group family (Moritz & Amelie Graf): Granted access
    • Group infra-admins: Granted access
    • Unauthenticated requests are redirected (HTTP 302) to https://auth.haumdaucher.de/application/o/authorize/...

3. Kubernetes Ingress Deployment

k8s/minio/hosting-ingress.yaml defines the two ingress tiers:

  • minio-hosting-family (Priority 100):
    • Path: /family (Prefix)
    • Middlewares: auth-authentik-forwardauth@kubernetescrd,minio-hosting-add-prefix@kubernetescrd
  • minio-hosting (Priority 10):
    • Path: / (Prefix)
    • Middlewares: minio-hosting-add-prefix@kubernetescrd

4. Generator Scripts & Codebase Hardening

  1. Elimination of Root Duplication:
    • skills/datastore/scripts/update_website.py: Removed all S3 upload and local sync calls to root mila.html. Target is strictly family/mila.html.
    • skills/datastore/scripts/update_moritz_website.py: Removed S3 upload and local sync calls to root moritz.html. Target is strictly family/moritz.html (and family/nutrition.html).
  2. PWA Scope Alignment:
    • Web App Manifests configure start_url: "/family/mila.html" and scope: "/family/".
    • Service workers are registered at /family/sw.js.
  3. Telegram Link Consistency:
    • Agent prompts for mila (Topic 20) and koch (Topic 21) only broadcast the secure URL: https://hosting.haumdaucher.de/family/....

5. Automated Continuous Leak Audit Guard

An automated audit check runs continuously (via OpenClaw periodic audit and datastore maintenance):

  • Inspects the MinIO hosting bucket root (prefix="").
  • Detects any forbidden files matching patterns: *mila*, *moritz*, *nutrition*, *sleep*, *schlaf*, *health*, *diaper*.
  • If detected:
    1. Immediately deletes/quarantines the offending object from the public bucket root.
    2. Raises a critical notification/event to the system log.

Consequences

Positive

  • Guaranteed Confidentiality: Family and personal health data is physically impossible to access without passing Authentik ForwardAuth.
  • Reproducible Infrastructure: The entire authentication chain is codified in Terraform and Kubernetes manifests.
  • Persistent Mobile PWA: Authentik's 30-day session lifetime ensures seamless standalone mobile app operation without repetitive login prompts.
  • Automated Safety Net: Even if a developer or agent script mistakenly attempts to mirror a file to root, the automated audit deletes it.

Negative & Mitigations

  • Authentication Step on First Mobile Launch:
    • Mitigation: When installing the PWA on iOS/Android, the user authenticates once via Authentik. The persistent session cookie maintains login across app launches.