Design decisions
The full records are in docs/decisions/. Each one covers context, decision, alternatives and consequences.
| ADR | Decision |
|---|---|
| 0001 | Workspace layout, beholder-* names, pinned toolchain; beholder-ipc and beholder-e2e added; the local data/ folder is never committed. |
| 0002 | rusqlite on the server too, instead of sqlx: one SQLite build, faster compiles, one serialized connection. |
| 0003 | Wire format: flag byte + postcard, zstd above 1 KB, 4 MiB cap, version in Hello, append-only evolution. |
| 0004 | Sync: per-campaign event log, per-recipient filtering through Outgoing, dedupe by UUIDv7 for 8 days, snapshot fallback, optimistic view = confirmed + pending. |
| 0005 | Auth: Argon2id (19 MiB), hashed opaque tokens, rotation with a grace period, rate limits, open registration. |
| 0006 | Images: header checks before decoding, capped decode, lossy WebP thumbnail + display size, content-addressed. |
| 0007 | Client networking: system TLS (native-tls), ureq for HTTPS, connection states and backoff. |
| 0008 | Desktop: no router, Tauri IPC without wrappers, raw-body uploads, img:// image cache, Fluent, keyring. |
| 0009 | Deployment: static musl image on scratch, compose memory limits, Caddy HTTP/1+2, CI-built installers. |
| 0010 | Documentation with Astro Starlight, local only, on a user-local Node. |
| 0011 | Notes: per-user filtering and frame sharing, reveal as upsert/removal, snapshot catch-up across visibility changes, conflict copies, protocol v2, sanitized Markdown with pulldown-cmark. |
| 0012 | Compendium: SRD vendored from 5e-srd-api (5e-database is archived), dev-only beholder-xtask converter, committed reproducible bundle, served immutable with Range, FTS5 search on the client. |
| 0013 | Open5e: per-book bundles converted from a pinned commit (raw data not vendored), generated OGL notices, per-campaign sources (SRD included), game-system tag, name-clash priority with ‘Also in’, protocol v3. |
| 0014 | Server library: author or any DM edits, stale saves refused, notice over WebSocket and changes over HTTP, library as top-priority source, per-campaign hidden entries (protocol v4), JSON/YAML files with serde_norway. |
| 0015 | Large library imports: SaveMany batches of 500, library bundle streamed to disk and rebuilt only when over 1,000 changes behind, raw-body file import, dnd-data converter, versioned file format with a JSON Schema. |
| 0016 | Undoable library imports: an ImportId per import sent with ImportMany batches, the server records each entry’s previous log position, UndoImport deletes created entries and restores replaced ones but keeps entries edited since, by the importer or any DM. |
| 0017 | Importing JSON of any shape: native file picker (tauri-plugin-dialog), streaming reader that never holds the file, mapping profiles stored in the config folder and exportable, suggestions from the first 20 entries, partly mapped entries kept, summary review before importing. |
| 0018 | Setup import from an external repository: BEHOLDER_IMPORT_REPO, archive downloaded over rustls and streamed through tar and gzip, the dnd-data converter moved into the beholder-dnd-data crate, one undoable import by a server actor, run once. |
| 0019 | Character sheets: rules as pure functions in core, targeted change commands with full-state CharacterSaved events, full sheets for owner and DM and summaries for others filtered on the server, PROTOCOL_VERSION 5. |
| 0020 | Computed spell and weapon effects: core::actions parses spell and weapon texts into structured info (checks, damage, scaling), computes effects for the character as Roll values the dice system will roll, parsing done in the shell. |
| 0021 | Dice: core::dice parses and rolls notation with an injected random source, the server rolls with OS randomness, public and to-DM visibility filtered on the server, a 50-roll log in the campaign snapshot, PROTOCOL_VERSION 7. |