# ADR-0002 — Space content as markdown in Forgejo | Field | Value | |-------|--------| | Status | **Provisional** (accepted for T04 vertical slice; revisit write path) | | Date | 2026-08-12 | | Deciders | bernd | | Workplan | CSOC-WP-0004-T03 | ## Context coulomb.social co-creation **spaces** need durable page/artefact content. Bubble stores opaque page graphs. The rebuild should: - keep content **agent- and human-editable** in git, - version and review changes like normal software, - avoid making Postgres the system of record for long-form prose, - align with Forgejo already hosting fleet code on railiance01. Identity and space **metadata** stay in the app DB (`Space`, memberships). ## Decision ### 1. Canonical content form - **Markdown** (CommonMark + limited GFM: tables, fenced code, images). - Static assets (images, attachments) live beside markdown under a content root. - Postgres stores **metadata and binding only** — never full page bodies as SoR. ### 2. Repository layout (v1) **One Forgejo repository per space** under a dedicated org (recommended: `coulomb-spaces` or tenant-scoped org later). ```text // README.md # optional space intro pages/ index.md # default landing page .md assets/ # images etc. referenced from pages ``` | Field on `Space` | Meaning | |------------------|---------| | `forgejo_owner` | org/user | | `forgejo_repo` | repo name | | `default_branch` | usually `main` | | `content_root` | default `pages` | **Branch policy (v1):** app reads from `default_branch` only. PRs for review are a human/Forgejo workflow; the app does not merge PRs in v1. **Monorepo alternative (deferred):** single repo with `spaces//…` — rejected for v1 to keep permissions and migration packages simple. ### 3. Read path (T04) 1. App resolves `Space` by tenant + slug (authz already enforced). 2. If binding incomplete → empty state (no silent Bubble fetch). 3. Fetch file via **Forgejo raw/contents API** (preferred) or shallow cache: - HTTP GET with service token from env/OpenBao. - Cache rendered HTML or raw markdown in memory/disk with short TTL + optional webhook invalidation later. 4. Render markdown → HTML with a locked-down sanitizer (no raw script). 5. Fail closed on 404/403/network errors with operator-visible reason. ### 4. Write path (T05 — provisional) **v1 preference:** **edit in Forgejo** (web UI or git) + app refresh. - Lowest security surface (no app-held write credentials required if public-read internal repos use deploy token read-only). - Optional later: in-app editor → commit as bot user via API. **Not in v1:** bidirectional live sync with Bubble. ### 5. Mapping from Bubble (later CSOC-WP-0001) | Bubble concept | Target | |----------------|--------| | Space / room-like container | `Space` row + Forgejo repo | | Page / chunk prose | `pages/.md` | | Attachments | `assets/…` | | Permissions | `SpaceMembership` + NetKingdom groups (refine later) | Export scripts should emit markdown files + a manifest JSON for binding fields. ### 6. Secrets | Secret | Storage | |--------|---------| | Forgejo API token (read, later write) | K8s Secret / OpenBao — env e.g. `FORGEJO_TOKEN` | | Forgejo base URL | non-secret config e.g. `FORGEJO_BASE_URL=https://forgejo.coulomb.social` | Never commit tokens. Never render tokens in Session details. ## Consequences **Positive** - Content is git-native and agent-friendly. - Clear boundary: app = membership + UX; Forgejo = document history. - Migration can ship repos per space without rewriting history into SQL. **Negative / follow-ups** - Need Forgejo org, tokens, and network reachability from the app pod. - Offline/local dev needs a stub or fixture markdown path. - Search and cross-space queries need a separate index later. ## Open questions (non-blocking for T04) 1. Org name: `coulomb-spaces` vs per-tenant org. 2. Private repos only vs public read for some community spaces. 3. When to add webhook-driven cache purge. 4. Whether user OAuth to Forgejo is required for in-app write (T05). ## References - CSOC-WP-0004, Space model in `coulomb_social.apps.spaces` - ADR-0001 NetKingdom identity - Host posture: `docs/deploy.md` (app.coulomb.social)