- Python 99.8%
- Mako 0.1%
|
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
|
||
|---|---|---|
| alembic | ||
| k8s | ||
| scripts | ||
| src/pal_e_docs | ||
| tests | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| .woodpecker.yaml | ||
| alembic.ini | ||
| CLAUDE.md | ||
| Dockerfile | ||
| pyproject.toml | ||
| README.md | ||
pal-e-api
The platform's knowledge service (repo formerly pal-e-docs — renamed to match its role as the FastAPI backend). Every SOP, architecture decision, project plan, and onboarding guide lives here — queryable by AI agents, readable by humans, and versioned in a database that never bloats the repo.
Why This Exists
The pal-e platform is a self-hosted system where the tools you use to build are the tools you ship. But tools without knowledge are just programs. pal-e-docs is what turns the platform into a system that remembers:
- AI agents query it for context at the start of every session — SOPs, project state, architecture decisions — so they never start cold
- Humans browse the same data through a web frontend — same source of truth, different view
- Services query it for platform conventions — auth patterns, deployment steps, naming conventions
- Plans are stored as notes with tags and cross-links, forming a chain where each plan inherits context from the one before it
Without pal-e-docs, knowledge lives in scattered markdown files, stale Notion pages, and context windows that evaporate between sessions. With it, knowledge accumulates, stays current, and is always one API call away.
Architecture
Storage: SQLite on PVC
The database is a single SQLite file on a Kubernetes PersistentVolumeClaim. The repo contains only schema migrations and application code — it never grows as content is added.
Why SQLite:
- Single writer (admin-only), single replica, read-heavy — a perfect fit
- No separate database Deployment, Service, PVC, or secrets to manage
- WAL mode enables concurrent reads during writes
- Backup is a file copy (Litestream for continuous replication later)
API: FastAPI
A REST API serves notes, projects, and tags. Tag intersection queries (?tags=auth,basketball-api) return exactly the context needed — no scanning file trees, no token-heavy bulk fetches.
Auth: pal-e-auth (shared platform package)
Public notes are readable without authentication. Write operations require the admin role via JWT from the shared pal-e-auth package. Currently there is one admin (Lucas); the "single writer" design reflects this — SQLite's single-writer lock is a feature, not a limitation.
Data Model
Project Note Tag
─────── ──── ───
id id id
name ◄── project_id name (unique)
slug title
platform slug NoteTag
repo_url html_content ───────
is_public note_id
created_at tag_id
updated_at
NoteLink
NoteRevision ────────
──────────── source_id
id target_id
note_id
html_content
revised_by ← PR URL or commit SHA
revised_at
revision_number
Projects organize notes by codebase. A project maps to a repo (GitHub, Forgejo, or local).
Notes store HTML content (rendered from Markdown on write, stored as HTML for fast retrieval). They can belong to a project or stand alone as general knowledge. Public notes are readable by anyone; private notes require auth.
Tags enable cross-cutting queries. A note about auth in basketball-api has tags auth and basketball-api. Querying ?tags=auth returns all auth-related notes across all projects.
NoteLinks connect notes bidirectionally — zettelkasten-style cross-references.
NoteRevisions track every edit with a revised_by field linking back to the PR or commit that prompted the change. This is documentation versioning without git tracking content.
Three Roles
1. Platform Knowledge Base
SOPs, conventions, and architecture decisions live as tagged notes:
GET /notes?tags=sop,active— all current SOPsGET /notes?tags=onboarding— how to deploy a new serviceGET /notes?tags=architecture,auth— auth design decisions
These are the notes that claude-custom hooks reference on session startup. When an SOP changes, the note is edited in pal-e-docs and the next session picks it up — no commit to claude-custom needed.
2. Planning Continuity
Plans are not isolated documents — they form a chain. Each plan reads the previous plan's Vision and Next Plan Seeds, carries forward the decision log, and produces the next iteration. No session starts cold.
Plans are notes with structured content:
- Tagged with
plan+ the project slug - Cross-linked to the previous plan (via NoteLink)
- Decisions accumulate as tagged notes (
decision+ project) - Next Plan Seeds from one plan become candidate phases in the next
The /plan skill reads the latest plan note, carries forward Vision and Seeds, and writes the new plan back. The SOP enforces the template, the template enforces continuity. Context flows session to session without loss.
3. Project Documentation
Every project (basketball-api, pal-e-auth, claude-custom) has a project entry with notes:
GET /projects/basketball-api/notes— all basketball-api docsGET /notes?tags=basketball-api,stripe— Stripe integration specificsGET /notes/{slug}/revisions— when was this doc last updated, and why
PR templates across all repos remind: "Did this PR change behavior documented in pal-e-docs? Update the relevant note."
How claude-custom Connects
Session Start
│
▼
~/.claude/CLAUDE.md (bootstrap: identity, platform URL)
│
▼
Session-start hook queries pal-e-docs:
GET /notes?tags=sop,active → current SOPs
│
▼
Agent has: identity + SOPs + available tools (hooks, skills, MCP)
│
▼
Agent assesses: what repos exist, what's deployed, what's in progress
│
▼
Work begins with full platform context
claude-custom controls behavior — hooks enforce the SOP, skills provide workflows, commands provide shortcuts.
pal-e-docs controls knowledge — what the SOPs say, what decisions have been made, what the current state of each project is.
This separation means SOPs can evolve through pal-e-docs edits without touching claude-custom. The hooks enforce that SOPs are followed; pal-e-docs defines what the SOPs are.
API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/projects |
none | List all projects |
GET |
/projects/{slug}/notes |
none/admin | Notes in a project |
GET |
/notes |
none/admin | Search by tags: ?tags=auth,sop |
GET |
/notes/{slug} |
none/admin | Note with HTML content |
POST |
/notes |
admin | Create note |
PUT |
/notes/{slug} |
admin | Update note (creates revision) |
DELETE |
/notes/{slug} |
admin | Delete note |
GET |
/notes/{slug}/links |
none | All linked notes (both incoming and outgoing) |
PUT |
/notes/{slug}/links |
admin | Set outgoing links (reverse links created automatically) |
GET |
/notes/{slug}/revisions |
none | Revision history |
POST |
/projects |
admin | Create project |
PUT |
/projects/{slug} |
admin | Update project |
GET |
/tags |
none | List all tags |
GET |
/healthz |
none | Health check |
GET |
/metrics |
none | Prometheus metrics |
Public notes (is_public = true) are readable without auth. Private notes and all write operations require admin JWT.
Deployment
Deployed via the standard pal-e-services GitOps pipeline:
# In pal-e-services k3s.tfvars
pal-e-docs = {
forgejo_repo = "forgejo_admin/pal-e-api"
image_repo = "pal-e-docs/api"
port = 8000
funnel = true
}
tofu apply creates the namespace, Harbor project, ArgoCD app, and Tailscale funnel. Woodpecker CI builds and pushes on every merge to main. ArgoCD syncs.
Live at: https://pal-e-docs.tail5b443a.ts.net
Tech Stack
- Runtime: Python 3.12 + FastAPI + Uvicorn
- Database: SQLite with WAL mode, on Kubernetes PVC
- ORM: SQLAlchemy + Alembic migrations
- Auth: pal-e-auth (shared JWT + Google OAuth package)
- CI: Woodpecker CI → Harbor → ArgoCD
- Monitoring: Prometheus metrics + Loki logs via platform stack
Roadmap
- Scaffold: FastAPI + SQLAlchemy + SQLite + Alembic + k8s manifests
- Auth integration: pal-e-auth for admin write access
- Seed initial content: platform SOPs, architecture decisions, project docs
- MCP tool: Claude reads/writes notes directly via MCP server
- Frontend: human-readable UI browsing the same SQLite data
- claude-custom hook: session-start SOP injection from pal-e-docs API
- PR templates: "update pal-e-docs" reminder across all repos
- Litestream: continuous SQLite backup/replication
- Notion migration: move PROJ-17 product docs to pal-e-docs