Editing These Docs
This site is a workspace app like any other: apps/docs, built with VitePress. Documentation lives next to the code it describes and ships through the same pipeline.
Running it
pnpm docs # from the repo root
pnpm --filter @playpals/docs dev # equivalentOpens on http://localhost:5173 with hot reload.
pnpm --filter @playpals/docs build # static site → .vitepress/dist
pnpm --filter @playpals/docs preview # serve the built outputStructure
apps/docs/
├── .vitepress/
│ ├── config.ts site config, nav, sidebar
│ ├── markdown/mermaid.ts ```mermaid fence → <Mermaid> component
│ └── theme/
│ ├── index.ts theme entry, registers <Mermaid>
│ ├── custom.css PlayPalz brand colours
│ └── Mermaid.vue client-side diagram rendering
├── public/ logo, favicon
├── index.md home page
├── guide/ architecture/ services/ database/
├── api/ mobile/ infrastructure/ operations/
├── reference/ platform/
├── Dockerfile
└── nginx.confAdding a page
- Create the markdown file in the right directory.
- Add it to the sidebar in
.vitepress/config.ts. A page not in the sidebar is unreachable. - Link to it from related pages.
Links are root-relative and omit the .md extension (cleanUrls is on):
See [Realtime](/architecture/realtime).The build fails on a dead link (ignoreDeadLinks: false), which is deliberate — a broken cross-reference is caught before it ships.
Diagrams
Mermaid works in a plain fenced block:
```mermaid
graph LR
A[Client] --> B[api]
B --> C[(PostgreSQL)]
```A custom markdown-it rule replaces the fence with a <Mermaid> component that renders client-side and re-renders when the reader toggles light/dark. Sequence diagrams, ER diagrams, flowcharts, and Gantt charts are all used in these docs.
Callouts
::: tip
Helpful aside.
:::
::: warning
Something that will surprise you.
:::
::: danger
A defect, a security issue, or an irreversible action.
:::
::: info
Neutral context.
:::Use warning for behaviour that differs from what a reader would reasonably expect, and danger for real defects, security gaps, and destructive commands. Do not use them for emphasis.
Status badges
<span class="pp-pill pp-pill--live">Shipped</span>
<span class="pp-pill pp-pill--partial">Partial</span>
<span class="pp-pill pp-pill--planned">Planned</span>House style
Say what is true, including when it is unflattering. These docs name specific defects with file paths and suggested fixes. That is the point — a developer who hits the ogun /admin prefix bug at 2am should find it documented, not discover it.
Point at the code. apps/api/src/lib/roomAccess.ts is more useful than "the room access logic".
Explain why, not just what. The reader can read the code for what. Document the reasoning that is not in the code — why esu calls back into the API, why the socket ingress is separate, why anansi is a singleton.
Keep code excerpts short and real. Copy from the source rather than paraphrasing; a stale snippet is worse than none.
Line length ~100 characters, matching Prettier's 120 with margin.
Keeping it current
Update the docs in the same pull request as the change:
| You changed | Update |
|---|---|
| A route | The relevant API Reference page |
| An environment variable | Environment Variables |
| A socket event | Socket Events |
| The schema | Domain Models |
| A deployment manifest | Kubernetes |
| A service's behaviour | That service's page |
If you fix one of the defects documented here, remove the callout in the same PR.
Deployment
The site builds to static HTML and is served by nginx on port 8080 in a distroless-style Alpine image, running as the non-root nginx user.
./scripts/build-docs.sh [tag]
kubectl apply -f infra/k8s/docs-deployment.yaml # first time only
kubectl rollout restart deployment/playpalz-docs| Property | Value |
|---|---|
| Image | registry.digitalocean.com/playpalzproduction/docs |
| Manifest | infra/k8s/docs-deployment.yaml |
| Replicas | 2 |
| Port | 8080 |
| Host | docs.playpalz.gg |
| Resources | 10m CPU / 32Mi requested — the smallest workload in the cluster |
./scripts/build-all.sh includes docs.
Restricting access
The docs describe internal architecture, known defects, and operational procedures. The ingress has commented-out basic-auth annotations ready to enable:
htpasswd -c auth <username>
kubectl create secret generic docs-basic-auth --from-file=authThen uncomment the three auth-* annotations in infra/k8s/ingress.yaml and re-apply. Worth doing before the site is publicly reachable.
First-time setup
- Add a
docsA record —dns.tfalready contains it whenmanage_dns = true, otherwise create it manually pointing at the LoadBalancer IP. ./scripts/build-docs.shkubectl apply -f infra/k8s/docs-deployment.yamlkubectl apply -f infra/k8s/ingress.yaml- Watch
kubectl get certificateuntilplaypalz-docs-tlsreports Ready.
Notes on the build
.dockerignoreexcludes**/*.mdfor the service images, with an explicit!apps/docs/**/*.mdexception — without it the docs image would build an empty site.- nginx
try_files $uri $uri.htmlis what makescleanUrlswork: VitePress emitsgetting-started.htmlbut links to/getting-started. - Hashed assets get a one-year immutable cache; HTML must revalidate so a redeploy is picked up immediately.
