Domain Model: pal-e-app
Domain Model: pal-e-app
Diagram
Components
| Component | Purpose | Notes |
|---|---|---|
| KeycloakUser | Identity — who is this person | Not a local table. Extracted from JWT <code>sub</code> claim at request time. |
| UserProjectPermission | Authorization — what can this person see | NEW table in pal-e-docs Postgres. Maps keycloak_sub → project → read/write. |
| Project | Scope boundary for permissions | Existing entity. User gets project access → sees all child notes and boards. |
| Note | Content unit — docs, SOPs, stories, phases | Existing. Has parent_slug for hierarchy. Permission cascades from project. |
| Board | Kanban view of project work | Existing. One board per project. Visible if user has project access. |
| BoardItem | Work item on a board | Existing. Inherits visibility from board/project. |
Key Decisions
- Keycloak for authn, pal-e-docs for authz. Keycloak answers "who are you" (JWT). The pal-e-docs API answers "what can you see" (UserProjectPermission table). This avoids coupling Keycloak realm roles to pal-e-docs project structure.
- Project is the permission boundary, not note. Per-note permissions would create O(n) complexity. Project-scoped access with parent-child cascade is O(1) per query — check project, done.
- No local user table. The keycloak_sub in UserProjectPermission is the only user reference. User profile data stays in Keycloak. No sync, no drift.
- is_public bypasses permissions. Public notes are visible to everyone regardless of project assignment. This preserves the existing unauthenticated experience.
- read vs write is the only permission axis. No viewer/editor/admin hierarchy. Two levels is sufficient for stakeholder vs operator access.
Related
- Data Flow: pal-e-app — how auth flows at runtime
- Deployment: pal-e-app — where services run
- Project: pal-e-app — project page