2026-08-12 18:38:27 +02:00
# Architecture Blueprint — coulomb.social content & platform
| Field | Value |
|-------|--------|
2026-08-13 00:48:33 +02:00
| Status | **draft — decisions recorded 2026-08-13** |
2026-08-12 18:38:27 +02:00
| Product | coulomb.social rebuild (`coulomb-social` ) |
2026-08-13 00:48:33 +02:00
| Related | stage-1 cut, capability maps, authoring, stale/derived, upgrade path |
| Non-goals | Implementing UI chrome here; full markitect feature build in this doc |
2026-08-13 00:27:44 +02:00
2026-08-13 00:48:33 +02:00
## 0. Decision log (2026-08-13)
2026-08-13 00:27:44 +02:00
2026-08-13 00:48:33 +02:00
| # | Topic | Decision |
|---|--------|----------|
| D1 | **Content plane** | **Start thin:** directory + markdown files, **secured in git** . Supported **upgrade path** : thin → **shard-wiki** (when wiki/federation matters) via markitect-shaped content; then → **kontextual** (structure, scale, rich media) as a **supported upgrade** of the information base. |
| D2 | **asset-store** | **Extract later.** Stage-1 may use local files next to markdown (and/or temporary blob helpers); do not block on a new repo. |
| D3 | **PolicyGateway / content authz ownership** | **Do not invent a parallel owner.** Prefer **NetKingdom consolidation** so identity *and* authorization decisioning for app resources become the obvious path (flex-auth / NK as one story). Until that lands, coulomb uses coarse product authz (session + SpaceMembership + tenant); content plane stays fail-closed when a PDP is wired. |
| D4 | **Structure UX** | Aspire to **outline-editor flexibility** : **explode** one markdown into smaller **connected** documents; **implode** selection(s) back into one file. **Default = live** (spreadsheet-like: includes/derived resolve to current inputs automatically). **Pin** only when the user freezes a snapshot. |
2026-08-13 00:27:44 +02:00
2026-08-13 00:48:33 +02:00
Earlier freezes (unchanged): NetKingdom identity, Railiance deploy, markdown SoR + DB cache, markitect-tool not host, page-first, dual authoring, lazy projections + lock, stage-1 cut.
2026-08-13 00:27:44 +02:00
---
2026-08-12 18:38:27 +02:00
## 1. Architectural aim
Deliver a co-creation product where:
1. **Identity** (users, groups, tenants) is **NetKingdom** — not reimplemented.
2. **Runtime** is **Railiance** — not reimplemented.
2026-08-13 00:48:33 +02:00
3. **User-owned content** is **markdown (+ assets)** in a **git-secured directory tree** , exportable without the app database.
2026-08-13 00:27:44 +02:00
4. The **database is a cache / projection** for cards, memberships, search speed.
2026-08-13 00:48:33 +02:00
5. **markitect-tool** makes markdown structured and operable (parse, include, explode/implode, later derived slots) — **not** the product host.
6. **coulomb** owns co-creation UI (cards, spaces, outline-oriented editing, copy/transfer).
7. Knowledge work stays **fluent** : top-down and bottom-up; **live** reuse by default; pin when freezing matters.
8. Growth does not strand data: **thin git base → shard-wiki → kontextual** are **supported upgrades** , not rewrites from a proprietary store.
2026-08-12 18:38:27 +02:00
## 2. Non-negotiable rules
| # | Rule |
|---|------|
2026-08-13 00:48:33 +02:00
| R1 | Markdown (frontmatter + body + backmatter) + assets under a **directory tree** are the **exportable SoR** . |
| R2 | App DB holds **projections and app linkage only** ; disposable for content bodies. |
2026-08-12 18:38:27 +02:00
| R3 | Authn/IdP = **NetKingdom** ; coulomb maps OIDC → Member; no password store. |
2026-08-13 00:48:33 +02:00
| R4 | Authz: prefer **NetKingdom-consolidated** decisioning; no second user directory; no permanent coulomb-specific PolicyGateway fiefdom. |
2026-08-12 18:38:27 +02:00
| R5 | **markitect-main** is legacy reference, not a runtime foundation. |
2026-08-13 00:48:33 +02:00
| R6 | **markitect-tool** is the markdown structure dependency. |
| R7 | **Default content plane = thin dir+git.** PageOps hide the plane; upgrades swap adapters. |
2026-08-13 00:27:44 +02:00
| R8 | Stage-1 must: onboarding; spaces/pages with **Title / Abstractor / Visual** ; page copy; page transfer. |
2026-08-13 00:48:33 +02:00
| R9 | **Pages first** — address / live-transclude / explode-implode / promote; not a priori chunk storage. |
| R10 | **Live by default** for includes and other formula-like derived content; **pin** freezes a snapshot; pinned/stale use badge → alternative → **replace \| keep** + lock. |
| R11 | **Explode / implode** (outline-editor flexibility) are first-class structure operations (markitect primitives + coulomb UX). |
| R12 | **asset-store** extraction is **deferred** ; assets may live beside markdown until then. |
2026-08-12 18:38:27 +02:00
## 3. System shape
```text
┌──────────────────────────┐
│ NetKingdom │
2026-08-13 00:48:33 +02:00
│ OIDC · groups · tenants│
│ (authz consolidation →)│
2026-08-12 18:38:27 +02:00
└────────────┬─────────────┘
│ principal
▼
┌─────────────────────────────────────────────────────────────┐
│ coulomb.social (product) │
2026-08-13 00:48:33 +02:00
│ • Cards, spaces, dual authoring, outline explode/implode │
│ • PageOps; live resolve; pin when asked │
2026-08-13 00:27:44 +02:00
│ • Railiance deploy │
2026-08-12 18:38:27 +02:00
└───────────────┬─────────────────────────────┬───────────────┘
2026-08-13 00:48:33 +02:00
│ cache │ PageOps
2026-08-12 18:38:27 +02:00
▼ ▼
┌───────────────┐ ┌───────────────────────────┐
2026-08-13 00:48:33 +02:00
│ Postgres │ │ Content plane │
│ projections │ │ ★ A thin dir + git │
└───────────────┘ │ (default / stage-1) │
│ ↗ B shard-wiki upgrade │
│ ↗ C kontextual upgrade │
2026-08-12 18:38:27 +02:00
└─────────────┬─────────────┘
2026-08-13 00:48:33 +02:00
│
┌─────────────▼─────────────┐
2026-08-12 18:38:27 +02:00
│ markitect-tool │
2026-08-13 00:48:33 +02:00
│ structure · include LIVE │
│ explode/implode · query │
│ pin/derived (proposed) │
2026-08-13 00:27:44 +02:00
└─────────────┬─────────────┘
▼
┌───────────────────────────┐
2026-08-13 00:48:33 +02:00
│ Assets (files next to md) │
│ later: shared asset-store │
2026-08-12 18:38:27 +02:00
└───────────────────────────┘
```
2026-08-13 00:27:44 +02:00
### PageOps (store-agnostic)
2026-08-12 18:38:27 +02:00
| Op | Meaning |
|----|---------|
2026-08-13 00:48:33 +02:00
| `list_pages` / `get_page` / `put_page` / `archive_page` | CRUD |
| `copy_page` / `transfer_page` | Duplicate / re-home |
| `explode_page` / `implode_pages` | Outline-style split/join (markitect) |
| `resolve_view` | Live includes/derived for display (default) |
| `pin_slot` / `unpin` / `refresh_pinned` | Freeze snapshot; stale → alternative → replace\|keep |
| `project_field` / `regenerate` / `lock` | Title/abstractor/visual lifecycle |
| `put_visual` / `get_asset` | Binary next to tree (later asset-store) |
### Upgrade path (supported)
```text
Stage now A dir + .md + assets (+ git)
│ markitect-tool always
│
When wiki / B same markdown becomes shard-wiki shard(s)
federation │ PageOps adapter swap; export tree still valid
│
When scale / C kontextual KnowledgeAsset + blobs
rich media / │ markdown remains portable representation
governance │ PageOps adapter swap; migration tooling required
```
**Requirement:** every upgrade **preserves user export** of markdown+assets; no dead-end proprietary body store.
2026-08-12 18:38:27 +02:00
## 4. Information model (product)
| Concept | Representation |
|---------|----------------|
2026-08-13 00:48:33 +02:00
| **Page** | One markdown file (or connected set after explode) |
| **Space** | Directory / container + metadata page |
| **Tree** | Git-secured directory layout |
| **Card** | UI projection (title, abstractor, visual, actions, stale) |
| **Outline** | Headings / exploded connected docs — **not** Bubble chunk rows |
| **Live include** | `mkt:include` resolves current source (default) |
| **Pinned derived** | Optional frozen snapshot; explicit refresh |
2026-08-13 00:27:44 +02:00
| **Member / tenant** | NetKingdom + app session |
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
### 4.1 Outline editor flexibility (explode / implode)
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
Aspire to outline-editor UX powered by markitect **explode / implode** (and related compose/include):
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
| Action | User intent | Markitect analogue |
|--------|-------------|-------------------|
| **Explode** | Split a large page into smaller **connected** documents (sections as pages or files) | `mkt explode` (+ connectivity via includes/refs) |
| **Implode** | Merge selected docs/sections **back into one** markdown file | `mkt implode` / compose |
| **Navigate outline** | Treat structure as editable hierarchy | Headings + connected pages view |
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
Connectivity after explode: prefer **live includes or content references** so the outline stays one logical document until the user pins or permanently severs links.
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
### 4.2 Live by default (spreadsheet metaphor)
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
| | Live (default) | Pinned (exception) |
|--|----------------|-------------------|
| Includes / formula-like derived | Resolve to **current** inputs on view/export | Stored snapshot until refresh |
| Body of a normal page | User text as written | n/a |
| Projections (title/abstractor/visual) | Lazy fill then **persist** ; regenerate on demand | Lock freezes; stale offers alternative |
| Stale pinned / locked derived | — | Badge → generate alternative → **replace or keep** |
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
**Spreadsheet rule:** formulas stay live unless the user **pastes values** (pin).
Pinning is deliberate, not the default co-creation mode.
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
### 4.3 Dual authoring + lazy projections
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
Unchanged intent: top-down and bottom-up both natural; missing display fields lazy-fill then persist; regenerate + lock per field.
See `authoring-modes-and-lazy-projections.md` , `stale-derived-content.md` .
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
## 5. Content plane — decided start + upgrades
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
### 5.1 Default: A — thin directory + git
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
```text
spaces/< space-slug > /
pages/< page-slug > .md
assets/…
# entire tree in git (user- or service-mediated commits)
```
2026-08-12 18:38:27 +02:00
2026-08-13 00:27:44 +02:00
| | |
|--|--|
2026-08-13 00:48:33 +02:00
| **Why** | Simplest SoR; user data is files; matches “secured in git” |
| **coulomb role** | PageOps over filesystem/git; DB indexes cards |
| **markitect role** | Parse, live include, explode/implode, validate |
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
### 5.2 Upgrade B — shard-wiki
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
When multi-shard wiki behavior, overlays, or federation become product-relevant:
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
- Ingest the **same** markdown tree (or git repo) as a shard.
- PageOps adapter targets shard-wiki; UI unchanged.
- markitect-tool remains structure layer for resolve/explode.
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
### 5.3 Upgrade C — kontextual-engine
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
When asset identity, scale, rich multi-format media, audit/lineage, or ECM-class governance dominate:
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
- Supported **migration** of the information base into kontextual assets/blobs.
- Markdown remains the portable authoring/export form where applicable.
- **Not** required for stage-1.
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
### 5.4 What we do *not* do now
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
- Do not build coulomb **on** markitect-main.
- Do not require kontextual or shard-wiki for first productive transfer.
- Do not extract **asset-store** before a second consumer (D2).
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
## 6. Assets (deferred extract)
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
- Stage-1: files under `assets/` (or equivalent) in the content tree; optional simple content-addressing later.
- **asset-store** shared repo: **later** (seed from kontextual BlobStorage when extracted).
- Visual field stores a **ref** into that tree (or future store).
2026-08-12 18:38:27 +02:00
## 7. Identity & access control
```text
2026-08-13 00:27:44 +02:00
Browser → NetKingdom OIDC → coulomb session
2026-08-13 00:48:33 +02:00
├─ product: tenant_id, SpaceMembership (and future NK groups)
└─ long-term: NetKingdom-consolidated authz (flex-auth / NK)
so PolicyGateway ownership becomes trivial or unused
2026-08-12 18:38:27 +02:00
```
2026-08-13 00:48:33 +02:00
| Now | Target |
|-----|--------|
| Session + membership + tenant isolation | Same + NK-group claims |
| Content plane fail-closed if PDP present | PDP = consolidated NK story |
| No coulomb-owned IdP | Unchanged |
**D3:** Avoid multi-team thrash on “who owns PolicyGateway”; push consolidation in NetKingdom so apps only pass principal + resource attributes.
2026-08-12 18:38:27 +02:00
## 8. Deploy sketch (Railiance)
| Component | Deploy |
|-----------|--------|
2026-08-13 00:27:44 +02:00
| coulomb-social | Existing chart; `app.coulomb.social` |
2026-08-13 00:48:33 +02:00
| Content tree | Volume or git-backed storage accessible to the app; **git** is SoR discipline |
| Assets | Beside markdown until asset-store |
2026-08-12 18:38:27 +02:00
| NetKingdom | Existing KeyCape / Authelia |
2026-08-13 00:48:33 +02:00
## 9. Stage-1 product scope
2026-08-13 00:27:44 +02:00
2026-08-13 00:48:33 +02:00
**Must:**
2026-08-13 00:27:44 +02:00
2026-08-13 00:48:33 +02:00
- Onboarding (NetKingdom)
- Spaces/pages: Title, Abstractor, Visual, body
- Copy, transfer
- Cards; dual authoring; lazy projections
- Content as **dir+git** via PageOps
- Live includes when markitect is wired; pin optional
2026-08-13 00:27:44 +02:00
2026-08-13 00:48:33 +02:00
**Aspire early (structure UX):**
2026-08-13 00:27:44 +02:00
2026-08-13 00:48:33 +02:00
- Outline-oriented **explode / implode**
2026-08-12 18:38:27 +02:00
2026-08-13 00:48:33 +02:00
**Later:** social, marketplace, monetization, shard-wiki/kontextual upgrades, asset-store extract.
2026-08-13 00:27:44 +02:00
2026-08-13 10:04:46 +02:00
## 10. ADRs
| ADR | Topic | Status |
|-----|--------|--------|
| ADR-0001 | NetKingdom identity | accepted |
| ADR-0002 | Forgejo vertical slice | partly superseded by 0003/0004 |
| **ADR-0003** | Page-centric markdown SoR, authoring, live/pin | **accepted** |
| **ADR-0004** | Thin git plane, PageOps, upgrades B/C | **accepted** |
| ADR-0005 | asset-store extraction | later |
| — | NetKingdom authz consolidation | platform (NK) |
2026-08-13 00:48:33 +02:00
Markitect-tool: live include (done), explode/implode (done), pin/derived proposal (`proposed-stale-derived-and-include-pinning.md` ).
## 11. Spikes (narrowed)
With D1– D4, prioritize:
1. **Thin tree layout + git write path** (PageOps on files).
2. **markitect** parse + **live** include in get/view.
3. **Explode/implode** UX prototype on real pages.
4. **Lazy title/abstractor** + stale replace\|keep.
5. Export zip/tar of tree without DB.
Defer deep shard-wiki/kontextual spikes except **export/import round-trip sketches** that prove upgrade feasibility.
Full list remains in `spike-checklist.md` (update scorecard: A = chosen start).
## 12. Closed discussion prompts
| Prompt | Resolution |
|--------|------------|
| A vs B vs C for start | **A** ; B/C = supported upgrades |
| asset-store now? | **Later** |
| PolicyGateway owner | **Consolidate under NetKingdom** ; don’ t fork ownership |
| Chunk UX | **Outline editor + explode/implode** , not chunk tables |
| Live vs pin default | **Live** ; pin is explicit |
---
## 13. Working recommendation (frozen)
| Layer | Decision |
|-------|----------|
| Product UI | coulomb cards, spaces, outline explode/implode, dual authoring |
| Markdown structure | markitect-tool; **live** includes by default |
| Identity / deploy | NetKingdom / Railiance; authz → NK consolidation |
| Content plane | **Thin dir + git** now |
| Upgrades | → shard-wiki → kontextual (supported, markdown-preserving) |
| Assets | Beside tree now; **asset-store later** |
| Derived/stale | Live default; pin + badge → alternative → replace\|keep |
2026-08-12 18:38:27 +02:00
| markitect-main | Avoid |
---
## References
2026-08-13 00:27:44 +02:00
### coulomb-social
2026-08-12 18:38:27 +02:00
- `docs/architecture/2026-08-12-content-substrate-exploration.md`
2026-08-13 00:27:44 +02:00
- `docs/architecture/transclusion-and-chunks.md`
- `docs/architecture/authoring-modes-and-lazy-projections.md`
- `docs/architecture/stale-derived-content.md`
- `docs/architecture/spike-checklist.md`
2026-08-12 18:38:27 +02:00
- `docs/capability/stage-1-cutover.md`
2026-08-13 00:27:44 +02:00
- `docs/capability/model-v0.yaml`
- `docs/capability/existing-bubble-capability-map.md`
2026-08-12 18:38:27 +02:00
- `docs/decisions/2026-08-12-feature-cut-stage-1.md`
2026-08-13 00:27:44 +02:00
### fleet
2026-08-13 00:48:33 +02:00
- markitect-tool: transform-compose-include, explode-implode, content-references, **proposed-stale-derived-and-include-pinning.md**
- shard-wiki: INTENT (Coulomb as consumer; upgrade target)
- kontextual: blob-storage, asset-registry (upgrade + later asset-store seed)
- markitect-main: successor-gap (legacy only)