# Space content — Forgejo markdown (read + write path) See **ADR-0002**. App DB holds metadata; page bodies are markdown in Forgejo. **Operator runbook (seed, bind, cluster smoke):** `docs/deploy.md` § Operator runbook — spaces on app.coulomb.social. ## Read (T04) 1. Space row has `forgejo_owner`, `forgejo_repo`, `default_branch`, `content_root`. 2. App fetches `{content_root}/index.md` (or `?page=` → `{slug}.md`) via Forgejo raw URL. 3. HTML is sanitized and cached in-process (successes only). Env (non-secret defaults in settings): | Variable | Purpose | |----------|---------| | `FORGEJO_BASE_URL` | default `https://forgejo.coulomb.social` | | `FORGEJO_TOKEN` | optional; needed for private repos | | `FORGEJO_WEBHOOK_SECRET` | optional; enables push webhook cache bust | ## Write / sync (T05) **Git remains the source of truth.** There is no in-app markdown editor in v1. ### Operator / author workflow 1. Open the space on app.coulomb.social. 2. Click **Edit in Forgejo** (file editor) or **View source**. 3. Commit on the space’s `default_branch` (usually `main`). 4. Click **Refresh content** on the space page (or rely on webhook below). Demo seed (public raw, no token): ```bash python manage.py seed_demo_space # binds demo → coulomb/coulomb-social @ docs/space-fixtures/demo/pages ``` ### Webhook (optional, recommended) So authors do not need to click Refresh after every push: 1. Set `FORGEJO_WEBHOOK_SECRET` in the app env Secret (not in git). 2. In Forgejo: repo → **Settings → Webhooks → Add webhook → Gitea**. 3. **Target URL:** `https://app.coulomb.social/app/spaces/hooks/forgejo/` 4. **HTTP Method:** POST, content type JSON. 5. **Secret:** same value as `FORGEJO_WEBHOOK_SECRET` (Forgejo signs with HMAC-SHA256). 6. Trigger: **Push** events. Auth accepted by the app: - `X-Gitea-Signature: ` (Forgejo default when secret set), or - `X-Coulomb-Webhook-Secret: ` Response JSON: `{ ok, owner, repo, cache_entries_cleared, spaces_matched }`. ### Fail closed - Missing binding → clear UI error, no Bubble fallback. - Fetch errors → error card; previous cache entry is not used after Refresh. ## Related - `docs/adr/ADR-0002-space-content-forgejo-markdown.md` - `python manage.py seed_demo_space`