From 7cb2ab5a961709944294fa47b7ad5f9172f9c25c Mon Sep 17 00:00:00 2001 From: bogdanpydev Date: Tue, 30 Jun 2026 01:19:53 +0300 Subject: [PATCH] docs(catalog-gaps): record formbricks shadow-bind dead-end The one-shot-migrate + writable shadow-bind workaround over /home/nextjs/packages/database/migrations was attempted on a #182 revival draft and verified live to fail: the migration runner rm+recreates that hardcoded dir (no env override), which needs write on the 1001-owned parent no malmo UID has. Records the dead-end on the existing nonroot-data-ownership formbricks entry so it isn't re-attempted; the app stays blocks-start, gated on userns-remap (#193 / NEXT.md). --- docs/dev/catalog-import-gaps.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/catalog-import-gaps.md b/docs/dev/catalog-import-gaps.md index 6005ce5f..0b02c35b 100644 --- a/docs/dev/catalog-import-gaps.md +++ b/docs/dev/catalog-import-gaps.md @@ -217,6 +217,7 @@ Unlike `docs/progress/` entries (frozen ADR snapshots), this file is **mutable b - **What breaks:** install never completes. No malmo-assignable UID can write the 1001-owned baked dir — verified that **root under `cap_drop: ALL` also can't** (no `CAP_DAC_OVERRIDE`); only uid 1001 works. Separately, the bundled Postgres can't chown its datadir as root under the stripped `CAP_CHOWN`. - **Why malmo can't satisfy it (v1):** `writeOverride` pins one malmo-owned `user:` + `cap_drop: ALL` + `no-new-privileges` on **every** service (`internal/lifecycle/lifecycle.go`), and admission rejects a numeric `user:` (`internal/admission/admission.go`) — by design, since a manifest-named UID would alias a real host principal under malmo's no-userns-remap model (`APP_ISOLATION.md` # Runtime identity & data ownership, `THREAT_MODEL.md`). The main app hardcodes an internal UID for its own baked files and ignores the runtime `user:` — the **same hardcoded-internal-UID class as poznote/postiz**, which waits on userns-remap. The "conflicting per-service-UID" framing (#193 case 2) adds no separate need: the Postgres sidecar adopts any non-root UID fine via `service_user: true` (the runtime `user:` malmo would pin), so the only service that truly blocks is the main app's hardcoded 1001 — still case 1. Per-service distinct UIDs wouldn't help. - **Status:** open — curation-reject, no `catalog/formbricks/` files written. Platform limitation triaged on **#193** (filed off #182). Same fate as poznote/postiz; deferred to `NEXT.md` # User-namespace remap for hardcoded-internal-UID app images (the count-trigger is now met, but the mechanism stays gated on the feasibility spike there). The four Gate-D *degradations* recorded in #182 (link-survey reachability, OAuth callbacks, SMTP, third-party integrations) are independent of this gap and don't apply — the app never reaches a degraded-but-running state. +- **Re-confirmed live (2026-06-30) — shadow-bind workaround is structurally impossible, don't re-attempt:** a later session revived #182 with a drafted entry that split migrations into a one-shot `formbricks-migrate` service (`SKIP_STARTUP_MIGRATION=true` on the app) and **shadow-mounted** a writable bind (`./data/db-migrations-staging`, chowned to the runtime UID) over `/home/nextjs/packages/database/migrations`, betting that gives the runner a writable dir. Booted under the real override (non-1001 `user:`, `cap_drop: ALL`, `no-new-privileges`, binds chowned to the runtime UID): `pgvector/pgvector:pg18` Postgres and `valkey` both come up **healthy**, but `formbricks-migrate` exits 1 with `EACCES: permission denied, rmdir '/home/nextjs/packages/database/migrations'`. The runner doesn't write *into* that dir — `runMigrations` (`packages/database/src/scripts/migration-runner.ts`) `fs.rm`s the dir **itself** then `fs.mkdir`s it on every run, and the path is hardcoded (`PRISMA_MIGRATIONS_DIR = path.resolve(__dirname, "../../migrations")`, **no env override**). `rmdir` of the dir (or of the bind's mountpoint) needs write on the **1001-owned parent** `/home/nextjs/packages/database/`, which no malmo-assigned UID has; the parent can't be shadowed (it holds the read-only migration source + compiled `dist/`), and the app's own startup-migrate path runs the identical code. So the bind-shadow can never work — this is the same uid-1001 wall, just hit one level up. Still `blocks-start`, still gated on userns-remap; **no `catalog/formbricks/` files shipped.** ### managed-pg-role-creation — windmill (2026-06-25)