Fold page-first/transclusion, dual authoring, lazy projections, stale replace-or-keep, and open content-plane decisions into the main draft.
15 KiB
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:
- Identity (users, groups, tenants) is NetKingdom — not reimplemented.
- Runtime is Railiance — not reimplemented.
- User-owned content is markdown (+ assets) exportable without the app database.
- The database is a cache / projection for cards, memberships, search speed.
- markitect-tool makes markdown an efficient structured substrate (lib/CLI/API) — not the product host.
- coulomb owns co-creation UI (cards, spaces, copy/transfer, authoring UX) and product verbs.
- 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
┌──────────────────────────┐
│ 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:
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):
- Missing on display need → generate (lazy) → persist as normal field values.
- Regenerate and lock are per-field.
- 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)
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
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.
Add when scheduling: dual-authoring smoke, stale projection compare UX, include pin (after markitect).
12. Discussion prompts (remaining)
- Content plane for next 3–6 months: A velocity, B federation path, or C governance/blobs?
- Visual: kontextual/local blob first, or block on asset-store extract?
- PolicyGateway production owner?
- Bubble chunk migration: sections/regions only, or keep a chunk list UX as a view?
- 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.mddocs/architecture/transclusion-and-chunks.mddocs/architecture/authoring-modes-and-lazy-projections.mddocs/architecture/stale-derived-content.mddocs/architecture/spike-checklist.mddocs/capability/stage-1-cutover.mddocs/capability/model-v0.yamldocs/capability/existing-bubble-capability-map.mddocs/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)