Files
pirats/README.md
Tim McCarthy 692c6d26c1 Cap request body size (HTTP 413)
LimitRequestBodyMiddleware (pure ASGI, registered outermost) rejects request
bodies larger than PIRATS_MAX_BODY_BYTES (default 1 MiB) before they're buffered
into memory: it checks the declared Content-Length first, then counts the bytes
actually streamed so a chunked/length-omitting client can't bypass the header
check. Exposed as services.pirats.maxBodyBytes and documented in the README.

Tested in isolation (Content-Length fast path + streamed path) and through the
real app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:59:48 -07:00

181 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Rats with Gats Remote Play (`pirats`)
A web-based remote play companion app for the tabletop roleplaying game **Rats with Gats** (where players roleplay as gunslinging pirate rats, or "Pi-Rats", in the magical land of Yeld). The application manages lobbies, character creation, card mechanics, scene phases, obstacles, challenges, and player voting. See [RULEBOOK.md](RULEBOOK.md) for the game rules.
It is built with a **FastAPI** + **SQLModel** (SQLite) backend and a **Svelte 5** single-page frontend (in `frontend/`), which polls the backend's JSON state endpoint for near-real-time updates.
---
## Features
- **Game Lobby:** Create games and join as players via a unique game ID.
- **Character Creation:**
- Fill out the character profile (appearance, smell, catchphrase).
- Delegate custom "Like/Hate" relationship questionnaire cards to other crewmates (answering them for each other!).
- Draft three "Secret Pirate Techniques" which are automatically shuffled and swapped among players.
- Bind swapped techniques to Face Cards (Jack, Queen, King) for play.
- **Dynamic Card Mechanics:**
- Automated 54-card deck shuffling and card drawing.
- Automatically matches card suits to thematic obstacles (Clubs for combat, Spades for stealth/cunning, Hearts for social/morale, Diamonds for nautical/athletics).
- **Scene Management:**
- Assign roles (Pi-Rats or "The Deep" narrator).
- Dynamic challenge creation linking multiple active obstacles.
- Automatically handles card matching colors/values, automatic success when playing Face Card techniques, and Joker card triggers to discard/replace obstacles.
- **Between-Scenes Voting:**
- Vote on crewmates to rank up.
- Toggle personal objectives (Get a Gat, Earn a Name, Die like a Pirate).
---
## Installation & Running
This project supports standard Python package tools as well as Nix flakes.
### Option 1: Standard Python Installation
#### Prerequisites
- Python >= 3.9
- Virtual environment tool (optional but recommended)
#### 1. Setup Virtual Environment (Optional)
```bash
python -m venv .venv
source .venv/bin/activate
```
#### 2. Install Package
Install the package and its dependencies in editable mode:
```bash
pip install -e .
```
#### 3. Run the App
Launch the FastAPI server using the installed package entrypoint:
```bash
pirats --host 0.0.0.0 --port 8000 --reload
```
Alternatively, run it with `uvicorn`:
```bash
uvicorn pirats.main:app --host 0.0.0.0 --port 8000 --reload
```
Once running, access the web UI at [http://localhost:8000](http://localhost:8000).
#### Configuration (environment variables)
| Variable | Default | Purpose |
| --- | --- | --- |
| `DATABASE_URL` | `sqlite:///./rats_with_gats.db` | SQLAlchemy database URL. |
| `PIRATS_LOG_FILE` | _(unset)_ | If set, also write logs to this file (rotating, 10 MB × 5 backups). Logs always go to stderr regardless. |
| `PIRATS_LOG_LEVEL` | `INFO` | Minimum log severity (`DEBUG`/`INFO`/`WARNING`/`ERROR`/`CRITICAL`). |
| `PIRATS_DEV_MODE` | _(unset = on)_ | Default Dev Mode for new games. Unset is treated as a local checkout (on); set to `0`/`false` to disable. |
| `PIRATS_PURGE_ENABLED` | `true` | Periodically purge old finished/inactive games. |
| `PIRATS_PURGE_INTERVAL_HOURS` | `24` | How often the purge task runs. |
| `PIRATS_PURGE_FINISHED_DAYS` | `14` | Purge games that finished more than this many days ago. |
| `PIRATS_PURGE_INACTIVE_DAYS` | `30` | Purge games with no activity for this many days. |
| `PIRATS_MAX_BODY_BYTES` | `1048576` | Reject request bodies larger than this (HTTP 413). |
---
### Option 2: Using Nix Flakes
If you use the Nix package manager, this repository provides a complete environment.
#### 1. Enter the Development Shell
Includes Python, all runtime dependencies, and testing tools (`pytest`):
```bash
nix develop
```
#### 2. Run the Application
Run the package directly from the flake:
```bash
nix run . -- --host 0.0.0.0 --port 8000
```
#### 3. Build the Application Package
```bash
nix build .
```
The executable will be located in `./result/bin/pirats`.
---
## Deploying as a NixOS Service
The Nix flake exposes a NixOS module that configures the application to run as a systemd service with appropriate sandboxing.
Add this flake to your system configuration and configure the service:
```nix
services.pirats = {
enable = true;
host = "127.0.0.1";
port = 8000;
databasePath = "/var/lib/pirats/rats_with_gats.db";
logFile = "/var/log/pirats/pirats.log"; # rotating; stderr also goes to the journal
logLevel = "info";
openFirewall = false; # Set to true to open ports in the firewall
purge = {
enable = true; # periodically remove old finished/inactive games
intervalHours = 24;
finishedDays = 14; # purge games finished more than 14 days ago
inactiveDays = 30; # ...or with no activity for 30 days
};
};
```
The service writes its log to `logFile` (under the systemd-managed `/var/log/pirats`
`LogsDirectory`, so the sandboxed `DynamicUser` can write to it) and additionally
to the systemd journal via stderr (`journalctl -u pirats`).
---
## Running Tests
Tests are written using `pytest`.
### Running with Pip
```bash
pytest
```
### Running with Nix
```bash
nix develop --command pytest
```
---
## Project Structure
```
pirats/
├── pyproject.toml # Python package metadata and dependencies
├── flake.nix # Nix package (incl. frontend build), dev shell, NixOS service module
├── tests/
│ └── test_game.py # Game logic and CRUD test suite (pure-crud, no HTTP for most)
├── frontend/ # Svelte 5 + Vite SPA
│ └── src/
│ ├── pages/ # Routes: Home, Join, Dashboard (game UI), Admin
│ ├── components/ # One component per game phase, Card.svelte, and scene/ (ScenePhase sub-panels)
│ └── lib/ # api.js (fetch wrapper), cards.js (card display helpers), suggestions.js (Suggest-button pools)
└── src/
└── pirats/ # FastAPI backend package
├── main.py # App setup, /api/game create/join/state endpoints, CLI entrypoint
├── models.py # SQLModel tables (Game, Player, Obstacle, Challenge, Vote, GameEvent)
├── cards.py # Card parsing, deck building, obstacle table from the rulebook
├── crud.py # Facade re-exporting all crud_* modules
├── crud_*.py # Game logic by phase: base (deck/hands/rank), character, scene, challenge, upkeep
├── routes_*.py # Thin API routers by phase, mounted under /api
└── static/ # Built frontend lands here (gitignored; populated by the Nix build)
```
### Frontend Development
Run the backend (`pirats --reload`) and the Vite dev server side by side; Vite proxies `/api` to port 8000:
```bash
cd frontend && npm install && npm run dev
```