From a00a457f44ccd697d928f814f035a7cb3ca0bca2 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 8 Jun 2026 12:23:54 +0200 Subject: [PATCH] Establish clean starting point: workplan, access model, INTENT amendments Add SHARD-WP-0001 workplan backed by the StateHub yawex-requirements workstream (6 design tasks, all todo). Includes access-model blueprint, NetKingdom integration requirements draft, and INTENT amendments for open-by-default authorization delegated to external identity providers. --- INTENT.md | 16 +- docs/access-model-blueprint.md | 145 ++++++++++++++++++ docs/requirements-user-engine-netkingdom.md | 88 +++++++++++ workplans/SHARD-WP-0001-yawex-requirements.md | 120 +++++++++++++++ 4 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 docs/access-model-blueprint.md create mode 100644 docs/requirements-user-engine-netkingdom.md create mode 100644 workplans/SHARD-WP-0001-yawex-requirements.md diff --git a/INTENT.md b/INTENT.md index 5c61eb0..2941cae 100644 --- a/INTENT.md +++ b/INTENT.md @@ -27,6 +27,7 @@ It allows wiki-like systems to: * Detect and reconcile divergence between equivalent pages stored in different shards * Keep selected representations of the same wiki content synchronized where appropriate * Provide backup, versioning, and recovery for local, remote, and projected wiki content +* Run fully standalone with open read/write access and complete change history, then progressively layer multi-tenant enterprise access control through external identity integration * Allow existing wiki engines to become federation-capable through a shared API * Allow non-federation-aware systems to participate through adapters and projections @@ -94,7 +95,7 @@ This repository is **not** intended to: * Silently mutate remote systems without explicit adapter support and user intent * Act as a generic file synchronization daemon independent of wiki semantics * Guarantee real-time consistency across all shards and backends -* Solve organizational permissions, identity management, or secret storage by itself +* Implement its own authentication, identity store, user directory, or secret storage — these are **delegated to an external identity provider** (in the reference deployment, `user-engine` for accounts/memberships and `net-kingdom` for IAM/SSO). shard-wiki *does* own an **authorization model** (who may read/write/patch/merge a page or shard) and the **change history** that makes edits recoverable; it does not own authentication or identity lifecycle. * Collapse all shards into one permanently canonical source of truth Its responsibility is limited to **orchestrating wiki-shaped content across shards**, providing normalized interfaces, projections, overlays, synchronization mechanisms, Git-backed coordination, and versioned change flows. @@ -147,6 +148,15 @@ Policy decisions such as conflict preference, canonical source selection, public * **Composable integration** Wiki engines should be able to use the `shard-wiki` API to become federation-enabled without reimplementing federation internally. +* **Open by default, progressively governed** + A standalone `shard-wiki` must be runnable with zero external dependencies in a classic Ward Cunningham / c2-style open read/write-for-all mode. Access control is an *additive capability*, not a precondition: the same core progresses — without re-architecture — to authenticated single-user, to group/role-based, to multi-tenant enterprise access control, mirroring the NetKingdom capability ladder (lightweight → expanded). + +* **History as the safety net** + Because the open mode trusts all writers, every change must be captured in a complete, Git-backed history so that accidental loss, vandalism, or mistaken edits are always recoverable. Recoverability — not gatekeeping — is the baseline protection; access control hardens it but never replaces it. + +* **Authorization in core, authentication delegated** + shard-wiki owns the *authorization* model (capability-based decisions about who may read/write/patch/merge a page or shard) and expresses identity through a pluggable provider interface. It never implements its own authentication, credential store, or user directory; those are supplied by an external identity provider such as `user-engine` (accounts, memberships, profiles, audit) over a `net-kingdom` IAM backend (OIDC/PKCE in lightweight mode, Keycloak/SAML in expanded mode). + --- ## Maturity Target @@ -173,6 +183,10 @@ A mature version of this repository should: * Provide a UI for browsing, editing, projecting, comparing, and reconciling the full information space * Provide an API that allows wiki engines to become federation-enabled * Provide backup and recovery workflows for attached wiki shards +* Run standalone with zero external dependencies in an open, c2-style read/write-for-all mode +* Preserve a complete, Git-backed change history that enables recovery from accidental loss, vandalism, or mistaken edits +* Provide a pluggable authorization model that spans the full range from anonymous-open, through authenticated and group/role-based, to multi-tenant enterprise access control — without changing the core +* Integrate with `user-engine` (accounts, memberships, profiles, audit, events) and its `net-kingdom` IAM backend (OIDC/PKCE lightweight mode, Keycloak/SAML expanded mode) to deliver enterprise-grade multi-tenant access control * Serve as the default orchestration layer for federated Markdown wiki content across dependent systems A mature `shard-wiki` should make the practical mixture of repository wikis, application databases, synced directories, local Obsidian vaults, WebDAV folders, projected pages, and Git-backed content feel like a deliberate architecture rather than an accidental workaround. diff --git a/docs/access-model-blueprint.md b/docs/access-model-blueprint.md new file mode 100644 index 0000000..53c0549 --- /dev/null +++ b/docs/access-model-blueprint.md @@ -0,0 +1,145 @@ +# Architecture blueprint — shard-wiki access & history model + +Status: **draft for review** · Date: 2026-06-08 · Resolves pending decision +"Scope of a minimal access model in shard-wiki core". + +This blueprint settles how shard-wiki handles access control and change history. It +realizes the INTENT principles *Open by default, progressively governed*, *History as +the safety net*, and *Authorization in core, authentication delegated*. + +--- + +## 1. The core idea: one core, a ladder of modes + +shard-wiki ships **one** authorization core. What changes between deployments is how much +of it is switched on. No re-architecture is needed to climb the ladder — only +configuration and the presence (or absence) of an external identity provider. + +This mirrors the NetKingdom **capability-progression ladder** ("capability-driven, not +scale-driven"): start lightweight, expand when the need appears. + +| Mode | Identity provider | Who can read/write | External deps | Analogue | +|------|------------------|--------------------|---------------|----------| +| **L0 — Open (c2)** | none (everyone is `anonymous`) | everyone reads + writes | **none** | Ward Cunningham's original c2 wiki | +| **L1 — Attributed** | local/optional | open writes, but edits are attributed when an identity is presented | none | classic "sign your edits" wiki | +| **L2 — Authenticated** | user-engine (OIDC via net-kingdom lightweight) | authenticated principals; simple read-all / write-authenticated | user-engine + net-kingdom (OIDC/PKCE) | small team wiki | +| **L3 — Role/Group** | user-engine memberships | per-shard/per-namespace roles (reader/author/maintainer) | user-engine groups | departmental wiki | +| **L4 — Multi-tenant enterprise** | user-engine + net-kingdom expanded (Keycloak/SAML) | per-tenant isolation, per-page ACL, SSO/MFA, audit | full IAM stack | enterprise-grade | + +**Invariant:** L0 must always be reachable with zero external dependencies. Access control +is *additive*; removing the identity provider degrades gracefully back down the ladder, it +never bricks the wiki. + +--- + +## 2. Why history is the floor, not access control + +In L0 the wiki trusts every writer. The protection against accidental loss, vandalism, or +mistakes is therefore **not** gatekeeping but **complete, recoverable history**: + +- Every write is a Git commit on the information space's coordination layer (per INTENT's + Git-addressable coordination principle). +- Any revision of any page is restorable; deletion is a commit, never destruction. +- History exists identically in **all** modes — L4's access control *hardens* the wiki but + the recoverability guarantee is the same one that makes L0 safe to run open. + +This is the inversion of yawex, which gated with htpasswd and kept only a single `page~` +backup. We keep the openness, replace the gate with history, and make the gate optional. + +--- + +## 3. Component model + +``` + ┌─────────────────────────────────────────────┐ + │ shard-wiki │ + request ───► │ ┌────────────┐ ┌──────────────────────┐ │ + (principal?) │ │ Policy │ │ Authorization core │ │ + │ │ Enforcement├──►│ (PDP): capability │ │ + │ │ Point (PEP)│ │ decisions per │ │ + │ └─────┬───────┘ │ page / shard / tenant│ │ + │ │ └──────────┬───────────┘ │ + │ ▼ │ │ + │ ┌────────────┐ ┌──────▼───────────┐ │ + │ │ Shard │ │ IdentityProvider │ │ + │ │ adapters │ │ (interface) │ │ + │ │ (+capability│ └──────┬───────────┘ │ + │ │ profile) │ │ │ + │ └─────┬───────┘ │ │ + │ ▼ │ │ + │ Git-backed coordination journal │ │ + └──────────────────────────────────┼─────────────┘ + │ (L2+ only) + ┌────────────────────────▼───────────────────┐ + │ user-engine (accounts, memberships, │ + │ profiles, audit, events) │ + │ backed by net-kingdom IAM │ + │ (OIDC/PKCE lightweight · Keycloak/SAML exp) │ + └─────────────────────────────────────────────┘ +``` + +**Owned by shard-wiki core:** +- **Principal** — a resolved actor: `anonymous`, or an identity token + claims from the + provider. Core never stores credentials. +- **IdentityProvider interface** — a thin pluggable contract. The **null provider** + (everyone = `anonymous`) is the L0 default and ships in-core. +- **Authorization core (PDP)** — pure capability decisions: given (principal, action, + target page/shard/tenant), return allow/deny. Actions: `read, write, patch, merge, + administer`. Layered on each adapter's **capability profile** (a shard that can't write + can't be written regardless of policy). +- **Policy Enforcement Point (PEP)** — wraps every adapter operation; calls the PDP. +- **Tenant boundary** — a *root entity* is the unit of multi-tenancy. Shards attach to a + root; an L4 tenant maps to a root entity (or a set of them). +- **Change history** — Git commits on the coordination journal; the recovery substrate. + +**Delegated to the identity provider (L2+):** +- Authentication (who you are) — net-kingdom IAM (OIDC/PKCE → Keycloak/SAML). +- Identity lifecycle, user directory, credentials, secrets — net-kingdom / user-engine. +- Memberships, groups, profiles, org structure, audit sink — user-engine. + +--- + +## 4. Request flow (L4, fully governed) + +1. Caller presents an OIDC token (issued by net-kingdom IAM) to shard-wiki. +2. The configured **IdentityProvider** validates the token and resolves a **Principal** + (subject + tenant + group/role claims), enriching via **user-engine** memberships. +3. The **PEP** intercepts the requested action on a target page/shard. +4. The **PDP** decides using: tenant isolation → shard role bindings → optional per-page + ACL → adapter capability profile. +5. On allow, the adapter performs the op; the write lands as a **Git commit** and an + **audit event** is emitted to user-engine. +6. On deny, the op is refused; nothing mutates. + +**The same code path at L0:** step 1 is skipped, the null provider returns `anonymous`, +the PDP's open policy allows read+write, the write still lands as a Git commit. History is +identical; only the gate differs. + +--- + +## 5. Design rules + +- **Fail open only at L0, fail closed at L2+.** The mode is explicit configuration, never + inferred from whether the provider happens to be reachable (a flaky IAM must not silently + open an enterprise wiki). If an L2+ deployment loses its provider, it denies, it does not + fall back to open. +- **Authorization is pure and offline-capable.** Once a Principal is resolved, decisions + need no network call — role/ACL data is carried on the Principal or cached, so a federated + read of a projected page doesn't require a live IAM round-trip per page. +- **Per-page ACL is opt-in (L4).** Default scoping is per-shard / per-namespace (L3). Per-page + ACL is supported but off by default to avoid yawex's per-directory `AccessControl` sprawl. +- **Provenance carries authorization context.** A federated page records not just its source + shard and freshness but the authorization context under which it was read, so the union + view never leaks content a principal couldn't see at source. + +--- + +## 6. Open questions (carry into requirements) + +1. Token format/claims contract with net-kingdom (OIDC scopes, tenant claim name). +2. user-engine membership/role query shape and caching/TTL. +3. Audit event schema shard-wiki emits to user-engine. +4. How tenant ↔ root-entity mapping is configured and discovered. +5. Whether L1 "attributed but open" is worth shipping or we jump L0 → L2. + +See `requirements-user-engine-netkingdom.md` for the concrete asks these imply. diff --git a/docs/requirements-user-engine-netkingdom.md b/docs/requirements-user-engine-netkingdom.md new file mode 100644 index 0000000..2384429 --- /dev/null +++ b/docs/requirements-user-engine-netkingdom.md @@ -0,0 +1,88 @@ +# Requirements — what shard-wiki needs from user-engine & net-kingdom + +Status: **draft for review** · Date: 2026-06-08 + +Derived from `access-model-blueprint.md`. These are the integration contracts shard-wiki +needs to climb from L0 (open, standalone) to L4 (multi-tenant enterprise) **without +changing its core**. They are requirements *on* the NetKingdom domain repos, to be +negotiated with their owners — not assumptions about their current implementation. + +Reference deployment roles: +- **net-kingdom** — IAM/SSO backend. Issues and validates identities. OIDC/PKCE in + lightweight mode (KeyCape: Authelia + LLDAP + privacyIDEA), Keycloak/SAML in expanded + mode. Owns authentication, credentials, MFA, the NetKingdom IAM Profile. +- **user-engine** — headless user-domain service: accounts, memberships, profiles, + catalogs, projections, audit, events. Owns the *membership/role/tenant* read model + shard-wiki authorizes against. + +shard-wiki authorizes; it never authenticates or stores identity. Everything below is a +**read or emit** contract — shard-wiki must not need write access to identity data. + +--- + +## A. From net-kingdom (authentication / IAM) + +1. **Token issuance & validation (OIDC/PKCE).** A standard OIDC relationship shard-wiki can + register as a confidential or public client; ability to validate presented access/ID + tokens (JWKS endpoint or introspection). Lightweight mode must work without Keycloak. +2. **Stable subject identifier.** A durable `sub` that does not change across renames/email + changes, usable as the Principal key. +3. **Tenant claim.** A claim that identifies the tenant/organization a token is scoped to + (claim name TBD), so shard-wiki can map token → tenant → root entity. +4. **Group/role claims (optional fast path).** If groups/roles can ride in the token, L3/L4 + decisions need no extra round-trip. Otherwise shard-wiki resolves them via user-engine (B). +5. **Expanded-mode parity.** The same claim contract must hold when the backend is swapped + to Keycloak/SAML, so climbing L3→L4 is a deployment change, not a shard-wiki change. +6. **Logout / token revocation signal** so shard-wiki can drop cached Principals. + +## B. From user-engine (membership / authorization read model) + +1. **Resolve principal → memberships.** Given a `sub` (and tenant), return the principal's + group/role memberships relevant to wiki access. Read-only, low-latency, cacheable with a + stated TTL. +2. **Tenant ↔ root-entity mapping.** A way to resolve which tenant(s) a principal belongs to + and how those map to shard-wiki root entities. Either user-engine owns this mapping or it + exposes the primitives for shard-wiki to hold it as config. +3. **Role vocabulary.** An agreed, minimal role set that maps onto shard-wiki actions: + `reader → read`, `author → read+write+patch`, `maintainer → +merge+administer`. Custom + roles allowed but must declare which actions they grant. +4. **Profile lookup for attribution.** Given a `sub`, return display name / handle so edits + and signatures are attributable (the L1 "sign your edits" need, generalized). +5. **Stable, versioned read API.** A versioned contract (REST/gRPC) for B1–B4 so shard-wiki + isn't coupled to user-engine internals; INTENT requires stable adapter contracts. +6. **Bulk/batch resolution.** Resolving memberships for many principals (e.g. rendering a + BackLinks/history view) must not be N+1; provide batch lookup. + +## C. Audit / events (shard-wiki → user-engine) + +1. **Audit event sink.** shard-wiki emits access-relevant events (page read-denied, + write, patch, merge, admin change) to user-engine's audit/event stream. Need the + **event schema** and transport (user-engine already lists "audit" and "events" as owned + concerns — align to that). +2. **Non-blocking emission.** Audit emission must be async/best-effort so an audit outage + never blocks a wiki write (history in Git remains the source of truth regardless). + +## D. Cross-cutting / non-functional + +1. **Graceful degradation contract.** A defined behavior when the provider is unreachable in + L2+: shard-wiki **fails closed** (denies), and must be able to distinguish "provider down" + from "principal unauthorized" for operability. (L0/L1 never depend on the provider.) +2. **No secret custody in shard-wiki.** Client secrets / keys are provisioned and rotated by + net-kingdom; shard-wiki consumes them via the deployment's secret mechanism, never stores + or commits them. (Consistent with INTENT non-goal on secret storage.) +3. **Offline authorization.** After Principal resolution, decisions must be computable without + per-page network calls (carry claims on the Principal or cache memberships). +4. **Capability-progression alignment.** The integration should slot into NetKingdom's + C0–C6 ladder so shard-wiki's L0–L4 modes correspond to recognizable NetKingdom capability + levels rather than introducing a parallel scheme. + +--- + +## Next actions + +- [ ] Send this as a **capability request / interface negotiation** to the netkingdom domain + (user-engine + net-kingdom owners) via the state hub. +- [ ] Lock the **token claim contract** (A2–A4) and the **membership read API** (B1–B5) first + — they gate everything L2+. +- [ ] Define shard-wiki's **audit event schema** (C1) against user-engine's existing audit model. +- [ ] Resolve blueprint open questions §6 once the above are answered. diff --git a/workplans/SHARD-WP-0001-yawex-requirements.md b/workplans/SHARD-WP-0001-yawex-requirements.md new file mode 100644 index 0000000..ba92278 --- /dev/null +++ b/workplans/SHARD-WP-0001-yawex-requirements.md @@ -0,0 +1,120 @@ +--- +id: SHARD-WP-0001 +type: workplan +title: "shard-wiki requirements from yawex prior art" +domain: whynot +repo: shard-wiki +status: active +owner: tegwick +topic_slug: whynot +created: "2026-06-08" +updated: "2026-06-08" +state_hub_workstream_id: "0ed023a2-760b-4990-b931-8ee1f41ea08f" +--- + +# SHARD-WP-0001 — shard-wiki requirements from yawex prior art + +## Goal + +Turn the yawex (2004–05 Perl wiki engine) prior-art review into shard-wiki +requirements and design decisions. Produce federation-relevant specifications +that map onto `INTENT.md` without inheriting yawex's monolithic engine shape. + +## Context + +- Prior art: `history/priorart/yawex-0.7.4/` +- Requirements seed: `docs/requirements-from-yawex.md` +- Access model draft: `docs/access-model-blueprint.md` +- NetKingdom integration draft: `docs/requirements-user-engine-netkingdom.md` + +Open architectural thread: a minimal authorization model in core revises an +INTENT non-goal and needs a ratified INTENT amendment before the access model +is treated as settled. + +## Design federation page-resolution model + +```task +id: SHARD-WP-0001-T1 +status: todo +priority: high +state_hub_task_id: "ebc036e4-1c3c-4e39-959b-1e37bc1049ad" +``` + +Design shard-wiki's cross-shard name resolution fresh. Use yawex's +LOCAL/CLIMB/DROP/GLOBAL/REMOTE/SWITCH/JUMP/VIRTUAL/FAILED states as a checklist +of cases to handle (especially REMOTE for remote shards and VIRTUAL for +projections), not as the interface. Decision 2026-06-08: inspiration only. + +## Define namespace/path model and page+shard roles + +```task +id: SHARD-WP-0001-T2 +status: todo +priority: high +state_hub_task_id: "431b4d28-3768-468f-b76f-4767f5aa1888" +``` + +Union path/namespace model with relative/absolute paths and normalization (from +yawex topics-as-dirs). Map page classes local/global/virtual to shard roles +canonical/cross-cutting/projected-computed. + +## Specify union-level derived views + +```task +id: SHARD-WP-0001-T3 +status: todo +priority: medium +state_hub_task_id: "564545ec-84f7-4c3d-a779-5b9c356919f4" +``` + +Decide which derived views are core vs adapter-provided: BackLinks, +RecentChanges, AllPages, SiteMap, Search. BackLinks (link-graph over the +federated union) is the strongest core candidate. + +## Provenance and freshness model + +```task +id: SHARD-WP-0001-T4 +status: todo +priority: medium +state_hub_task_id: "738326f5-b76c-4ecf-8843-7c762c3647d8" +``` + +Generalize yawex Page::info (modtime, TODO lasteditor/hits/edits) into +INTENT's explicit provenance: source shard, freshness, cached?, has-overlay?, +diverges-from?. + +## Overlay / lightweight-patch model + +```task +id: SHARD-WP-0001-T5 +status: todo +priority: medium +state_hub_task_id: "a268de6a-d1ef-4ed9-9828-c70991e1a772" +``` + +Non-destructive edits against remote/read-only shards as drafts/patches/overlays +before mutation (INTENT: overlay-before-mutation). Seeded by yawex's append + +threaded-comment workflow. + +## Markdown link semantics + +```task +id: SHARD-WP-0001-T6 +status: todo +priority: low +state_hub_task_id: "a7499f3e-911c-455b-afc3-eefc401eb60d" +``` + +TRANSFORM yawex CamelCase/[[free]]/red-? links into a CommonMark wikilink + +red-link extension. Open question: core vs reference-UI concern. + +## Acceptance criteria + +- Each task produces a written spec or ADR-ready design note in `docs/`. +- Designs honor INTENT boundaries: mechanism over policy, union without erasure, + overlay before mutation, capability-aware adapters. +- Access-model and NetKingdom integration drafts are either ratified or explicitly + deferred with tracked open questions. +- The next implementation workplan (domain model / adapter contract) can start + without unresolved yawex-derived design gaps. \ No newline at end of file