coulomb-social/docs/architecture/ArchitectureBlueprint.md
tegwick c27a50e6d3 Revise ArchitectureBlueprint with full discussion synthesis
Fold page-first/transclusion, dual authoring, lazy projections, stale
replace-or-keep, and open content-plane decisions into the main draft.
2026-08-13 00:27:44 +02:00

307 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Architecture Blueprint — coulomb.social content & platform
| Field | Value |
|-------|--------|
| Status | **draft for discussion** (revised 2026-08-13) |
| Product | coulomb.social rebuild (`coulomb-social`) |
| Related | stage-1 cut, capability maps, substrate exploration, authoring, stale/derived |
| Non-goals | Final store vendor lock-in; full UI chrome; implementing markitect features in this doc |
## 0. Review summary (2026-08-13)
This revision folds in topics settled after the first draft:
| Topic | Stance |
|-------|--------|
| Identity / tenants / groups | NetKingdom only |
| Deploy | Railiance |
| Content SoR | Markdown + assets; DB = cache |
| markitect | **tool** as structure lib; **not** “run on markitect”; **main** = legacy |
| Page vs chunk | **Page-first**; chunks = address / transclude / promote |
| Authoring | Top-down **and** bottom-up; no forced mode |
| Title / Abstractor / Visual | Real fields; may start **lazy**; then persist; **regenerate + lock** |
| Stale derived content | Badge → generate **alternative****replace or keep** |
| Includes | Live resolve **and** (proposed) pinned snapshots with same stale grammar |
| Content plane | Still open: thin store / shard-wiki / kontextual (spikes) |
| Assets | kontextual BlobStorage now; extract asset-store when 2nd consumer |
| Live Bubble | Mapped; S1 aligns with Title/Abstractor/Visual/spaces/pages/cards |
**Still open (decisions, not re-litigate principles):** content-plane pick (A/B/C), visual storage short-term, PolicyGateway owner, chunk migration strategy.
---
## 1. Architectural aim
Deliver a co-creation product where:
1. **Identity** (users, groups, tenants) is **NetKingdom** — not reimplemented.
2. **Runtime** is **Railiance** — not reimplemented.
3. **User-owned content** is **markdown (+ assets)** exportable without the app database.
4. The **database is a cache / projection** for cards, memberships, search speed.
5. **markitect-tool** makes markdown an efficient structured substrate (lib/CLI/API) — **not** the product host.
6. **coulomb** owns co-creation UI (cards, spaces, copy/transfer, authoring UX) and product verbs.
7. Knowledge work stays **fluent**: top-down and bottom-up authoring; structure and reuse appear when needed (transclusion, lazy projections), not as mandatory a priori chunk graphs.
## 2. Non-negotiable rules
| # | Rule |
|---|------|
| R1 | Markdown (frontmatter + body + backmatter) + assets are the **exportable SoR** for content. |
| R2 | App DB holds **projections and app linkage only** for content; disposable for bodies. |
| R3 | Authn/IdP = **NetKingdom**; coulomb maps OIDC → Member; no password store. |
| R4 | Authz composes NetKingdom groups, app membership, content-plane ACLs — **no second user directory**. |
| R5 | **markitect-main** is legacy reference, not a runtime foundation. |
| R6 | **markitect-tool** is the markdown structure dependency (parse, validate, query, include, later derived slots). |
| R7 | Content plane (shard-wiki / kontextual / thin store) sits behind a **narrow PageOps** interface. |
| R8 | Stage-1 must: onboarding; spaces/pages with **Title / Abstractor / Visual**; page copy; page transfer. |
| R9 | **Pages first** — do not model documents as a priori chunk assemblies; use address/transclude/promote. |
| R10 | Derived content (projections, pinned includes, pipelines, AI) uses **stale badge → alternative → replace\|keep** and **lock**; no silent overwrite of locked/user content. |
## 3. System shape
```text
┌──────────────────────────┐
│ NetKingdom │
│ OIDC, groups, tenants │
└────────────┬─────────────┘
│ principal
┌─────────────────────────────────────────────────────────────┐
│ coulomb.social (product) │
│ • Card UI, spaces chrome, dual authoring flows │
│ • Session / Member / SpaceMembership │
│ • PageOps + projection UX (lazy / stale / lock) │
│ • Railiance deploy │
└───────────────┬─────────────────────────────┬───────────────┘
│ projections │ content ops
▼ ▼
┌───────────────┐ ┌───────────────────────────┐
│ App DB cache │ │ Content plane (adapter) │
│ (Postgres) │ │ A thin folder/git │
└───────────────┘ │ B shard-wiki (thin) │
│ C kontextual-engine │
└─────────────┬─────────────┘
│ structure
┌───────────────────────────┐
│ markitect-tool │
│ parse, FM, query, include │
│ derived/stale (proposed) │
└─────────────┬─────────────┘
┌───────────────────────────┐
│ Asset bytes (blobs) │
│ content-addressed │
└───────────────────────────┘
```
### PageOps (store-agnostic)
| Op | Meaning |
|----|---------|
| `list_pages(space)` | Card projections |
| `get_page(id)` | Full markdown + resolved visual; optional include resolve |
| `put_page(...)` | Create/update attributes + body |
| `archive_page` | Soft-delete / lifecycle |
| `copy_page` | Independent duplicate (rewrite includes as needed) |
| `transfer_page` | Re-home to another space |
| `put_visual` / `get_asset` | Binary visual/attachment |
| `project_field` / `regenerate` / `lock` | Lazy & stale projection actions |
| `derived_refresh` | Stale include/pipeline: alternative → replace\|keep (when markitect supports) |
Coulomb templates/services do **not** call Forgejo/shard/kontextual APIs ad hoc — only PageOps.
## 4. Information model (product)
| Concept | Representation |
|---------|----------------|
| **Page** | Markdown: frontmatter (title, abstractor, visual, space, projections lifecycle, …) + body + backmatter |
| **Space** | Container; may be `type: space` page or container id in frontmatter |
| **Visual** | Asset ref → blob store |
| **Card** | UI only: visual + title + abstractor + actions (+ stale badges) |
| **Region / “chunk”** | Addressable unit of a page (heading, `mkt:region`, selector) — **not** a required storage type |
| **Transclusion** | Live `mkt:include` and/or pinned `mkt:derived` snapshot (proposed) |
| **Member / tenant** | NetKingdom + app session |
### 4.1 Page-first vs Bubble chunks
Live Bubble has `custom.chunk` graphs. Rebuild stance:
```text
Write fluently in a page
→ address regions when needed
→ transclude / extract / promote
→ materialize a new page only when reuse earns independence
```
See `transclusion-and-chunks.md`. Migration may flatten Bubble chunks into sections/regions/pages+includes.
### 4.2 Dual authoring
| Mode | Flow |
|------|------|
| **Top-down** | Title → abstractor → outline (headings) → elaborate body → visual |
| **Bottom-up** | Body first; title/abstractor/visual/structure later |
No mandatory mode toggle. Empty body with metadata is valid; body-only is valid.
### 4.3 Lazy projections + lock + stale alternatives
For **title**, **abstractor**, **visual** (and similar display fields):
1. Missing on display need → **generate** (lazy) → **persist** as normal field values.
2. **Regenerate** and **lock** are per-field.
3. If inputs changed → **May be stale****Generate updated version** → compare Current \| Alternative → **Keep** or **Use alternative**.
Detail: `authoring-modes-and-lazy-projections.md`, `stale-derived-content.md`.
Same grammar for **pinned includes** and **pipeline/AI/rule** blocks (markitect proposal: `mkt:derived`).
## 5. Content-plane options (comparison)
### 5.1 Option A — Thin folder/git store + markitect-tool
| | |
|--|--|
| **Idea** | `.md` tree + assets; markitect validates; app indexes cards |
| **Pros** | Fastest S1; trivial export; low structure tax |
| **Cons** | Copy/transfer/versioning are app work; federation later costs more |
| **When** | Velocity first; plane maturity unclear |
### 5.2 Option B — shard-wiki (thin slice)
| | |
|--|--|
| **Idea** | Headless wiki orchestration; IdP external; coulomb = UI |
| **Pros** | Page union, git coordination, multi-shard later; INTENT names Coulomb |
| **Cons** | Structure tax if federation leaks into product; multi-tenant ops still maturing |
| **Thin-slice rule** | One InformationSpace / instance; one primary shard; hide federation from cards |
### 5.3 Option C — kontextual-engine
| | |
|--|--|
| **Idea** | Knowledge ops: asset identity, representations, blobs, audit, workflows; markdown via markitect adapter |
| **Pros** | Blobs ready; lineage; multi-format future; PolicyGateway port |
| **Cons** | Heavier; NetKingdom not wired; must enforce markdown export as happy path |
| **Auth gap** | Map OIDC → Actor; PolicyGateway → flex-auth |
### 5.4 Evolution (not exclusive forever)
```text
PageOps → A or thin B/C now
→ folder/git shard can become shard-wiki
→ assets can move to kontextual / asset-store
→ markitect-tool always structure layer
```
**Working shortlist (unchanged):** spike **A** as control + **B thin** and **C** in parallel; pick after scorecard (`spike-checklist.md`).
## 6. Asset management
| Source | Role |
|--------|------|
| kontextual BlobStorage | Prefer for Visual/bytes now (fs/S3/memory, content-addressed) |
| markitect-main assets | Ideas only — do not runtime-depend |
| Extract `asset-store` | When **second** consumer needs same API; seed from kontextual port |
Visual field stores a **ref** (path or `sha256:…`); bytes never live only in Postgres.
## 7. Identity & access control
```text
Browser → NetKingdom OIDC → coulomb session
├─ product authz: tenant_id, SpaceMembership
└─ content plane: Actor(external_ref=iss|sub, groups=…)
PolicyGateway → flex-auth (preferred)
```
Fail closed on ambiguous access. Spike: OIDC → Actor → cross-tenant deny.
## 8. Deploy sketch (Railiance)
| Component | Deploy |
|-----------|--------|
| coulomb-social | Existing chart; `app.coulomb.social` |
| Content plane | In-process, sidecar, or platform service |
| Asset bytes | PVC and/or S3; secrets via env-secret pattern |
| NetKingdom | Existing KeyCape / Authelia |
Apex DNS stays Bubble until stage-1 cut + transfer readiness.
## 9. Stage-1 product scope (capability cut)
**Must for productive transfer** (Bubble → reference-only):
- Member onboarding (NetKingdom; public reg may lag via provisioned users)
- Spaces + pages with **Title, Abstractor, Visual** + markdown body
- Page **copy** and **transfer**
- Card presentation
- Dual authoring + lazy projections (at least title/abstractor); stale replace|keep preferred early
**Later:** social, ventures/invest, challenges marketplace, monetization, full chunk-stage machine, multi-shard personal vaults.
Live Bubble map: `docs/capability/existing-bubble-capability-map.md`.
## 10. ADR roadmap
| ADR | Topic |
|-----|--------|
| ADR-0001 | NetKingdom identity (**done**) |
| ADR-0002 | Forgejo binding — **demote** to one adapter/export shape |
| **ADR-0003** | Page-centric model + SoR + DB cache + page-first/transclusion |
| **ADR-0004** | Content-plane choice + PageOps |
| **ADR-0005** | Asset-store extraction (if/when) |
| **ADR-0006** | Authoring modes + projections + stale/derived (or fold into 0003) |
Markitect-tool: implement `proposed-stale-derived-and-include-pinning.md` as its own work.
## 11. Spike checklist
See **[spike-checklist.md](./spike-checklist.md)**.
Add when scheduling: dual-authoring smoke, stale projection compare UX, include pin (after markitect).
## 12. Discussion prompts (remaining)
1. Content plane for next 36 months: **A velocity**, **B federation path**, or **C governance/blobs**?
2. Visual: kontextual/local blob first, or block on asset-store extract?
3. PolicyGateway production owner?
4. Bubble **chunk** migration: sections/regions only, or keep a chunk list UX as a *view*?
5. Default include mode in product: **live** vs **pinned** for shared excerpts?
## 13. Working recommendation (principles frozen; plane open)
| Layer | Recommendation |
|-------|----------------|
| Product UI | coulomb cards + spaces + dual authoring |
| Markdown structure | markitect-tool (includes + future derived slots) |
| Identity / deploy | NetKingdom / Railiance |
| Content plane | Spike A+B+C; choose with export fidelity as hard constraint |
| SoR | Markdown export always works; engines add IDs/lineage without trapping content |
| Assets | kontextual blobs now; extract later if shared |
| Chunks | Transclusion/extract, not a priori graph |
| Derived/stale | Badge → alternative → replace\|keep + lock |
| markitect-main | Avoid |
---
## References
### coulomb-social
- `docs/architecture/2026-08-12-content-substrate-exploration.md`
- `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`
- `docs/capability/stage-1-cutover.md`
- `docs/capability/model-v0.yaml`
- `docs/capability/existing-bubble-capability-map.md`
- `docs/decisions/2026-08-12-feature-cut-stage-1.md`
### fleet
- kontextual: architecture-blueprint, asset-registry, blob-storage, markitect reuse boundary
- shard-wiki: INTENT, SCOPE
- markitect-tool: transform-compose-include, content-references, cache-incremental, **proposed-stale-derived-and-include-pinning.md**
- markitect-main: successor-gap-assessment (legacy)