Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion projects/ProvinceSystem/DEPLOY.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ the owner of both checkouts. Deploys to the same site run one at a time.

1. Refuses commits not on `origin/main` (exit 4) and, on www, a checkout with uncommitted changes to tracked files
(exit 1). On dev, `git checkout --detach` keeps the expected local edits and refuses if they would be overwritten.
2. Backs up `province.db` with SQLite's backup API inside the backend container as `province.db.before-deploy-<sha>-<UTC time>`, keeping the newest 10.
2. Backs up `province.db` with SQLite's backup API inside the backend container as `province.db.before-deploy-<sha>-<UTC time>`, keeping the newest 10. When the compose file has a `db` service, it starts it and writes `postgres.before-deploy-<sha>-<UTC time>.dump` (`pg_dump --format=custom`), also keeping 10.
3. Rebuilds only what changed: `frontend/`, `shared/` or `docker-compose*` rebuild the frontend; `backend/`,
`shared/`, `frontend/lib/skins/` or `docker-compose*` rebuild the backend (`up -d --build --no-deps`).
Other changes only move the checkout.
Expand Down Expand Up @@ -118,6 +118,23 @@ files out of images, so a local `docker-compose.staging.yml` run gets only the s
sync to the site go to `backend/src/data/plugin-assets/`, inside the data mount. Both compose files run the backend and
frontend with `init: true`; in the backend it reaps the renderer's Chromium processes.

### The site's Postgres

Each compose project has a `db` service (`pgvector/pgvector:pg17`) holding Precedent and patch notes, in the
volume `provincesystem_db` (www) or `tfmc-staging_db` (Dev). It has no host port; the backend reaches it as `db`
and waits until it is healthy. Each site's password is `POSTGRES_PASSWORD` in its project's `.env`
(`/home/tfmc/ProvinceSystem/.env` or `/home/tfmc/tfmc-staging/.env`, mode 600; hex, as it goes into a URL), and www's
`backend/.env` sets
`DATABASE_URL=postgresql://provincesystem:<password>@db:5432/provincesystem`. Dev's compose file builds `DATABASE_URL`
from the same password, and its override repeats it because its `environment: !override` replaces the list.
Postgres reads `POSTGRES_PASSWORD` only when it creates the volume; to change the password later, run
`ALTER USER provincesystem PASSWORD '…'` in `psql` and update both files.

```bash
sudo -u tfmc ps-compose main exec db psql -U provincesystem provincesystem # a shell
sudo -u tfmc ps-compose main exec -T db pg_restore -U provincesystem -d provincesystem --clean --if-exists < backup.dump
```

## Keys and secrets

| Secret | Where | Used by |
Expand Down
22 changes: 12 additions & 10 deletions projects/ProvinceSystem/docs/integrations/precedent.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ similar incidents get similar rulings. Bot never decides punishments — it's ad

1. Staff run `/case-log` after handling an incident (players, rule broken, evidence
summary, ruling, punishment) → cog POSTs to ProvinceSystem → ProvinceSystem embeds
the case (Voyage AI) and stores it in Supabase Postgres (`pgvector`).
the case (Voyage AI) and stores it in the site's Postgres (`pgvector`).

**Only the summary is embedded** (`_case_text` in `precedent_routes.py`). A search
query is always a description of what happened: staff cannot cite the rule number
Expand Down Expand Up @@ -117,9 +117,10 @@ Implementation: `backend/src/api/precedent_routes.py`, `backend/src/precedent/{d

## Audit trail

Every write to `precedent_cases` appends a row to `precedent_audit` in the same Supabase
database. There is **no API route to read it** — inspecting it requires direct Supabase
access, so staff cannot review or erase their own trail from the website.
Every write to `precedent_cases` appends a row to `precedent_audit` in the same database.
There is **no API route to read it**: inspecting it takes database access (the root-only
[Database studio](../ops/database-studio.md) or `psql` on the host), so staff cannot review or
erase their own trail from the website.

| Column | Meaning |
|--------|---------|
Expand Down Expand Up @@ -161,8 +162,9 @@ corpus). Ids that already exist are skipped rather than overwritten, so the scri
re-run and safe to use while the corpus is only partly damaged. Restores are themselves
audited, as `action='restore'`, `source='script'`.

**This only covers deletions made through the API.** Direct SQL against Supabase bypasses the
audit trail entirely — Supabase's own backups are the recovery path for that.
**This only covers deletions made through the API.** Direct SQL bypasses the audit trail; the
`postgres.before-deploy-*.dump` files that each deploy writes ([DEPLOY.md](../../DEPLOY.md)) are
the recovery path for that.

Useful queries:

Expand All @@ -180,19 +182,19 @@ SELECT before FROM precedent_audit WHERE action = 'delete' AND case_id = '<uuid>

## Storage

Supabase Postgres, `pgvector` extension. Two tables: `precedent_cases` (id, logged_by,
The site's Postgres (the `db` service in each site's compose project), `pgvector`
extension. Two tables: `precedent_cases` (id, logged_by,
players, summary, rule, ruling, punishment, embedding `vector(1024)`, created_at) and
`precedent_audit` (see above). Tables + extension are created lazily on first request
(`migrate()` in `precedent/db.py`).

This is separate infrastructure from the existing SQLite skins/drinks DB — ProvinceSystem
had no Postgres/Supabase usage before this feature.
Patch notes share the same database; the rest of the site is still in SQLite (`province.db`).

## Configuration

| Env (ProvinceSystem `backend/.env`) | Purpose |
|--------------------------------------|---------|
| `SUPABASE_DB_URL` | Postgres connection string (Supabase project) |
| `DATABASE_URL` | The site's Postgres, `postgresql://provincesystem:<password>@db:5432/provincesystem` |
| `VOYAGE_API_KEY` | Voyage AI embeddings |
| `ANTHROPIC_API_KEY` | Claude synthesis |

Expand Down
2 changes: 1 addition & 1 deletion projects/ProvinceSystem/docs/ops/database-studio.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Each site lists the databases it can reach:
|----|------|--------|
| `site` | `province.db`: accounts, characters, wardrobes, chronicle and ledger indexes, the staff audit log | Read and edit |
| `coreprotect` | The CoreProtect SQLite file named by `COREPROTECT_DB` (Main on www, Dev on the Dev site) | Read only |
| `supabase` | The Postgres database in `SUPABASE_DB_URL` (Precedent and patch notes), `public` schema; www only | Read and edit |
| `postgres` | The site's Postgres in `DATABASE_URL` (Precedent and patch notes), `public` schema | Read and edit |

The list shows each database's tables, size and how many audited queries it has had.
Opening one shows its tables with columns and approximate row counts (`max(rowid)` on
Expand Down