Skip to content

docs(catalog-gaps): record formbricks shadow-bind dead-end (blocked on userns-remap) - #283

Open
bogdanpydev wants to merge 1 commit into
mainfrom
docs/182-formbricks-blocked-reconfirm
Open

docs(catalog-gaps): record formbricks shadow-bind dead-end (blocked on userns-remap)#283
bogdanpydev wants to merge 1 commit into
mainfrom
docs/182-formbricks-blocked-reconfirm

Conversation

@bogdanpydev

Copy link
Copy Markdown
Collaborator

What

Appends a live re-confirmation to the existing nonroot-data-ownership — formbricks (2026-06-20) entry in docs/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-migrate service (SKIP_STARTUP_MIGRATION=true on the app) and shadow-mounting a writable bind over /home/nextjs/packages/database/migrations. Booted under the real brain override (non-1001 user:, cap_drop: ALL, no-new-privileges, binds chowned to the runtime UID):

  • pgvector/pgvector:pg18 Postgres and valkey come up healthy — the sidecar adaptation is fine.
  • formbricks-migrate exits 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 — it fs.rms the dir itself then fs.mkdirs it every run, and the path is hardcoded (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/). 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). No catalog/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.

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).
@bogdanpydev bogdanpydev self-assigned this Jun 29, 2026
@bogdanpydev
bogdanpydev requested a review from onel June 29, 2026 22:20
@bogdanpydev bogdanpydev mentioned this pull request Jun 29, 2026
5 tasks
@greptile-apps

greptile-apps Bot commented Jun 29, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Documentation-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 fs.rm on the bind mountpoint, parent not shadowable), cross-references are correct, and no catalog or code files are touched.

No files require special attention; only docs/dev/catalog-import-gaps.md is changed and the edit is straightforwardly additive.

Important Files Changed

Filename Overview
docs/dev/catalog-import-gaps.md Appends a single re-confirmation bullet to the formbricks entry, documenting that the shadow-bind workaround was attempted and is structurally blocked by the same uid-1001 wall one level up; no code changes.

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.**

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Catalog] Add Formbricks

1 participant