Architecture: Multi-Tenant Access

arch-multi-tenant Doc

architecture active

Architecture: Multi-Tenant Client Board Access

Decision

Option B — pal-enterprises proxy pattern. Clients access their project board through pal-enterprises, which fetches data from pal-e-docs internally and renders a read-only view. pal-e-docs remains an internal ops tool with no client-facing auth.
Decided: 2026-05-10. Spike: ldraney/pal-enterprises#13

Options Evaluated

Option A — pal-e-docs native scoping (rejected): Would require adding multi-tenant RBAC to pal-e-docs. The API has zero auth — list_boards() returns all boards, list_board_items() has no scoping. Changes the fundamental nature of pal-e-docs from internal ops tool to client-facing platform. High effort, high risk, wrong separation of concerns.
Option B — pal-enterprises proxy (selected): pal-enterprises already owns client auth (Keycloak OIDC). A new controller fetches board data via pal-e-docs API (internal, service-to-service), renders read-only view filtered to client's project. ~100 lines of Rails code, no changes to pal-e-docs. Lucas controls what clients see — can filter labels, hide ops-only items.
Option C — Hybrid (not needed): No advantage over pure proxy since pal-e-docs doesn't need client auth for any other reason.

Keycloak Mapping Strategy

  • Add project_slug user attribute to client users in Keycloak (e.g. project_slug: "westside")
  • Configure a protocol mapper on the pal-enterprises OIDC client to include project_slug in token claims
  • pal-enterprises reads the claim, fetches list_board_items(board_slug: "board-{project_slug}")
  • Same attribute can gate Forgejo repo access later via Keycloak group/role mapping

Implementation Shape

  • Keycloak: Add project_slug user attribute + OIDC protocol mapper
  • pal-enterprises: ClientBoardController — reads claim, calls pal-e-docs API, renders read-only view
  • pal-enterprises: Board view template using existing CSS design system (columns, cards, status indicators)
  • No write endpoints — pure display, no forms, no mutations
Follow-up ticket: ldraney/pal-enterprises#17

Read-Only Enforcement

  • pal-enterprises only exposes GET routes for the board view
  • pal-e-docs API calls are server-side only — client browser never contacts pal-e-docs
  • No client-side JS that could be exploited to make write calls
  • ldraney/pal-enterprises#13 — spike that produced this decision
  • ldraney/pal-enterprises#17 — follow-up feature ticket
  • ldraney/pal-enterprises#9 — owner dashboard (Lucas sees all clients)