docs(catalog-gaps): record formbricks shadow-bind dead-end (blocked on userns-remap) - #283
docs(catalog-gaps): record formbricks shadow-bind dead-end (blocked on userns-remap)#283bogdanpydev wants to merge 1 commit into
Conversation
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).
Confidence Score: 5/5Documentation-only change; no runnable code is modified. The entire change is one appended bullet in a markdown file recording a dead-end investigation. The technical reasoning in the new text is internally consistent (parent-dir write required for No files require special attention; only Important Files Changed
Reviews (1): Last reviewed commit: "docs(catalog-gaps): record formbricks sh..." | Re-trigger Greptile |
| - **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.** |
There was a problem hiding this comment.
Forward-dated re-confirmation timestamp
The new bullet is dated 2026-06-30 but the PR is opened on 2026-06-29. If the live boot was run today, the date should be 2026-06-29 to match the actual event. A one-day discrepancy is harmless in practice, but other entries in this file use the date of the actual run rather than the anticipated merge date, so it's worth aligning.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
What
Appends a live re-confirmation to the existing
nonroot-data-ownership — formbricks (2026-06-20)entry indocs/dev/catalog-import-gaps.md, recording that the one-shot-migrate + writable shadow-bind workaround for Formbricks' uid-1001 migrations dir is structurally impossible, so it isn't re-attempted.Why
A revival draft for #182 tried to defeat the original blocker by splitting migrations into a one-shot
formbricks-migrateservice (SKIP_STARTUP_MIGRATION=trueon the app) and shadow-mounting a writable bind over/home/nextjs/packages/database/migrations. Booted under the real brain override (non-1001user:,cap_drop: ALL,no-new-privileges, binds chowned to the runtime UID):pgvector/pgvector:pg18Postgres andvalkeycome up healthy — the sidecar adaptation is fine.formbricks-migrateexits 1:EACCES: permission denied, rmdir '/home/nextjs/packages/database/migrations'.The runner (
packages/database/src/scripts/migration-runner.ts) doesn't write into that dir — itfs.rms the dir itself thenfs.mkdirs it every run, and the path is hardcoded (path.resolve(__dirname, "../../migrations"), no env override).rmdirof 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 + compileddist/). Same uid-1001 wall, one level up. The app's own startup-migrate path runs the identical code.Outcome
Formbricks stays
blocks-start, still gated on user-namespace remap (#193 /NEXT.md). Nocatalog/formbricks/files are shipped (bail per ADAPT, DON'T FORCE). This PR is the doc record only.Refs #182, #193. Does not close #182 — it remains blocked.