pal-e-app

pal-e-app forgejo

Notes

Project Page 1
  • Project: pal-e-app project-pal-e-app

    Vision

    The SvelteKit frontend for the pal-e-docs project. The / route is the public landing page (sign-in CTA, public notes, marketing surface); all other routes are gated by Keycloak. Once authenticated, users see role-scoped projects, kanban boards, and notes. The permission model is project-scoped: users are assigned to projects with read or write access, and see all child notes of those projects. Per definition-app, the landing page is a route inside the app, not a separate service — one repo, one hostname, one auth funnel.

    User Stories

    Key Role Story Status
    PA-S1 Stakeholder Register via pal-e-landing form so Lucas can onboard me Not started
    PA-S2 Stakeholder Log in and reset password via Keycloak so I can access my scoped content Not started
    PA-S3 Stakeholder See only the projects and boards assigned to me so I'm not overwhelmed Not started
    PA-S4 Admin (Lucas) Assign users to projects with read/write permissions so I control what each person sees Not started
    PA-S5 Admin (Lucas) Configure a user's landing page so they see what matters most on login Not started

    Architecture

    Three architecture notes (pending creation):

    1. Domain Model — user, project, permission, note hierarchy
    2. Data Flow — landing → Keycloak → pal-e-app → pal-e-docs API (filtered by permissions)
    3. Deployment — pal-e-landing (static) + pal-e-app (SvelteKit, adapter-node) + Keycloak + pal-e-docs API

    Key Decisions

    • Permission scope = project. User gets access to a project → sees all child notes of that project note.
    • Permission levels: read or write per user per project.
    • Keycloak handles authentication (who are you). pal-e-docs API handles authorization (what can you see).
    • pal-e-landing is a separate static site (adapter-static, new repo). Links to pal-e-app for login.
    • pal-e-docs-app (old deployment name) is retired. pal-e-app is the canonical frontend.
    • Existing SvelteKit codebase is the foundation — block renderer, board views, project nav, Keycloak integration all stay.

    Board

    board-pal-e-app

    Status

    Project created 2026-03-30. Existing pal-e-app repo has 11 routes, Keycloak OIDC, block renderer, board kanban views, project navigation. Missing: project-scoped permissions, registration flow, configurable user landing pages.

    Milestones

    None yet.

    Repos

    Repo Platform Role Status
    pal-e-app Forgejo SvelteKit authenticated frontend (adapter-node) Active — needs permissions layer
    pal-e-landing Forgejo Static landing page (adapter-static) Not yet created

    Key People

    Role Person Access
    Superadmin Lucas Everything — all projects, full read/write
    Admin Marcus westside-basketball — read/write on boards, stories, docs
    Stakeholder Mom westside-basketball — read-only on project kanban, timeline/dependency visibility

    First Users & AC

    Phase 1 AC: Marcus and Mom can register via pal-e-landing, log in via Keycloak, reset password, and see scoped project boards. Lucas can see them in Postgres and assign permissions.

    Phase 2 AC: Authenticated views (projects, kanban, tickets) look good and are useful for stakeholders. Mom can see dependencies and timelines.

User Story 1
  • Story: Board Card Traceability (pal-e-app) story-pal-e-app-board-card-traceability

    story: Board Card Traceability

    Role

    Lucas (platform owner / operator)

    Key

    board-card-traceability

    Want

    As a platform owner, I want to see the full traceability triangle (story / arch / spec / PRs) for any board card I click in pal-e-app.

    So That

    So that I can navigate from any ticket to why it exists, what part of the system it touches, and what code came from it — without leaving pal-e-app or hand-grepping Forgejo. This is the foundation of "every piece of code known and mapped."

    Acceptance Criteria

    • [ ] Clicking any board card opens a right-side drawer (does not navigate away)
    • [ ] Drawer shows: title, type badge, current column, full label list, linked Forgejo issue (if any), linked note (if note_slug set)
    • [ ] Each label in the drawer is a clickable link: story:X/notes/story-{project}-X, arch:X/notes/arch-*-{project} (resolution order documented in the convention)
    • [ ] Drawer closes on Escape, backdrop click, or close button
    • [ ] "Move to column" affordance moves to a button inside the drawer (no longer triggered by tap-on-card)
    • [ ] Long unbreakable tokens in card titles (e.g. draft_tournament_email) wrap correctly via overflow-wrap: anywhere
    • [ ] Create-Item and Delete-Item modals render correctly without Tailwind utility classes — pure CSS vars matching app.css
    • [ ] Visually verified on Lucas's phone (mobile-first) and on desktop

    Success Metric

    Lucas can click any board card on any board and reach all four traceability links (story note, arch note, Forgejo issue, parent project page) in ≤ 1 click each from the drawer. Self-reported "I can find anything" — measured by absence of follow-up complaints in the next two weeks of active use.

    • arch-dataflow-pal-e-app — SvelteKit kanban page is the data-flow surface; this story extends it. Components table needs an entry for "Board Renderer" with the drawer pattern (discovered scope, separate ticket).
    • arch-domain-pal-e-app — BoardItem entity carries the labels the drawer surfaces; no schema change needed (data is already there).
    • Canonical label for tickets: arch:sveltekit-ssr (per convention-architecture-ids) since the board page is the SvelteKit rendering surface.
    • project-pal-e-app — parent project page
    • board-pal-e-app — board where execution tickets live
    • convention-architecture-ids — defines canonical arch: labels used in the drawer's link resolution
    • feedback_traceability_triangle — the platform principle this story dogfoods
    • feedback_no_tailwind — drives the modal cleanup acceptance criterion
    • feedback_block_component_model — documents the Board as the renderer exception
    • Future: convention-admin-surface-ui (to be created from this work, applied to westside-admin next)
Doc 1
  • Validation: pal-e-production → pal-e-app rename (#114)

    Verdict: PASS

    Acceptance Criteria

    Criteria Status Evidence
    Forgejo repo at forgejo_admin/pal-e-app PASS API rename returned 200, URL accessible
    k8s namespace pal-e-app with running pod PASS kubectl: 1/1 Running
    https://pal-e-app.tail5b443a.ts.net returns 200 PASS curl: HTTP/2 200, content-type text/html
    ArgoCD synced + healthy PASS Synced Healthy
    Monitoring probe renamed PASS PR #287 merged — probe name + labels updated
    Network policies reference pal-e-app PASS PR #287 merged — lines 172, 196 updated
    Golden signals dashboard renamed PASS PR #287 merged — file renamed, 14+ PromQL refs updated
    Local remote updated PASS ~/pal-e-app origin → forgejo_admin/pal-e-app.git

    PRs Merged

    • pal-e-deployments#120 — new kustomize overlay
    • pal-e-deployments#121 — SOPS harbor-creds (initial)
    • pal-e-deployments#122 — fix harbor-creds password
    • pal-e-deployments#123 — fix harbor-creds username (bash $ expansion)
    • pal-e-platform#287 — monitoring + network policies + dashboard

    Issues Encountered

    • SOPS harbor-creds had wrong username: bash $ expansion turned robot$pal-e-app into ldraney. Fixed by generating YAML with Python.
    • Harbor image was OCI format — containerd HEAD request got 404 misread as 401. Fixed with skopeo copy --format v2s2 (OCI → Docker manifest).
    • Terraform state drift: pal-e-production robots not in state, blocking applies. Resolved by removing pal-e-production from tfvars.
    • Overlay directory was pal-e-docs-app not pal-e-production — previous rename never completed at directory level.

    Remaining Cleanup

    • Old overlays/pal-e-docs-app/ directory still in pal-e-deployments — can be deleted in a future PR
    • CORS env var PALDOCS_ALLOWED_ORIGINS on pal-e-docs deployment needs updating to include https://pal-e-app.tail5b443a.ts.net
    • Woodpecker CI webhook needs verification on next push to renamed repo
    • Board items #979, #980 have Forgejo URLs pointing to old repo name (auto-redirected by Forgejo)
Review 2
  • Verdict: APPROVED

    Re-review after refinement. All 6 items from review-989-2026-04-12 have been addressed. Ticket is well-scoped with verified file targets, clear decomposition, and blue-green execution strategy. Routes to skill-decompose-ticket for sub-board creation.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — standalone, references related bugs #99, #97, #81
    • [x] Repo — multi-repo documented (pal-e-services, pal-e-deployments, pal-e-platform, pal-e-production)
    • [x] User Story — platform operator naming convention compliance
    • [x] Context — explains 5-layer name embedding, blue-green strategy
    • [x] File Targets — comprehensive with line numbers, includes "do not touch" section
    • [x] Acceptance Criteria — 12 criteria covering all layers
    • [x] Test Expectations — tofu plan, curl, CORS, grep verification
    • [x] Constraints — blue-green, lock=false, SOPS, Harbor re-tag, dashboard filename
    • [x] Checklist — 10 items tracking execution
    • [x] Related — project, definition, CORS fix, naming convention
    • [x] Decomposition — 5-step ordered breakdown with parallelism noted

    Traceability

    • [x] story:app-definition label — new story key for naming convention compliance initiative. Not yet in project-pal-e-app user-stories table. Acceptable for cross-cutting infra work.
    • [ ] story note NOT YET CREATED — [SCOPE] Add "app-definition" story entry to project-pal-e-app user-stories section: "As a platform operator, I want infrastructure names to match the naming convention so that discoverability and consistency are maintained."
    • [x] arch:pal-e-app label — references the component being renamed. Architecture notes (arch-deployment-pal-e-app) are listed as pending on the project page. Acceptable for infra rename — no arch diagram changes needed.
    • [ ] arch note pending — arch-deployment-pal-e-app not yet created per project page. Not blocking for this ticket.
    • [x] Forgejo issue — forgejo_admin/pal-e-deployments#114, open

    File Targets

    • [x] pal-e-services/terraform/k3s.tfvars — verified: line 202 has pal-e-production service key with forgejo_repo, image_repo, source_path
    • [x] pal-e-deployments/overlays/pal-e-production/ — verified: directory exists with prod overlay (ingress.yaml, kustomization.yaml)
    • [x] pal-e-deployments/overlays/pal-e-docs/prod/kustomization.yaml — verified: 2 occurrences of pal-e-production (cross-namespace refs)
    • [x] pal-e-platform/terraform/modules/monitoring/main.tf lines 386-388 — verified: old pal-e-production probe exists alongside pal-e-app probe at 391
    • [x] pal-e-platform/terraform/modules/monitoring/main.tf lines 649-661 — verified: pal-e-production dashboard ConfigMap with file() reference
    • [x] pal-e-platform/terraform/modules/monitoring/main.tf lines 730-741 — verified: embedding worker ServiceMonitor with pal-e-production namespace selector and app labels
    • [x] pal-e-platform/terraform/modules/ops/main.tf lines 109-125 — verified: embedding worker metrics service in var.pal_e_production_namespace
    • [x] pal-e-platform/terraform/modules/ops/variables.tf line 1-2 — verified: variable "pal_e_production_namespace" with description
    • [x] pal-e-platform/terraform/modules/database/main.tf lines 59-67 — verified: data source + postgres secret in pal-e-production namespace
    • [x] pal-e-platform/terraform/modules/database/outputs.tf line 11-13 — verified: output "pal_e_production_namespace" with description
    • [x] pal-e-platform/terraform/network-policies.tf lines 172, 196 — verified: cross-namespace allow rules referencing pal-e-production
    • [x] pal-e-platform/terraform/secrets.auto.tfvars.example line 35 — verified: comment "pal-e-production"
    • [x] pal-e-platform/terraform/dashboards/pal-e-production-golden-signals.json — verified: file exists, needs rename

    Repo Placement

    OK. Issue filed on pal-e-deployments (central kustomize repo). Multi-repo scope is documented: pal-e-services, pal-e-deployments, pal-e-platform, pal-e-production. Decomposition section assigns each repo to a sub-task.

    Dependencies

    • Board items #979 and #980 reference forgejo_admin/pal-e-production issue URLs — ticket documents these need updating post-rename (AC item 12)
    • Item #714 (namespace rename pal-e-docs-app to pal-e-app) overlaps — different rename vector (from pal-e-docs-app, not pal-e-production). May need coordination.
    • Sub-task ordering is correct: repo rename first, then parallel terraform/kustomize, then validation

    Acceptance Criteria

    12 criteria, all verifiable by an agent: kubectl checks, curl, grep, ArgoCD status, tofu plan. Test Expectations section provides specific commands. Comprehensive and testable.

    Blast Radius

    • pal-e-docs CORS env var needs updating (documented in AC #6)
    • Woodpecker CI pipeline in pal-e-production repo references Harbor path — covered by sub-task 1 (repo rename)
    • ArgoCD app definition references old overlay path — covered by sub-task 3
    • No other services reference pal-e-production by name (checked network-policies.tf — only postgres and ollama netpols reference it)

    Decomposition Assessment

    12 AC across 4 repos, 13+ file targets. Clearly exceeds 5-minute rule. Ticket provides a 5-step decomposition with correct ordering and parallelism. Route to skill-decompose-ticket for sub-board creation.

    Recommendation

    • [SCOPE] Add "app-definition" story entry to project-pal-e-app user-stories table. Suggested wording: "As a platform operator, I want infrastructure names to match the naming convention so that discoverability and consistency are maintained."
    • [DECOMPOSE] 12 AC across 4 repos, 5-step decomposition provided in ticket body. Route to skill-decompose-ticket for sub-board creation.

    No blocking issues. Scope is solid. File targets all verified against live codebase.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone with consolidated bug refs
    • [x] Repo -- Multi-repo identified
    • [x] User Story -- present
    • [x] Context -- thorough, explains 5-layer cascade
    • [x] File Targets -- extensive, with "do not touch" list
    • [x] Acceptance Criteria -- 11 items
    • [x] Test Expectations -- 5 items with commands
    • [x] Constraints -- 7 items, blue-green strategy documented
    • [x] Checklist -- 10 items
    • [x] Related -- cross-references present

    All required sections for Feature template are present and well-populated.

    Traceability

    • [x] story:app-definition label -- present on board item
    • [ ] story note MISSING -- No "app-definition" story found in project-pal-e-app user-stories section. Stories there are PA-S1 through PA-S5. [SCOPE] Create user story entry "app-definition" on project-pal-e-app user-stories section, or re-label to match an existing story key.
    • [x] arch:pal-e-app label -- present on board item
    • [ ] arch note MISSING -- search for "arch-pal-e-app" returned no results. [SCOPE] Create architecture note arch-pal-e-app for the pal-e-app component.
    • [x] Forgejo issue -- forgejo_admin/pal-e-deployments#114, open
    • [ ] definition-app note referenced in issue body does not exist in pal-e-docs (search returned empty). [BODY] Verify or create definition-app note.

    File Targets

    • [x] pal-e-services/terraform/k3s.tfvars -- verified: contains pal-e-production service key at line 202
    • [x] pal-e-deployments/overlays/pal-e-production/prod/ -- verified: 4 files exist (kustomization.yaml, ingress.yaml, deployment-patch.yaml, harbor-creds.enc.yaml)
    • [x] pal-e-deployments/overlays/pal-e-docs/prod/kustomization.yaml -- verified: lines 54, 71 contain commented pal-e-production cross-namespace refs
    • [x] pal-e-platform/terraform/modules/monitoring/main.tf lines 386-388 -- verified: blackbox probe name/url/labels for pal-e-production
    • [x] pal-e-platform/terraform/modules/monitoring/main.tf lines 649-661 -- verified: golden signals dashboard ConfigMap
    • [x] pal-e-platform/terraform/modules/monitoring/main.tf lines 730-741 -- verified: embedding worker ServiceMonitor with pal-e-production namespace
    • [x] pal-e-platform/terraform/dashboards/pal-e-production-golden-signals.json -- verified: exists, 14 occurrences of pal-e-production
    • [x] pal-e-platform/terraform/modules/ops/main.tf lines 109-125 -- verified: embedding worker metrics service in pal-e-production namespace
    • [x] pal-e-platform/terraform/modules/ops/variables.tf line 2 -- verified: variable description references pal-e-production
    • [x] pal-e-platform/terraform/modules/database/main.tf lines 59-67 -- verified: namespace data source + postgres secret
    • [x] pal-e-platform/terraform/modules/database/outputs.tf line 12 -- verified: output description
    • [x] pal-e-platform/terraform/network-policies.tf lines 172, 196 -- verified: cross-namespace allow rules
    • [x] pal-e-platform/terraform/secrets.auto.tfvars.example line 35 -- verified: secrets comment

    All 13 file targets verified. Line numbers are accurate.

    Repo Placement

    Issue is filed on pal-e-deployments but touches 3 repos (pal-e-services, pal-e-deployments, pal-e-platform) plus the Forgejo repo rename. This is correctly identified in the issue body. Multi-repo coordination is acknowledged.

    Dependencies

    • Board items #714 (pal-e-platform#257 "Namespace rename pal-e-docs-app to pal-e-app") and #712 (pal-e-platform#255 "Keycloak client rename") are related naming work -- potential ordering dependency not documented.
    • Board items #979 and #980 are filed against pal-e-production repo -- these will need URL updates after repo rename.
    • Board items #716, #717 reference pal-e-production issues -- Forgejo issue URLs on these board items will break after repo rename.
    • Consolidated bugs (#99, pal-e-deployments#97, #81) are listed as related but dependency ordering is clear (this ticket resolves them).

    Acceptance Criteria

    11 acceptance criteria. Most are verifiable by an agent (kubectl, curl, grep, tofu plan). The criteria are clear and testable. However, 11 AC across 3 repos plus a Forgejo admin API call is too many for a single agent pass.

    Blast Radius

    • Grep confirms 34 occurrences of "pal-e-production" in pal-e-platform/terraform/ and 16 in pal-e-deployments. The ticket's "60+ references" claim is roughly correct when including the dashboard JSON (14 alone).
    • Existing board items referencing pal-e-production Forgejo URLs (#979, #980, #716, #717) will have broken issue links after rename. These need updating.
    • monitoring/main.tf already has BOTH a pal-e-production probe (line 386) AND a pal-e-app probe (line 391) -- the new probe exists, so the rename is partially in progress. Ticket should note this to avoid creating a duplicate.
    • The embedding worker references (ops/main.tf, monitoring/main.tf) use a var.pal_e_production_namespace variable -- renaming this variable cascades through the module call in the root module. Ticket should document this terraform variable rename.

    Decomposition Assessment

    NEEDS DECOMPOSITION. This ticket fails the 5-minute rule on all dimensions:

    • 13+ file targets across 3 repos (threshold: >3 files across >2 repos)
    • 11 acceptance criteria (threshold: >5)
    • Estimated agent work: 30+ minutes (blue-green across 3 repos with tofu apply steps)
    • Natural decomposition: (1) Forgejo repo rename, (2) pal-e-services terraform, (3) pal-e-deployments kustomize, (4) pal-e-platform monitoring/network-policies, (5) validation + old infra cleanup

    Route to skill-decompose-ticket for sub-board creation.

    Recommendation

    • [SCOPE] Create user story entry "app-definition" on project-pal-e-app user-stories section, or relabel board item to match an existing story key (PA-S1 through PA-S5).
    • [SCOPE] Create architecture note arch-pal-e-app for the pal-e-app component.
    • [BODY] Note that monitoring/main.tf already has a pal-e-app probe at line 391 -- ticket should clarify this is a remove-old, not add-new for that specific target.
    • [BODY] Document the terraform variable rename: var.pal_e_production_namespace must become var.pal_e_app_namespace (affects ops/variables.tf, database/main.tf, database/outputs.tf, and the root module call).
    • [BODY] Add note about board items (#979, #980, #716, #717) whose Forgejo issue URLs will break after repo rename -- these need URL updates.
    • [DECOMPOSE] 13 file targets across 3 repos, 11 AC, estimated 30+ min. Route to skill-decompose-ticket. Natural split: (1) Forgejo repo rename + Harbor re-tag, (2) pal-e-services terraform, (3) pal-e-deployments kustomize, (4) pal-e-platform terraform, (5) validation + cleanup.
Architecture 3
  • Deployment: pal-e-app arch-deployment-pal-e-app

    Deployment: pal-e-app

    Diagram

    graph TB
        subgraph Public["Public Internet"]
            Browser[Browser / Mobile]
        end
    
        subgraph TS["Tailscale Network"]
            subgraph k3s["k3s Cluster"]
                subgraph ns_landing["Namespace: pal-e-landing"]
                    Landing["pal-e-landing\nnginx / adapter-static"]
                end
                subgraph ns_app["Namespace: pal-e-app"]
                    App["pal-e-app\nSvelteKit adapter-node\nPort 3000"]
                end
                subgraph ns_kc["Namespace: keycloak"]
                    KC["Keycloak\nRealm: pal-e"]
                end
                subgraph ns_api["Namespace: pal-e-docs"]
                    API["pal-e-docs API\nFastAPI\nPort 8000"]
                    Worker["Embedding Worker"]
                end
                subgraph ns_db["Namespace: cnpg-system"]
                    DB["PostgreSQL\nCNPG Cluster"]
                end
            end
    
            F1["Funnel: pal-e.tail5b443a.ts.net"]
            F2["Funnel: pal-e-app.tail5b443a.ts.net"]
            F3["Funnel: keycloak.tail5b443a.ts.net"]
            F4["Funnel: pal-e-docs.tail5b443a.ts.net"]
        end
    
        Browser -->|HTTPS| F1 --> Landing
        Browser -->|HTTPS| F2 --> App
        Browser -->|HTTPS| F3 --> KC
        Landing -.->|Login redirect| KC
        App -->|PKCE auth| KC
        App -->|Bearer API calls| API
        API --> DB
        KC --> DB
    

    Components

    Component Purpose Notes
    pal-e-landing Public landing page for Pal-E Enterprises Not yet deployed. New repo, static site, nginx or adapter-static. Hostname: pal-e.tail5b443a.ts.net
    pal-e-app Authenticated SvelteKit frontend Currently deployed as pal-e-docs-app in namespace pal-e-docs-app. Needs namespace rename. Hostname: pal-e-app.tail5b443a.ts.net
    Keycloak OIDC identity provider Running in keycloak namespace. Realm: pal-e. Client: pal-e-docs-app (needs rename to pal-e-app).
    pal-e-docs API Content API + authorization enforcement Running in pal-e-docs namespace. FastAPI. Will add UserProjectPermission query layer.
    Embedding Worker Vector embedding for semantic search Sidecar in pal-e-docs namespace. Uses Ollama.
    PostgreSQL Persistent storage for all services CNPG-managed cluster. Shared by pal-e-docs and Keycloak.

    Key Decisions

    • All ingress via Tailscale Funnels. No cert-manager, no Traefik. Each service gets its own Tailscale hostname with automatic TLS.
    • pal-e-landing gets its own namespace and funnel. Separate from pal-e-app because it's a different deployment (static vs node) with a different audience (public vs authenticated).
    • Namespace rename needed. Current pal-e-docs-app namespace → pal-e-app. This is a deployment coordination task across ArgoCD, Tailscale Ingress, and the Keycloak client.
    • ArgoCD manages deployments. pal-e-deployments repo has kustomize overlays. New services need overlay creation + ArgoCD Application.
    • CI via Woodpecker. Builds push images to Harbor, trigger ArgoCD sync.
  • Data Flow: pal-e-app arch-dataflow-pal-e-app

    Data Flow: pal-e-app

    Diagram

    Flow 1: Registration and First Login

    sequenceDiagram
        participant U as User (Marcus/Mom)
        participant L as pal-e-landing
        participant K as Keycloak
        participant A as pal-e-app
        participant D as pal-e-docs API
        participant DB as PostgreSQL
    
        U->>L: Visit pal-e.tail5b443a.ts.net
        U->>L: Fill registration form
        L->>K: Redirect to Keycloak registration
        K->>K: User creates account (username, password)
        K-->>L: Redirect back (registered)
    
        Note over U,DB: Lucas sees new user in Keycloak, assigns project permissions
    
        U->>L: Click "Login"
        L->>K: Redirect to Keycloak login
        K->>K: Authenticate (username + password)
        K-->>A: Redirect with PKCE auth code
        A->>K: Exchange code for JWT
        A->>D: GET /projects (Bearer token)
        D->>DB: SELECT projects WHERE user has permission
        DB-->>D: Filtered project list
        D-->>A: [westside-basketball]
        A->>A: Render scoped sidebar and home
    

    Flow 2: Authenticated Board Interaction

    sequenceDiagram
        participant U as User
        participant A as pal-e-app
        participant D as pal-e-docs API
        participant DB as PostgreSQL
    
        U->>A: Navigate to /boards/board-westside-basketball
        A->>D: GET /boards/board-westside-basketball/items (Bearer)
        D->>DB: Check UserProjectPermission for sub + project
        alt Has access
            DB-->>D: Permission found (read or write)
            D->>DB: SELECT board items
            DB-->>D: Items
            D-->>A: Board items JSON
            A->>A: Render kanban columns
        else No access
            DB-->>D: No permission row
            D-->>A: 403 Forbidden
            A->>A: Show "access denied"
        end
    

    Components

    Component Purpose Notes
    pal-e-landing Public entry point, registration form Static site. No auth. Redirects to Keycloak for login.
    Keycloak OIDC provider — registration, login, password reset Realm: pal-e. Public client with PKCE. Self-registration TBD.
    pal-e-app Authenticated SPA — renders scoped content SvelteKit, keycloak-js. Client-side API calls with Bearer token.
    pal-e-docs API Content + authorization — filters by user permissions FastAPI. Extracts sub from JWT, queries UserProjectPermission.
    PostgreSQL Persistent storage — notes, boards, permissions CNPG-managed. UserProjectPermission is the new table.

    Key Decisions

    • Client-side auth, not SSR. pal-e-app is a SPA (ssr=false). keycloak-js handles the PKCE flow in the browser. This avoids server-side session management.
    • Permission check at API layer. The pal-e-docs API checks UserProjectPermission on every request, not the frontend. Frontend renders what the API returns — no client-side permission logic.
    • Registration may be manual initially. Lucas creates Keycloak users and assigns project permissions. Self-registration can be enabled on the Keycloak realm later if needed.
    • Token refresh is automatic. keycloak-js refreshes 30 seconds before expiry. No user-visible session interruption.
  • Domain Model: pal-e-app arch-domain-pal-e-app

    Domain Model: pal-e-app

    Diagram

    erDiagram
        KeycloakUser {
            string sub PK "Keycloak subject ID (JWT claim)"
            string preferred_username
            string email
            string name
        }
    
        UserProjectPermission {
            int id PK
            string keycloak_sub FK "References KeycloakUser.sub"
            int project_id FK "References Project.id"
            string permission "read | write"
            timestamp created_at
        }
    
        Project {
            int id PK
            string slug UK
            string name
            string platform
            bool is_public
        }
    
        Note {
            int id PK
            string slug UK
            string title
            string note_type
            bool is_public
            string parent_slug FK "nullable — hierarchy"
            int project_id FK "nullable — ownership"
        }
    
        Board {
            int id PK
            string slug UK
            string name
            int project_id FK
        }
    
        BoardItem {
            int id PK
            int board_id FK
            string column
            string title
            string item_type
        }
    
        KeycloakUser ||--o{ UserProjectPermission : "has permissions"
        Project ||--o{ UserProjectPermission : "grants access to"
        Project ||--o{ Note : "contains"
        Project ||--o{ Board : "has"
        Board ||--o{ BoardItem : "contains"
        Note ||--o{ Note : "parent-child"
    

    Components

    Component Purpose Notes
    KeycloakUser Identity — who is this person Not a local table. Extracted from JWT sub 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.
Board 1
Repos 1
  • pal-e-app
    active