Files
gupi-osint-board/README.md
T
2026-08-14 12:43:11 +02:00

90 lines
5.8 KiB
Markdown

# GUPI OSINT Board
A standalone, server-backed proof of concept for the Glitch University investigation desktop. Levels are authored data: the repository contains the engine and schema, not a compiled-in case.
The accepted normalized domain model and terminology are specified in [`docs/exhibit-data-model.md`](docs/exhibit-data-model.md). PostgreSQL stores **exhibits**; frontend **widgets** visualize exhibit types.
Planned schema and exhibit work is tracked in [`docs/TODO.md`](docs/TODO.md).
## Run locally
Requires Node 20 or 22 (Node 22 is used by the production container).
The self-contained development stack starts PostgreSQL, runs migrations, and serves the production build:
```bash
docker compose -f docker-compose.dev.yml up --build
```
Open `http://localhost:8787`. PostgreSQL is also exposed on `localhost:5433` for inspection.
For Vite hot reload, start only the database, then run the server and web app natively:
```bash
docker compose -f docker-compose.dev.yml up -d db
cp .env.example .env
npm install
npm run migrate:up
npm run dev
```
Open `http://localhost:5173`; Vite proxies `/api` to port 8787.
## Data and API
The server stores its tables and migration ledger in the dedicated `osint` schema of PostgreSQL. `DATABASE_URL` is required.
The accepted model stores each playable or editable level as an isolated mutable board of normalized exhibits. Immutable template-version boards are cloned to create levels, and mutable levels can be cloned back into new template versions. Assets remain immutable and reusable across those copies.
The running POC still uses the earlier `widgets` and `playthrough_*` tables while the exhibit-schema cutover is prepared. They are explicitly transitional; new domain concepts should follow the exhibit model rather than extending those tables.
The API surface is:
- `GET /api/levels`
- `POST /api/levels` (editor only)
- `GET /api/levels/:id`
- `PUT /api/levels/:id`
- `POST /api/levels/:id/reset`
- `GET /api/health`
The browser also keeps a local emergency copy so a network interruption does not lose an in-progress board.
## Level editor
Set `LEVEL_EDITING_ENABLED=true` and open `/?edit=1`. If the database is empty, this surface creates the first blank level. The target model intentionally makes editing and playing the same operation against a mutable level; authoring additionally exposes “save as template”. The current playthrough overlay remains only until the exhibit-schema migration is complete.
In edit mode, files can be dragged from the desktop onto the board or selected with **Import Document**. Images, PDFs, and text files render inside document windows; unknown formats remain downloadable source files. Extracted evidence becomes an editable folder widget. Its editor controls the title, annotation, contained documents, and each source document's publication time. The default upload limit is 25 MB and can be changed with `MAX_DOCUMENT_BYTES`.
Production defaults editing to disabled. Set `LEVEL_EDITING_ENABLED=true` in `/opt/gu_common/.env.prod` only when the authoring surface should be available. This is a capability switch, not authentication; add authentication before exposing production editing to untrusted users.
## POC exhibit and widget contract
The current POC defines four exhibit families and corresponding frontend widgets:
- **Folder** — a titled, annotated collection. Opening it expands its contained source files to their saved relation-bound board positions; closing it retracts and hides them.
- **Source file** — immutable binary evidence plus user-editable title, publication time, file type, and arbitrary key/value metadata. Image is the first fully visual file renderer. PDF, web capture, email, article, filing, price list, text, and generic file are registered types with a generic fallback until their renderers are built.
- **Note** — investigator-authored interpretation, visually distinct from source evidence.
- **Event** — an investigator-authored “this happened” statement with narrative text, an occurrence time, and normalized links to its supporting evidence. Chronologically ordered events become the emerging story.
The planned **Party** family adds distinct Person and Organization exhibits (businesses are organizations). Their dossier-like widgets reveal normalized evidence associations, but parties remain identity objects rather than special folders.
Folder ownership is stored as a normalized membership. The contained document exhibit owns its expanded `xpos` and `ypos`. For deterministic collapse behavior, one exhibit has at most one owning folder; two exhibits may reuse the same immutable asset when the same source file must appear in multiple folders.
An open folder draws a pale red containment band to each expanded file. Each dated file independently projects a grey line to the temporal index. This allows the player to arrange files until those grey lines are vertical, close the folder, and later reopen the same arrangement.
The frontend widget registry maps an exhibit type, and optionally a document type, to its React visualization. Widgets do not own investigation-domain data.
## Deploy at osint.glitch.university
The production service depends on `gu_common`: it joins the external `gnommo` Docker network, uses `gnommo-db`, and is routed by the shared nginx container. Deploy `gu_common` after its nginx configuration changes, then deploy this repository:
```bash
./deploy.sh
```
The deploy script builds and syncs the application, reads production database credentials from `/opt/gu_common/.env.prod`, executes pending migrations in the `osint` schema, starts `gnommo-osint-board`, and waits for its health endpoint.
## Deliberate POC boundaries
There are no accounts, arbitrary uploads, real-world web browsing, OCR, or collaboration yet. The server data model and provenance fields leave room for those later without making them part of the first playability test.