cpatpa/PIP

Deep Supabase-to-pg migration with firm-specific auth, workspaces, and a growing roadmap through vector RAG

High-volume, methodical. Commits land in ordered phases with each step independently buildable. The changelog and Instructions.md are kept current as code ships. Security audit findings are tracked by code (H1, L1, L5, M1, M2, etc.) and closed with references to the specific commits. Design documents are written and audited against the actual codebase before implementation begins - the Phase 9 design revision that caught several incorrect assumptions is a good example of that discipline. The fork is firmly tied to Piper Alderman's org data (AU jurisdictions, practice areas, seeded system prompts), but the infrastructure and data-layer work is broadly applicable.

View on GitHub →

cpatpa/PIP is a fork being rebuilt ground-up for Piper Alderman, an Australian law firm. The defining change is a complete replacement of the Supabase JS backend with plain parameterised pg queries across all routes - about 130 call sites converted in ordered migrations. On top of that: Auth.js v5 with a Credentials provider and Microsoft Entra OIDC, a mandatory post-signup onboarding wizard collecting AU-specific practice profile data, workspaces as a team-scope layer above users, join-table sharing replacing JSONB email arrays, and a full admin panel with per-field audit events.

The fork has now completed nine phases. Phase 9 landed per-user memory with model-driven capture and - critically - actually implemented the four-layer system-prompt assembly (org → workspace → user custom instructions → memory block) that the upstream architecture doc described but never wired up. Phases 10-14 exist as detailed design documents covering web search with SSRF defence, group-based RBAC, multi-model comparison, pgvector hybrid retrieval, and knowledge collections.

Infrastructure is Docker Compose: postgres, backend, frontend, Ollama for local LLM, Caddy for TLS, and a nightly pg_dump sidecar. Local storage uses AES-256-GCM per-file encryption. The Ollama adapter supports the full OpenAI-style tool-call loop.

What's in it

Direction

infrastructuremulti-tenantbranding

Activity

Themed changes and pull requests touching this fork, newest first. Themed changes that haven't been turned into a public post yet still appear — they're real work even without a published writeup.

📝 chat: JSON-encode user message content for the jsonb column 1 commit 2mo ago minor change

Threads of work (detailed view)

35 threads have been distilled into posts.

cpatpa ships per-user memory and finally wires the layered system prompt

Phase 9 is two features: a `user_memories` table with model-driven capture, LRU eviction, and a pin cap enforced by a Postgres trigger; and the four-layer system-prompt assembly that the fork's architecture doc had always described but never implemented. Both are behind opposite-default flags.

cpatpa lands design docs for five roadmap phases - web search through knowledge collections

Five docs-only commits covering Phases 10-14: web search with SSRF defence, groups and granular RBAC, multi-model side-by-side comparison, pgvector RAG with hybrid retrieval, and knowledge collections. No code yet, but the data model and migration plans are detailed enough to preview where this fork is heading.

LLM dispatch bug hunt: local/* prefix ignored, stale model ids crash chat

Seven commits tracing one user-visible symptom - `POST /chat` throwing "External AI providers are disabled" on a deployment where only Ollama was provisioned - through four layers of the dispatch stack before landing on the real root cause: `resolveModel` in `lib/llm/models.ts` didn't recognise `local/*` model ids and silently reset them to the Gemini default.

Bare-metal installer: whiptail wizard, self-signed TLS, operator tools

A `sudo bash install.sh` flow that takes a clean Ubuntu 22.04+ or Debian 12+ host from nothing to a running deployment. Fourteen commits of iterative hardening, fixing real bugs found on actual boxes. None of this is in upstream; most is firm-deployment-specific, but the gotchas it patches are generic.

Account page: hardcoded org name, role label replaces usage tier

Single commit reshaping the Account page for a single-tenant deployment. The per-user organisation field and the Usage Plan section are gone; in their place, a hardcoded "Piper Alderman" line and an "Account type" field showing "Administrator" or "User" derived from the JWT role.

Installer seeds bootstrap admin password; Postgres parameter-type bug fixed

Two commits. The installer now asks for a `BOOTSTRAP_ADMIN_PASSWORD` so the operator can sign in immediately after deploy without going through the signup form. The follow-up fixes a silent Postgres transaction abort that prevented the admin account from ever being created.

Phase 10: two-phase document retention with grace window and admin recovery UI

A daily retention job that flag-then-hard-deletes documents, chats, and tabular reviews past their retention horizon. A 7-day grace window between flag and deletion lets admins recover items before bytes are gone, and an `/admin/retention` tab makes that recovery visible.

cpatpa adds a mandatory onboarding wizard and per-user custom instructions

Three commits build the post-signup data-capture flow: a redirect-enforced onboarding wizard, a custom instructions panel with a 4096-char server-side cap, and a practice profile panel for updating the same fields after signup. The org-specific content (Piper Alderman's AU jurisdictions and practice areas) is seeded directly in `org_settings`.

cpatpa adds a workspaces layer between users and the organisation

Three commits build a team-scope container that sits above individual users: the `workspaces` and `workspace_members` tables, workspace_id foreign keys on projects/chats/reviews/workflows with backfill, and a full frontend workspace switcher and management UI. Each user gets an auto-provisioned Personal workspace on signup.

cpatpa replaces shared_with JSONB arrays with proper join tables

Two migrations swap the upstream `shared_with text[]` pattern for `project_members` and `review_members` join tables, both with FK to `users(id)`, RLS forced, and a live backfill of existing rows. The legacy JSONB columns stay in the schema for one cycle as a rollback net.

cpatpa replaces the entire Supabase JS backend with plain pg queries

Nine commits rip out `@supabase/supabase-js` from the backend and replace every PostgREST call with parameterised pg queries - 29 call sites in projects.ts alone, 49 in tabular.ts, roughly 130 total across all routes. The package is gone from package.json by the end of the sequence.

Phase 2: Auth.js v5 replaces Supabase Auth; Entra OIDC; password reset

Full replacement of Supabase Auth with Auth.js v5, plus Microsoft Entra OIDC as a second provider and a complete password-reset flow. The session model is worth reading if you're evaluating the fork's auth approach; the individual security patterns (non-enumerating reset endpoint, hash-before-store) are portable regardless of stack.

Phase 1 security hardening: dead secrets files deleted, body limits, CSP, error redaction

Three commits closing the bulk of an upstream security audit. The most urgent finding - two dead frontend files that referenced god-mode credentials - is fixed first. The rest cover body size limits, zip/XML bomb guards, JWT email validation, a CSP policy, multer upgrade, and scrubbing raw Postgres errors from API responses.