Governed operator lists (organizations, grants, people, billing×4) gain
server-side search, status filters, and 50-row pages with true totals
from count(*) OVER(); state is URL-addressable, out-of-range pages
clamp,
and no-match is distinct from true-empty.
People is the eighth flat sidebar entry: /operator/persons lists persons
newest-joined first (excluding the reserved system person), rows linking
to the existing detail.
Billing gains an operator invoice detail at
/operator/billing/invoices/{invoiceID} reusing the member projection;
open invoices past due present as Overdue (derived, filterable, stored
status untouched); all four views lead with the linked organization and
mute object IDs.
Grants filter over the derived Live/Superseded/Inactive state, the SQL
HAVING predicate pinned to the Go derivation by test. Embedded lists
(org composite ledger, Tier changes) adopt the shared controls under
namespaced params with sibling-state-preserving URLs and scoped htmx
swaps that hold the viewport.
Review corrections: blocked ladder Delete renders disabled with tooltip
and mutations fire toasts; collapse triggers paint their open state;
sections use outside headings; plan topology drops the orphan-product
check; domains policy collapses behind a disclosure.
33 KiB
Milestones
Mirrors Gitea milestones — title, description, status. Detailed phase breakdowns for completed work live in openspec/changes/archive/ (per-change artifacts) and archive/milestones-completed.md.
Completed
| # | Name | Description |
|---|---|---|
| 1 | Governance spine | Identity + organization. Person → org → workspace with auto-provisioning. |
| 2 | Grant-based FedWiki access | Entitlements + minimal billing. Admin grants → numeric site limits. |
| 3 | UI/UX alignment | Progressive disclosure, operator tools, validation, entitlement/product management. |
| 4 | Stripe Integration | Stripe as billing execution engine: schema, webhook pipeline, customer/product/subscription/invoice sync, operator UI. |
| 5 | Public Tier | Free FedWiki entry tier — signup → personal org → default-entitlement site (1 site, 16 MB); members see entitlements & usage; public products discoverable. |
| 6 | Plan Management Foundation | First-class plan ladders + supersession (end-and-re-apply, no dormant), the generic Transition primitive, pool_provision_transitions audit, and operator ladder / auto-provisioning / enrollment UIs. |
| 7 | Operator UX Foundation | Operator panel reworked SPA→MPA: information architecture + route hierarchy, design system (WCAG 2.1 AA), form/action conventions, and a member-console lint dead-reference / convention guard. |
| 8 | Standard Service | Public→Standard (Stripe Checkout upgrade) and Standard→Public (cancel downgrade, clamp policy); member plan catalog, ladder-aware switch mechanics (paid→paid / paid→free), operator plan-topology view. |
| 9 | Integration Architecture | Integrations as registered trees behind an explicit contract: registry-driven routes/workers/config/UI/migrations, per-stream schemas and goose ledgers, Discourse as the proving second integration (live commerce loop: checkout → entitlement → forum group), generic member-dashboard cards, runtime-managed integration settings. |
Per-phase detail for M5–M9 is in archive/milestones-completed.md.
Active
Milestone 10 — Open-Source / Public Launch Readiness
Serves: open-source launch (primary) + member-console features; 10d also enables the FedWiki Service Standard tier.
The "make it presentable" milestone: take the feature-complete engine (M1–M9) and make it safe and unconfusing for a stranger's first contact, so the member console can be published as an open-source project with its own website. The substance is built; this milestone is the front door, the first-run experience, and the one or two feature gaps that make the operator happy-path actually completable. Most items are already enumerated in status/issues.md; this milestone organizes them into a launch goal.
| Phase | Status | Scope |
|---|---|---|
| 10a Project front door | Done | README (what/why, quickstart, architecture, honest "pre-production" framing), CONTRIBUTING.md, SECURITY.md, LICENSE, and a docs reorg-by-audience all shipped. Operator-dashboard screenshot captured 2026-07-27 (docs/images/operator-overview.png, wired into the README) from mechanically-honest generated sample data — reversible seed/nuke pair at test/mockshot-seed.sql / test/mockshot-nuke.sql. |
| 10b First-run friction | Done | Shipped + archived via boot-config-validation (fail-fast config validation), temporal-first-boot-retry (compose health-ordering + client-side connect retry — the race was Temporal vs. its own DB, not just JWKS), keycloak-dedicated-test-realm (app OIDC moved off Keycloak's master admin realm onto a dedicated app realm (now named test) — account-console error fixed, live-verified), test-stack-hygiene (test-only secret labeling; untracked the unused realm export), and member-console-init-scaffold (init writes a usable starter config). The "looks broken on first run" cluster. |
| 10c Purchasability completion | Done | Shipped + archived 2026-07-03 (four changes, specs folded): purchasability-sync-completion (truthful Stripe-configured gate, failed/dead-letter state + retry, live-poll readiness card, "Payment processing" label), multi-price-support (default-price model, per-row sync/make-default/deactivate, price-aware product-idempotent sync), ladder-tier-append, and db-error-translation. Residual: runtime browser verification of the multi-price make-default→sync-without-duplicate path and the ladder-append flows is left for maintainer confirmation (stack running); behavior is unit/render-tested and the schema is confirmed live. |
| 10d Custom domain support | Code done, ops pending | Shipped 2026-07-24/25 in three slices (exploration): Slice 1 custom-domain-verification (entitlement gate, TXT challenge, verification UX — 4924784); Slice 2 domains-registry (8d05934) — the core internal/domains module owning disjoint claims + generic placements, verify-once-place-freely, /domains/ask moved to core with the domains-ask-fallback-url strangler seam, reserve-then-bind create saga, boot reconciliation/adoption, domains.name_rules; Slice 2.5 lifecycle hardening (c85ac6a, c4bb1ba) — 24h pending window (was 7d), abandonment ledger with sticky evidence latch, per-workspace initiation budget, system-cancel exemption, expiry sweep on boot + 15m Temporal schedule, carve guards bound to every caller. Live-verified end-to-end against real DNS. Remaining: Slice 3 (ops, not code) — deploy to prod, seed the registry from the census, cut the ask label over via the fallback seam, retire mkdir-as-domain-management, settle grandfathering over quota. Two follow-ups filed in issues.md (no tombstone on released hosted names; subtree exclusivity ignores DNS delegation for BYO domains). Design notes: model-card-notes-domains.md. Original scope — custom domains as an entitlement-gated capability: a boolean "allow custom domains" rule on a plan's entitlement set gates the affordance, plus the DNS + SSL story that makes it demo-able end-to-end. Folded out of the metering milestone — credible co-op hosting needs members' own domains at launch, and the FedWiki Service Standard tier lists it (spec). Scoped 2026-07-03: the checkbox branch is already wired end-to-end (handler → Temporal → farmmanager → fedwiki.sites.is_custom_domain) but wholly ungated, and entitlement_set_rules already permits rule_type='boolean' — the gap is the materializer (skips non-limit rules), a materialized boolean store + read query, the rule-authoring form (hardcodes limit), and the create-site gate. Resolves audit finding #5 (the dead-end checkbox — hidden by 10h until this lands). |
| 10e Hardening & polish | Done | Shipped 2026-07-31 (change hardening-polish): container runs as non-root (uid 65532, no runtime writes); styled 404/500 pages in the page chrome, wired at the router catch-all (which previously served the dashboard for any unknown path) and the panic-recovery middleware, with HTMX partials keeping text errors for the toast contract; operator/member heading hierarchy re-leveled across 29 templates (every page a real H1→H2→H3 outline, zero h5/h6 tags, visuals pinned via Bootstrap .hN classes, older pages gain the newer pages' visible H1 title); autocomplete on all forms + text inputs (Chrome flags cleared); cosmetics sweep (revoke-confirm empty-name fallback, truthful full-ladder tiers alert, catalog feature labels resolve resource_keys.display_name via the entitlement view's resolver); test-stack Keycloak client secrets re-valued to self-labeling TEST-ONLY- strings (extends 10b's test-stack-hygiene); /login made idempotent (unconsumed state reused <5 min — closes the recurring state-mismatch class structurally); extractRoles merges all four claim locations incl. client-scoped resource_access (union, deduped); CSRF-secret rotation procedure documented in docs/hosting.md. Also fixed en route: nil-logger SIGSEGV in logging.FromContext pre-initialization. Verified: build/vet, unit+render tests, full DB-backed suite, live-stack browser walkthrough (fresh-seeded Keycloak, operator login, heading outlines, styled 404, catalog labels). 13 issues closed to archive; trimmed scopes filed as follow-ups (IdP-agnostic role mapping, dual-key CSRF rotation). |
| 10f Presentable operator dashboard | Done | Shipped 2026-07-25 (56a743f queries, 4cec386 landing; change operator-overview-landing): /operator rebuilt as a four-region overview — hero lookup, at-a-glance stat tiles (sqlc-backed, per-source em-dash degradation, provisions-not-ledger delivering count), unified activity timeline, registry-driven System rail with outbox pending/retrying/dead-letter. Shell brand-neutralized. Built via a two-model experiment (Opus vs Kimi K3 in parallel worktrees; Opus merged, 10f-kimi branch kept as reference). Post-merge polish shipped 2026-07-26 (change operator-overview-polish): domain-claims tile swapped for open invoices (receivables count — domains de-centered from the IA, headline row reserved for core capability state per the spec delta), shadow/hover card styling replaced with the app's plain bordered-card idiom, and the tile's dead plumbing removed (operator DomainsQ, CountClaimsByLifecycle). Metric refinement shipped 2026-07-27 (change overview-money-and-teams): Organizations → Team organizations (non-personal, non-reserved — personal orgs track the people count by convention and read as an error), Subscriptions → Monthly recurring (monthly-normalized money, largest currency bucket, subscription counts in the caption; FX conversion filed in issues.md), captions reworked em-dash-free (People 30-day trend, invoices outstanding balance), and the Delivering grants headline narrowed to deliberately issued grants with the signup-default mass in the caption (the actionable set — conferral mints a default per org). Live-verified on the test stack. |
| 10g First-contact UX pass | In progress | Re-walk the whole app from a stranger's seat: progressive disclosure and learn-by-clicking IA so a newcomer can orient without a guide. Prior UX milestones (M3, M7) polished piecemeal; this one takes the newcomer's perspective as the explicit goal. The 2026-07-02 adversarial audit partly pre-empts this — 10h remediates its findings; 10g re-walks after. Evidence phase DONE 2026-08-22 per the new first-contact-ux-process.md (v1): 5 naive walkthroughs + 2 outside models (Kimi K3, DeepSeek) + 48-screen dual-state sweep + 8 model-card honesty audits + 3 independent heuristic reviews + code verification of the headline mysteries (3 of 4 were environment artifacts). Synthesis: ux-walks-2026-08/findings-2026-08.md, 19 findings UX-1…UX-19, four sev-4 (no first-boot door; phantom entitlement-set selection on product edit; grant delivery-state told three contradictory ways; operator lists unusable at scale). Maintainer triage 2026-08-22: fix everything inside M10; realm branding reframed to a configurable deployment name; slugs-eradication issue filed. Five changes planned, in order: ux-honest-surfaces DONE + archived 2026-08-23 (see findings' Change 1 status: 7 findings resolved in full, 5 partial, honest surfaces across both panels) → ux-first-run DONE 2026-08-23 (implemented + verified, uncommitted; see findings' Change 2 status: state-derived setup checklist at /operator/setup + landing region, blocked-vs-empty overhaul on the eight bare screens, point-of-blockage disclosures, topology nothing-to-validate state, billing Stripe-unconfigured branch, production-deployment guide + environment reference + README first-run order; full suite + e2e green, empty-DB browser walk end to end) → ux-ia-naming DONE + archived 2026-08-23 (implemented + verified through two maintainer review rounds folded in before the final re-archive, specs synced, uncommitted; see findings' Change 3 status: sidebar flattened to seven entries with sections navigating themselves in-page — the children mechanism, landing order, and inline IdP copy were REVERSED/revised in that review — map-first Plan Ladders + topology URL retired (404, no redirect), Enrollment retired for the org's own name, ui-vocabulary/form-conventions/chrome-conventions contracts applied with zero banned-term residue (workspace itself now banned from reachable UI per maintainer reversal; holdings attribute to the organization), member catalog rebuilt as Plans + a conditional non-plan section ("More products"/"Products"), member domains anchored, operator domains ledger/placements; full suite + e2e green after real walkthrough-suite fixes, all test-side) → ux-operator-scale DONE 2026-08-23 (implemented + verified, uncommitted, awaiting archive; see findings' Change 4 status: the operator-list-scale contract — search/filter pills/50-row pages with true totals, URL-addressable, on organizations, grants, people, billing×4; People directory as the eighth sidebar entry reversing the lookup-only decision; billing rows lead with the linked organization, IDs muted, new per-invoice detail; derived Overdue with a filter answering "who is behind on payments?"; grants filter proves SQL-vs-Go derivation agreement; 5 executor lanes, suites + 3 new e2e walkthroughs + live pass green; evening review round same day folded in — system person excluded from the directory, off-ladder products no longer "structural issues", flex min-width overflow fix, ladder delete disabled+tooltip with toast feedback, domains policy + composite forms progressively disclosed, integrations header tooltips, composite lists capped at 16, and Billing populated via a REAL Stripe test-mode checkout end to end; ARCHIVED 2026-08-24 after four further maintainer rounds on the organization composite, specs synced (73 items validate): a real defect fixed where extension resolved the pool's "first grant-backed provision" instead of the tier the button named, then a deliberated redesign (options weighed with three outside models before any code, per the maintainer's request) — the pool card groups by delivery so a shared product renders once with one Extend control and its ladder placements as rung pills; one heading rule (every section titled outside a box, every section boxed, card titles only name instances, disclosure actions inside the box they open into); the grants ledger split into Active/History tabs with search and paging on both; "Position history" renamed "Tier changes" reading in verbs and tier names instead of integer ranks; embedded lists defaulting to 10 rows with a size picker; and embedded controls doing scoped HTMX panel swaps so using them never scrolls the page while the URL still carries every list's state) → drop-entity-slugs. Acceptance = fresh walkers re-run the missions; sev-3/4 friction must not recur. |
| 10h Audit remediation | Done | Execute the triaged 2026-07-02 audit findings (triage): 43 quick-fixes, 4 de-advertisements (trial days, Replace stacking, both dead Active toggles — stop implying half-cooked affordances exist), the interim hide of the custom-domain checkbox (real remedy is 10d), and 7 interim guards for structural-defer findings. Blockers first (25). The 7 structural real fixes are NOT in scope — they gate on 10i model cards. Verified via build/vet/lint + full DB-backed suite + operator-walkthrough browser e2e against the live stack; two adjacent defects fixed en route (timeout-middleware process crash, test-harness migration numbering — see issues.md). Committed 2026-07-03 (8514df5 remediation, fa420ee org-default fix); same-day follow-up: the ladder Active toggle went from a "not yet enforced" badge to fully hidden, and the 11 postponed items are inventoried as deferred-remediation debt in issues.md. |
| 10i Model cards | Done | Write the model catalog (inventory): one canonical card per domain model in docs/models/, indexed by a docs/models/README.md catalog page linked from the docs front door, in the recommended order — product-kind, payments/billing, plan-transitions, entitlements, resource-pools, provider/integration, identity/workspace. Each card pins purpose/invariants/dimensions/edge-cases/open-questions, citing design/ + membcons-db decision docs. Cards are descriptive (code-as-built vs design) and are written in this repo; when a card surfaces an open question that needs a new normative decision (e.g. the addon structural signal, payments dimensions), that decision is design work done in membcons-db — the card then updates to cite the resulting decision doc (the doc-35 pattern). Unblocks the 7 deferred structural fixes and de-risks M13. Done 2026-08-21 (model-cards change): eight cards (domains-registry added; "product-kind" renamed product-catalog after Doc 41) + index in docs/models/, each code-verified by adversarial fact-check (~53–89 claims/card) and cold-read newcomer tests; per-card drift ledgered under "Model documentation" in issues.md; the run also produced the Doc-39 conformance check (doc39-conformance-2026-08-21.md) that ratified upstream Decisions 129–133, closing the design record through Decision 141 (data-model v16 synced). Verification surfaced new launch-relevant bugs, triaged into M10 — see the M10 addendum below and the ledgers. |
| 10j Verification-found hardening | Done | All three changes shipped 2026-08-22; phase closed. Fix what the 10i verification passes surfaced, sequenced before 10g so the first-contact re-walk isn't spent rediscovering known defects (the same ordering that put 10h before 10g). Triaged 2026-08-22 after a per-issue deliberate-vs-oversight audit: nine of the eleven Tier 2 gaps are confirmed oversights, two (rank-0 signup fragility, env enum validation) are recorded deferrals whose follow-on was already named — none is a blessed design position. All detail citations live in the issues.md model-documentation ledgers. Three small changes matching the triage tiers: (1) purchase-path-blockers — DONE 2026-08-22 (implemented + verified, uncommitted; archive with the landing commit): member catalog queries filter lifecycle_status = 'published' and checkout re-checks the shared member gate before any Stripe call, with a current-rung carve-out so an org enrolled on a now-unpublished tier keeps seeing its plan; the promised core.webhook_events partition creator now exists (internal/db/partitions.go, boot pass + 24h webhook-partition-ensure Temporal schedule, current + 3 months ahead); the Stripe receiver answers 500 when the event insert fails so Stripe redelivers (Discourse's receiver already did). Verified: full DB suite green, live walkthrough (draft hidden from /products, direct checkout POST 400s pre-Stripe, publish flip appears; boot created the month-ahead partition and registered the schedule). Cards updated (product-catalog inv 11; payments-billing inv 12/14 + map); three ledger entries resolved. (2) schema-hardening — DONE 2026-08-22 (implemented + verified, uncommitted; archive with the landing commit; maintainer arbitrations 2026-08-22 folded in): migration 00010 carries the schema seatbelts — one-default-pool-per-org and one-primary-per-workspace partial unique indexes (with a dedup pre-flight), pool_type CHECK, core.providers status CHECK + suspended_at/retired_at (doc-44 convergence), core.subscriptions.status CHECK over Stripe's full eight-value vocabulary (design-feedback note filed: the designed list lacks incomplete_expired). App guards: workspace creation via a shared transactional provisioning routine (no more false success); ExtendGrant multi-pool refusal + org/default pool ownership check; rank-0 both guards (last-tier delete refused on live org-type defaults; plan-less signup fallback with an error alarm); Slug()/manifest boot assertion; ValidateStart enum enforcement for env-sourced config shared with the override validator; checked int64→int32 conversions in the invoice projection (plus an executor-found live bug fixed: ProcessWebhookEvent's error wrapping silently made every non-retryable Temporal error retryable); domains cancel runs a strict fresh evidence probe with retries before stamping abandonment; stacking authoring restricted to additive-only (selector removed; homogeneity/tie-breaker work replaced by the "Stacking policies need a design exploration" issue). Verified: full DB suite + e2e walkthroughs green, migration applied live, all three constraints psql-rejected live, rule form checked in the browser; 12 ledger entries resolved/updated, 2 filed, 7 model cards updated. (3) model-doc-rot — DONE 2026-08-22 (uncommitted; archive with the landing commit): plan-architecture.md and grant-plan-safety.md retired rather than rewritten — both were model description now owned by the plan-ladders card, and the retire-don't-duplicate rule is written into the model-catalog spec; the 2026-04 incident narrative preserved at status/archive/incident-2026-04-grant-provision-sync.md; plan-management.md corrected in place (conferral boundary, real audit vocabulary, restore-records-initiate); identity-provider-setup.md role extraction now documents the four-location union; stripe.md sync-status claim fixed; dead code deleted (EnsureStripeCustomer function with its payload struct kept for the outbox consumer; two unused placement queries); the reconcileSite and 00012 comments corrected; the Makefile sqlc-generate target now loops the nine real configs — and its first honest run surfaced + folded in the internal/integration regeneration that migration 00010 had left behind. Nine ledger entries resolved. Explicitly deferred beyond M10, staying in the ledgers: rule-change re-materialization fan-out (design work), domains schema constraints and identity status vocabularies (upstream decisions), the ask-200 ambiguity (retires with 10d Slice 3). |
Remaining for M10 (what's missing): Done → 10a, 10b, 10c, 10e, 10f, 10h, 10i (2026-08-21). Code done, ops pending → 10d (all three code slices shipped; only Slice 3, the adoption/ops cutover, is left). 10j Done (2026-08-22) — all three changes shipped and archived (purchase-path-blockers, schema-hardening, model-doc-rot; see the 10j row). In progress → 10g, now the sole code phase left: evidence + triage done, changes 1 (ux-honest-surfaces), 2 (ux-first-run), and 3 (ux-ia-naming, including the maintainer's correction rounds) shipped and archived 2026-08-23, change 4 (ux-operator-scale) shipped 2026-08-23 and archived 2026-08-24 after its composite-redesign rounds; only change 5 drop-entity-slugs remains, then the acceptance re-walk. Carried-forward debts, tracked in issues.md: the 10c residual (maintainer live-verify of multi-price/ladder flows) and the 10h deferred-remediation items (4 de-advertisements; the 7 interim guards' card gates settled by 10i). The headline 10j item, maintainer-triaged 2026-08-21: the draft-product checkout bug — the member path never checks lifecycle_status, so a draft that is active+public+priced+mapped can be bought, with only conferral rejecting it after payment (found by the 10i model-card verification pass; full citations under "Product-catalog card: drift & trap ledger" in issues.md).
MVP Checkpoint: A newcomer can read the README and understand what the member console is in under a minute, run the stack from a documented quickstart without hitting a boot failure that looks like a bug, land on an operator dashboard worth showing off and find their way around it without a guide, complete the operator create-product → price → sync → sellable path end-to-end, and (Standard tier) attach a custom domain. The repo carries the standard OSS hygiene files, the audit's 25 publish-blockers are resolved or mitigated (10h), and every visible affordance does what it says — half-cooked features are hidden, not hinted. At that point it is presentable to the open-source community.
Proposed
Renumbered 2026-07-03 so milestone numbers match execution order: Launch Readiness 12→10 (phases 12a–12i→10a–10i), Audit Log 10→11, REST API 11→12; M13 unchanged. Documents under
archive/andlog/, and M7-era walkthrough evidence underdocs/, are historical records and keep the old numbers. The sections below are in expected take-up order: M11 (audit), M12 (API), then M13 (metering), as capacity and need dictate. Each milestone's Serves: line names its driving roadmap(s) — see the four-roadmaps section in strategy.md.
Milestone 11 — Audit Log & Observability
Serves: member-console features + design affordances (the audit module in design/).
"Who did what when, and how is the system behaving while they do it." Delivers the generic audit.log that M6a's pool_provision_transitions was designed as a view-shaped projection of; supersedes the Backlog "Audit trail" and absorbs the M7-7a action-telemetry deferral.
| Phase | Status | Scope |
|---|---|---|
| 11a Audit-log schema | Proposed | Generic table vs typed projections vs union view — the load-bearing decision; record shape, retention, PII checklist. |
| 11b Auto-instrumentation middleware | Proposed | One audit row per mutating route (actor, target, status, request id). |
| 11c Observability stack | Proposed | Metrics surface (latency, error rate, queue depth) + on-call dashboards; stack TBD. |
| 11d Operator audit UI | Proposed | Per-resource history views, built on the M7 design system (generalizes M6e). |
| 11e Retention, PII, alerting | Proposed | Per-category retention, payload PII scrub, signal alerting. |
Milestone 12 — Public REST API
Serves: member-console features.
A stable, versioned /api/v1/* surface for every privileged mutation, independent of the operator HTMX UI (which stays as-is). For external automation, CLI tooling, the demo seeder, and future integrations. An API is a permanent contract — auth model, error envelope, versioning, and idempotency are picked deliberately in 12a before the surface expands.
| Phase | Status | Scope |
|---|---|---|
| 12a API design & auth model | Proposed | REST + OpenAPI; token vs OAuth client-credentials; error envelope (likely RFC 7807); pagination / idempotency; ship /api/v1/health only to validate. |
| 12b Catalog endpoints | Proposed | Org types, products (+ Stripe mapping), entitlement sets (+ rules), plan ladders (+ tier-rank reorder). |
| 12c Runtime endpoints | Proposed | Persons, organizations, grants (both paths), per-org composite. |
| 12d Billing & integrations | Proposed | Read-only over Stripe mirror state; integration endpoints (FedWiki; shape informed by M9). |
| 12e OpenAPI publication | Proposed | docs/api/openapi.yaml in CI; first generated Go client used by the demo seeder. |
| 12f API audit, rate limits, retention | Proposed | Integrate M11 audit; per-principal rate limits; CSP/CORS lockdown. |
Relationship to M9: M9 is the contract for an integration plug-in; M12 is the contract for a programmatic caller. Different audiences — not conflated.
Milestone 13 — Usage Metering & Time-Tracked Services
Serves: member-console features + design affordances. Explicitly NOT required for the FedWiki Service launch — the service thread's latest word (post #16, 2025-11-23) proposes dropping bandwidth limits entirely, since no metering mechanism exists and raw VPS capacity dwarfs the stated limits.
Usage ingest + enforcement + billing over the M9-namespaced resource keys, plus time-tracked/hourly services. Measurement itself (bytes on the wire, disk consumed) is provider/infra scope — member-console cannot observe it and only ingests reported usage. Absorbs M8c's storage-compliance split-out: storage becomes a metered resource alongside network egress (storage_bytes exists in the model but is unmetered/unenforced today). Down the road — not near-term. (Custom-domain support moved to M10.)
| Phase | Status | Scope |
|---|---|---|
| 13a Usage ingest & alerts | Planned | Ingest provider/infra-reported usage per site/org; expose usage; threshold alerts. |
| 13b Limit enforcement | Planned | Block/throttle over-limit; configurable grace periods (per plan/policy). |
| 13c Time-tracked / hourly service billing | Planned | Time-tracking; hourly billing; add-on management UI. |
| 13d Usage-based billing | Planned | Metered subscriptions; pending charge → InvoiceItem overage sweep. |
Open (deferred): whether a metered resource can be scoped to an arbitrary subset of providers (e.g. storage pooled across {FedWiki, NextCloud} but not Discourse) rather than one-or-all. Needs a grouping construct, not a column tweak — M13 either accommodates subset scoping or ships a flat scalar for v1. See issues → "Pooled metered resources across arbitrary provider sets."
Backlog
Capabilities we want but don't yet fit into a milestone. Items graduate to milestones when triggered by need or capacity.
| Capability | Why We Want It | Current Thinking |
|---|---|---|
| FedWiki↔Discourse full parity | M9 demoted FedWiki onto the integration registry; a 2026-07-31 audit inventoried the residue. | Delivered 2026-08-01 in two slices (issue archived to archive/issues-resolved.md): integration-config-parity (core domains-connect-target key, typed ConfigSpec, generated init scaffold) and test-stack-integration-profiles (fedwiki chain + Caddy profile-gated, COMPOSE_PROFILES composition knob defaulting to core-only, presence guards across seed/teardown/walkthroughs). Still distinct and open: fedwiki's direct internal/domains import (parked interdependence exploration). |
| Forum integration | Public tier has "community-only" support (read, no post); Standard tier unlocks posting rights. | Discourse SSO + group sync based on membership status. |
| Backup self-service | Members can restore from backup without operator intervention. | Integration with backup system; point-in-time restore UI. |
| Site replication | Standard tier can host replicas of external sites for higher availability. | FedWiki farm configuration; origin→mirror sync workflow. |
| Voting rights management | Cooperative governance — membership classes have voting rights per Bylaws. | Link subscription status to voter registry; maybe separate from member-console. |
| Credit system | Prepaid credits for Flexible services or usage-based billing. | credit_grants + credit_transactions tables; apply to invoices. |
| Coupons & promotion codes | Discounts for promotions, referrals, etc. | coupon_mappings, promotion_code_mappings, sync core coupons to Stripe. |
| Refunds & disputes | Handle customer refunds and dispute resolution. | refund_mappings, dispute_mappings, bidirectional handling. |
| Credit application | Apply credit grants to Stripe Customer Balance. | Integration with Stripe Customer Balance API. |
| Invitations & team management | Orgs can invite additional members to collaborate on sites. | Organization invitations; role-based access within workspace. |
| Service accounts & API tokens | Programmatic access to member-console APIs. | Personal access tokens scoped to workspace. |
| Webhook integrations | External systems can push/pull events from member-console. | integration.webhook_events + integration_outbox. |
| Auth status endpoint | External sites check session state to show dynamic CTAs ("Sign in" vs "Console"). | GET /auth/status returning {authenticated}, CORS allowlist, parent-domain session cookie. |
| Hosted landing page | Co-ops without a separate website get a configurable front door with tier info. | Unauthenticated / renders configurable welcome page; links to /register and /login; optional pre-registration email capture. |
| Post-registration redirect | Return users to the originating site after signup. | ?return_to= param on /register, stashed in session, origin allowlist to prevent open redirect. |
| Custom theming (two-tier) | Member console is open-source; deployments need to brand it without forking. | Tier 1 (easy): operator admin UI overrides Bootstrap CSS variables (--bs-primary, --bs-secondary, --bs-body-color, a handful of others) — Bootstrap 5's CSS variable architecture makes this a small server-side feature (inject a <style> block with overrides). Tier 2 (power): unrestricted custom CSS injection for deeper changes. Reference model: Discourse admin appearance settings. Builds on the app.css scope contract established in M7c. |
| Minimum-term commitments (Decision 126 completion) | Offer plans that bind a member for a minimum term with a clear, enforced early-exit story (block / fee / allow). | Schema already landed (subscriptions.commitment_end / commitment_renewal / early_termination_policy + subscription_scheduled_changes), and block/allow enforcement shipped in plan-switch-mechanics. Three pieces remain, all latent until an operator can actually configure a commitment: (1) operator UI to set commitment terms on a subscription/product; (2) the fee branch — emit an early_termination_fee line item (needs the invoice_line_items.line_type column + credit-ledger, neither in code yet); (3) member-catalog per-tier labeling of block/fee moves (endpoints already enforce policy; only pre-click labeling is missing). See the three commitment-related entries in status/issues.md. |