Mark CSOC-WP-0002 identity done on app.coulomb.social. Defer bulk Bubble migration until product foundation exists. Open CSOC-WP-0004 for post-login entry and space content as markdown bound to Forgejo.
13 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated | depends_on | related | origin | origin_ref | state_hub_workstream_id | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CSOC-WP-0002 | workplan | Reestablish coulomb.social with NetKingdom user management | communication | coulomb-social | done | bernd | coulomb-social | 2026-08-09 | 2026-08-10 |
|
|
operator | session-2026-08-09-netkingdom-users | b9addc9a-cc08-4b08-bb67-b6544579beb8 |
Reestablish coulomb.social with NetKingdom user management
Stand up a new coulomb.social application whose members authenticate and are administered through NetKingdom infrastructure (IAM Profile + issuer, user-engine for user-domain facts, flex-auth for authorization decisions) — not Bubble app-local accounts and not a password store inside this repo.
Content and product parity with the live Bubble app are deferred. This
workplan delivers identity, membership, and a minimal authenticated shell so
the platform can exist again on the Railiance delivery lane. Claiming /
migrating Bubble content is a later workplan (after or alongside
CSOC-WP-0001 assessment), not a gate for user management.
Why NetKingdom (not pure app-local)
Business-app service contract v0.1 baseline is app-local accounts (DR-3 A) with a bounded auth module for a later OIDC switch. coulomb.social is a multi-member social product: platform identity and user-domain services are the intended long-term shape.
| Concern | Owner | This repo does |
|---|---|---|
| OIDC login, MFA, token lifecycle | NetKingdom issuer (KeyCape lightweight or Keycloak expanded) via IAM Profile | OIDC client + PKCE; no password store |
| User account, profile, membership facts | user-engine | Consumes projections / APIs; maps (issuer, sub) → local app context |
| Fine-grained authorization | flex-auth (PDP) | PEP only — register protected system + check decisions |
| Session cookie for browser | This app | Short-lived app session after successful OIDC; secrets via OpenBao at deploy |
| Product content (pages, artefacts, chunks) | Later workplan | Out of scope here |
Canonical references:
net-kingdom/canon/standards/iam-profile_v0.2.md(or current v0.3)net-kingdom/canon/standards/user-engine-boundary-contract_v0.1.mdnet-kingdom/docs/user-engine-interface-guidance.mdthe-custodian/canon/standards/business-app-service-contract_v0.1.md§§1–2- Delivery lane reference:
vergabe-teilnahme
Out of scope (explicit)
- Bubble content inventory, export, or migration (CSOC-WP-0001 / follow-on)
- Full UI parity with the live product
- Marketplace / co-creation domain features beyond auth shell
- Owning KeyCape, Keycloak, user-engine, or flex-auth implementation
- Platform SSO for other Binky apps (this work only onboards this app)
Acceptance (workplan done when)
- A deployed (or locally runnable) coulomb.social instance completes OIDC login against a NetKingdom IAM Profile issuer (dev/bootstrap allowed).
- Successful login creates or links a user-engine identity link and application membership for coulomb.social; logout ends the app session.
- Unauthenticated visitors cannot reach member routes; at least one protected “signed-in home” shell renders with principal display name/subject.
- Auth is isolated behind a single module/boundary (service contract §2.3).
- Tenant key present from migration one (Binky / client #1).
- Docs record the onboarding bindings (OIDC client, user-engine app id, flex-auth system id if registered) without secrets.
- No Bubble dependency for login or member identity.
T01 — ADR: NetKingdom identity for coulomb.social
id: CSOC-WP-0002-T01
status: done
priority: high
state_hub_task_id: "57a7b778-b1d6-44fe-95ba-f12373c0355c"
Write docs/adr/ADR-0001-netkingdom-identity.md deciding:
- IAM Profile as the only authentication contract (issuer-agnostic).
- user-engine as source of truth for user-domain facts and memberships.
- flex-auth as PDP for protected actions (fail-closed for sensitive writes).
- Explicit non-goals: app-local password DB; Bubble as IdP; content migration in this plan.
- Relationship to DR-3 A (contract baseline) vs this product’s NetKingdom path.
- Dev/bootstrap path (local-identity or KeyCape) vs production issuer.
Done when: ADR reviewed and linked from INTENT/SCOPE.
T02 — Application scaffold and tenant model
id: CSOC-WP-0002-T02
status: done
priority: high
state_hub_task_id: "7ffa6485-4929-4958-b3b1-af98241beb02"
Scaffold the standalone service (stack choice recorded; prefer alignment with the business delivery lane unless ADR says otherwise):
- App entrypoints, config, health/ready probes.
- DB migrations:
tenant_idon all client-owned tables from day one. - Empty member context table(s) keyed by tenant + stable user-engine
user_id/ external identity link — no password columns. - Dev compose / run instructions in README or
docs/dev.md.
Done when: make test (or equivalent) passes smoke tests for boot +
migrations; no product domain models beyond auth shell.
T03 — NetKingdom OIDC client binding
id: CSOC-WP-0002-T03
status: done
priority: high
state_hub_task_id: "1b1d1162-d44a-45db-b906-05ebd7110f21"
Register coulomb.social as an OIDC client against the active NetKingdom issuer mode (document which: KeyCape vs Keycloak vs local-identity for dev):
- client_id, redirect URIs (no wildcards), scopes, PKCE.
- Secrets only in OpenBao / local 0600 files — never Git.
- Conformance: tokens satisfy IAM Profile claims needed by the app
(
sub, issuer, tenant/principal claims as required).
Record non-secret binding facts under docs/identity/oidc-client.md.
Done when: browser authorization code + PKCE login completes against the
chosen issuer in a dev environment; evidence in docs/identity/.
2026-08-09: Registered public client coulomb-social on live KeyCape
(kc.coulomb.social) via scripts/register-keycape-client.sh. Authorize
probe: registered redirect → Authelia handoff; bad redirect →
invalid_profile_usage. Human MFA browser login remains the final UX check
when running the app with OIDC_ENABLED=true.
T04 — user-engine application onboarding
id: CSOC-WP-0002-T04
status: done
priority: high
state_hub_task_id: "bbf8183c-2dd6-448b-a257-8d837d2d5ebf"
Per application onboarding contract
(user-engine-interface-guidance.md):
- Register application
coulomb-socialin user-engine (display name, owner, allowed profile scopes, projection types). - On first successful OIDC login: create/link external identity
(issuer, sub)→user_id; ensure tenant membership for Binky / client #1. - Define minimal application profile attributes needed for shell (display name, avatar optional) — not Bubble content fields.
- Document bindings in
docs/identity/user-engine-binding.md.
Coordinate with NetKingdom/user-engine operators if platform registration steps are outside this repo’s authority (NK-WP-0023/0024, USER-WP-*).
Done when: login yields a resolvable user-engine projection usable by the app; second login is idempotent (same user_id).
2026-08-09: HttpUserEngineClient calls live user-engine
GET /api/v1/me with X-User-Engine-Proxy-Secret + verified claims
envelope (iss/sub/tenant/principal_type/aud including user-engine-portal).
Offline stub remains when URL/secret unset. Live probe created
usr_… under tenant:coulomb.
T05 — App auth module (OIDC session boundary)
id: CSOC-WP-0002-T05
status: done
priority: high
state_hub_task_id: "ea546dd7-e1c8-4040-a738-270d5004fd73"
Implement a single auth boundary module (§2.3):
- Login start / callback / logout routes.
- Secure session after code exchange (httpOnly cookie or equivalent).
- Middleware: unauthenticated → login; authenticated → attach principal (issuer, sub, user_id, tenant_id, display claims).
- No credentials stored in app DB; refresh strategy documented.
- Unit/integration tests for happy path + rejected/expired tokens.
Done when: tests green; module is the only place that talks OIDC.
T06 — Authenticated shell and member routes
id: CSOC-WP-0002-T06
status: done
priority: medium
state_hub_task_id: "b2d48db3-a216-440e-afbf-51fa9c33b4da"
Ship a minimal product surface that proves user management works:
- Public landing (unauthenticated) with “Sign in”.
- Protected home (
/appor similar) showing signed-in identity summary. - Sign out control.
- Optional: admin-only stub route gated via flex-auth or coarse IAM role (if flex-auth not ready, document temporary claim check + follow-up).
Do not rebuild Bubble Research / Pages / Artefacts here.
Done when: manual smoke checklist in docs/identity/smoke.md passes.
T07 — flex-auth protected system (minimal)
id: CSOC-WP-0002-T07
status: done
priority: medium
state_hub_task_id: "04c13dad-1c6e-45b9-96ba-438458e2c388"
Register coulomb.social as a flex-auth protected system with a tiny
resource/action vocabulary (e.g. shell:view, member:self:read). Wire the
app as PEP (fail-closed when policy engine unavailable for protected actions).
If cluster flex-auth is not yet available for this app, deliver:
- local policy adapter for dev, and
- a blocked residual / intake for production flex-auth binding.
Done when: at least one protected action is decided via the authz port; docs record system id and vocabulary.
2026-08-09: Local vocabulary + HTTP PEP (POST /v1/check) implemented; shell:view enforced on /app/. General flex-auth check Service still absent in-cluster — leave FLEX_AUTH_BASE_URL empty for local mode.
T08 — Delivery-lane packaging stub
id: CSOC-WP-0002-T08
status: done
priority: medium
state_hub_task_id: "4718f21c-d509-45b9-a8f5-31de2099fd3f"
Align with business-app delivery lane without full production cutover:
- Container image build from commit SHA (or documented path to it).
- Placeholder railiance-apps values notes / chart stub.
- Runtime secret references (OIDC client secret, DB) named for OpenBao — values not in Git.
- Health endpoints suitable for deploy smoke.
Done when: image builds in CI or documented local script; deploy notes in
docs/deploy.md.
2026-08-09: Dockerfile added; railiance-apps chart + values + ingress stub + Makefile targets. Image 7067145 published and Helm release deployed; migrations applied; in-cluster /healthz+landing OK. Public DNS still Cloudflare/Bubble; TLS cert pending DNS cutover to 92.205.62.239.
2026-08-09 (smoke continuation): Offline checklist + make test (15) passed. Port-forward with Host: coulomb.social: healthz/landing OK; /auth/login/ 302 to KeyCape with prod redirect + PKCE. KeyCape authorize accepts local and prod redirect URIs (→ Authelia). Full browser MFA login still human step (docs/identity/smoke.md); cutover steps in docs/deploy.md.
2026-08-10 (acceptance complete on parallel host):
- Public rebuild host
https://app.coulomb.social(apexcoulomb.socialremains Bubble). - KeyCape client
mfaRequired: false; id_token claim path deployed (:f5537d8). - Operator tegwick signed in end-to-end; authenticated shell shows principal (display name, subject, user-engine id, tenant).
- Workplan acceptance criteria 1–7 satisfied for identity/membership shell.
- Content, spaces, and Bubble migration are out of scope here — see
CSOC-WP-0004(product shell + markdown/Forgejo content) and deferredCSOC-WP-0001migration.
Sequencing
T01 ADR
└─► T02 scaffold + tenant model
├─► T03 OIDC client
└─► T04 user-engine binding
└─► T05 auth module
└─► T06 shell
├─► T07 flex-auth
└─► T08 packaging stub
T03 and T04 can proceed in parallel after T02 once redirect URIs and app id are known.
Handoffs / residuals (later plans)
| Residual | Future home |
|---|---|
| Post-login product entry + spaces UI | CSOC-WP-0004 (next) |
| Space content as markdown + Forgejo | CSOC-WP-0004 |
| Public self-registration | CSOC-WP-0003 + NK-WP-0025 (platform) |
| Bubble content claim & full migration | CSOC-WP-0001 — after product parity on app.* |
| Production Keycloak expanded mode | NetKingdom (NK-WP-0011 triggers) if demand |
Related
workplans/CSOC-WP-0001-bubble-io-exit-assessment.md— assessment; content laterdocs/design-extract/— visual language for future UI (not blocking T01–T08)docs/design-extract-auth.md— Bubble scrape auth only; not product IdP