A terminal-based management console for the pirate-borg rpg game.
  • Rust 93.2%
  • Shell 3.6%
  • Nix 1.9%
  • TeX 1.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Agent 1331889b15
All checks were successful
ci / gate (push) Successful in 25s
ci / deps (push) Successful in 16m49s
ci / static (push) Successful in 19m12s
ci / build (push) Successful in 19m41s
fix(engine): reject 0d6 instead of silently rolling a single die
Port the upstream character-sheet-template fix (889e104): the vendored
copy here still normalized Tok::Dice(0, _) via count.max(1), so a
zero-dice expression like 0d6 silently rolled one die instead of
reporting the typo. Bail with the same message the template uses and
add 0d6 to error_paths regression coverage.

Assisted-by: Agent (claude) <ai@blobfish.icu>
2026-07-23 09:28:32 -04:00
.agents/session-notes docs(docs): retarget prose at the flattened layout 2026-07-16 14:47:55 -04:00
.cargo build(nix): add a reproducible Nix build, toolchain pin, and SBOM 2026-07-20 17:02:35 -04:00
.forgejo/workflows ci(ci): put opengrep on PATH after installer runs 2026-07-21 23:56:52 -04:00
.githooks refactor(repo): make the cargo workspace the git root 2026-07-16 14:45:02 -04:00
ci ci(ci): replace the fmt/clippy/test job with the full security pipeline 2026-07-20 17:02:35 -04:00
crates fix(engine): reject 0d6 instead of silently rolling a single die 2026-07-23 09:28:32 -04:00
fuzz build(security): add cargo-deny, clippy, lints, and a property/fuzz harness 2026-07-20 17:02:35 -04:00
scripts build(hooks): add commit-msg scope enforcement 2026-07-15 23:10:26 -04:00
.editorconfig chore: initialize repository (gitignore, editorconfig, MIT license) 2026-07-01 09:27:28 -04:00
.gitignore build(nix): add a reproducible Nix build, toolchain pin, and SBOM 2026-07-20 17:02:35 -04:00
.pre-commit-config.yaml refactor(repo): make the cargo workspace the git root 2026-07-16 14:45:02 -04:00
AGENTS.md docs(docs): add the security policy, sources, and pipeline docs 2026-07-20 17:02:36 -04:00
Cargo.lock ci(ci): bump deps for vuln scanning 2026-07-21 23:20:38 -04:00
Cargo.toml build(security): add cargo-deny, clippy, lints, and a property/fuzz harness 2026-07-20 17:02:35 -04:00
CHANGELOG.md docs(docs): retarget prose at the flattened layout 2026-07-16 14:47:55 -04:00
character-sheet.yaml docs(docs): retarget prose at the flattened layout 2026-07-16 14:47:55 -04:00
clippy.toml build(security): add cargo-deny, clippy, lints, and a property/fuzz harness 2026-07-20 17:02:35 -04:00
config.example.toml feat(tui): port chord/action layer from character-sheet-template 2026-07-21 23:43:34 -04:00
CONTRIBUTING.md docs(docs): retarget prose at the flattened layout 2026-07-16 14:47:55 -04:00
deny.toml build(security): add cargo-deny, clippy, lints, and a property/fuzz harness 2026-07-20 17:02:35 -04:00
flake.lock build(nix): commit flake.lock for reproducible builds 2026-07-23 09:26:23 -04:00
flake.nix build(nix): add a reproducible Nix build, toolchain pin, and SBOM 2026-07-20 17:02:35 -04:00
install.sh refactor(repo): make the cargo workspace the git root 2026-07-16 14:45:02 -04:00
LICENSE docs(docs): retarget prose at the flattened layout 2026-07-16 14:47:55 -04:00
Makefile refactor(repo): make the cargo workspace the git root 2026-07-16 14:45:02 -04:00
osv-scanner.toml ci(ci): allow two transitive ratatui advisories in osv-scanner 2026-07-21 23:54:48 -04:00
README.md docs(docs): add the security policy, sources, and pipeline docs 2026-07-20 17:02:36 -04:00
rust-toolchain.toml build(nix): add a reproducible Nix build, toolchain pin, and SBOM 2026-07-20 17:02:35 -04:00
rustfmt.toml refactor(repo): make the cargo workspace the git root 2026-07-16 14:45:02 -04:00
SECURITY.md docs(docs): add the security policy, sources, and pipeline docs 2026-07-20 17:02:36 -04:00
sources.bib docs(docs): add the security policy, sources, and pipeline docs 2026-07-20 17:02:36 -04:00

pirate-borg

A Cargo workspace of tools for running a Pirate Borg character from the terminal.

crates/engine sheet-engine: vendored primitives (dice, roll, search, slug)
crates/core   shared library: model, dice, XDG store, config, creation tables
crates/tui    pirate-borg-tui — full-screen terminal manager
crates/cli    pirate-borg     — command-line manager over the character set

These tools target the five-ability variant of Pirate Borg (Strength, Agility, Presence, Toughness, Spirit). character-sheet.yaml at the repo root is the annotated blank sheet — the field reference for the Sheet model.

Storage

Characters live in an XDG data directory (default $XDG_DATA_HOME/pirate-borg/, i.e. ~/.local/share/pirate-borg/):

characters/<slug>.yaml    one sheet per character
ACTIVE_CHARACTER          symlink -> the active character's sheet
ACTIVE_CAMPAIGN           symlink -> the active campaign directory
campaign/<name>/notes/<slug>/   per-character notes within a campaign

(Older installs used ACTIVE and campaign/ACTIVE; those are still read as a fallback.)

Configuration is read from $XDG_CONFIG_HOME/pirate-borg/config.toml (see config.example.toml), including an optional campaign_dir that points the TUI's Notes tab at a campaigns container outside the default data directory.

Install

./install.sh           # cargo install both binaries, then `pirate-borg init`

install.sh builds and installs pirate-borg and pirate-borg-tui to ~/.cargo/bin, then runs pirate-borg init to scaffold the data directory (characters/, a default campaign) and write a documented config.toml, so both tools work on first launch. To build without installing:

cargo build            # or: cargo build --release
cargo test

Campaigns

Notes are organised per campaign under <data_dir>/campaign/<name>/notes/<slug>/. A default campaign is created on first run.

pirate-borg campaign list
pirate-borg campaign new "The Cursed Tide"   # creates + activates
pirate-borg campaign use default

In the TUI press c to open the campaign switcher (j/k select, Enter switch, n create a new one, Esc close).

TUI — pirate-borg-tui

# First run: pass a sheet to import into the store and open it.
cargo run -p pirate-borg-tui -- path/to/sheet.yaml

# Later: no argument opens the active character.
cargo run -p pirate-borg-tui

Three tabs, cycled with Tab:

  • Sheet — a 2x2 grid of panels: Sheet (abilities), Skills (class abilities + spells), Inventory (weapons/items/containers), and Money. Move focus with h/j/k/l (vim-like, configurable); j/k move the selection and spill into the panel above/below at the edges. Enter rolls the selected entry:
    • an ability or skill -> a d20 test popup showing the roll, your modifier, the DR and the success class (fumble / failure / success / critical);
    • a weapon -> its damage dice. In the popup: a advantage, d disadvantage, +/- adjust DR, r reroll, Esc close. On the Money panel, +/- adjust silver; s saves. n on the Skills or Inventory panel opens a picker of known options — the class's features and spells (Skills) or the starting-weapons table with damage (Inventory) — plus a Custom… entry that falls back to free text. Added entries mark the sheet dirty; s saves.
  • Notes — a git log-style chronology of the character's notes (newest first). Enter opens the selected note in $EDITOR (→ $VISUALvi); n creates a new dated session note from a template and opens it. t cycles the view density: compact (one line per note, no spacers), standard (a summary truncated to log.summary_len chars, default), and comfy (the full note rendered with light markdown, scrolls to follow the selection).
  • Meta — manage characters and campaigns together: two panels (Characters / Campaigns), h/l to switch focus, j/k to select, Enter to make the selected character or campaign active, p to place the selected character into the selected campaign (creates its notes there for bookkeeping across campaigns), and n to create a new campaign. The active character and campaign are marked *.

Every action's key is shown on screen: the header shows the campaign switch key, the status bar lists the active keys for the current tab, and the selected rollable entry shows a faded "roll" hint. Press ? for a full help panel listing all keybindings (loaded live from the config), and g to open the config file itself in $EDITOR (settings reload on save).

Colours and every keybinding are configurable via config.toml — see config.example.toml. Anything the loader has to refuse (a character bound to a finder control, an unparseable file) is appended to a diagnostics log under $XDG_STATE_HOME/pirate-borg/ rather than printed — printing would corrupt the screen the TUI is already drawing. A highlighted status-bar notice says so, and v opens the log's tail.

Help defaults to ? rather than h, because h is the vim "move left" binding; set help = ["h"] in [keys] if you'd rather use it.

Note: Pirate Borg has no native advantage/disadvantage; difficulty is set by the DR. Advantage/disadvantage here is a convenience house-rule (roll 2d20, keep the higher/lower), alongside the DR +/- control which is the rules- native lever.

CLI — pirate-borg

cargo run -p pirate-borg-cli -- <command>
command what it does
list list stored characters (* marks active)
show [slug] show a character (default: active)
use <slug> set the active character
import <path> import a sheet file and activate it
classes list creatable classes
roll <expr> dice roller, e.g. roll "2d6 x 10"
new [--class <id>] [--name <name>] interactive step-by-step creation
new --auto --class <id> roll a whole character in one shot
deps [--refresh] what this build is made of: every crate, its known advisories and its source repo's activity score

Interactive creation

pirate-borg new walks the rulebook order: choose/roll a class, roll each ability (3d6 -> modifier, plus class adjustment), hit points, Devil's Luck, a class feature or Sorcerer spell, a starting weapon, background (with the correct per-background silver), and the flaw/trademark/idiosyncrasy/incident/thing-of- importance tables, then name and save. At every prompt: Enter accepts, r rerolls, s skips (where optional), or type a value to set it manually. With no TTY (piped input) every prompt accepts its roll, so printf '' | pirate-borg new --class sorcerer --name X rolls a full character.

The creation tables in crates/core/data/creation.yaml are a paraphrased reference drawn from the rulebook; no verbatim text or scans are included.

Security & supply chain

The build and CI are aligned with character-sheet-template: the release artifact is built by Nix, its SBOM is generated from the build rather than scanned off a finished binary, and the provenance is signed and independently checkable. This reaches SLSA Build L2 in form (signed provenance from a hosted runner) but not L3 (the cosign key is readable by the release job); SECURITY.md states the gap and is the process half of CRA compliance.

Out of the box:

  • One-pass CycloneDX 1.6 SBOM. cargo-auditable writes the crate graph into a .dep-v0 section during nix build ([profile.release] keeps strip = "none" so it survives); one syft pass over the binary's runtime closure (closureInfo in flake.nix) then catalogs both the native store packages and the Rust crates — one tool, both halves.
  • Code scanning every PR (.forgejo/workflows/ci.yml): cargo-audit, cargo-deny, osv-scanner, cargo-machete, cargo-geiger, rustfmt, clippy, opengrep.
  • Dynamic + static analysis: proptest properties over the engine primitives (crates/engine/src/props.rs), a cargo fuzz target on the same oracle, Miri (nightly), Kani scaffolded off. The workspace forbids unsafe and denies clippy's all group; the restriction lints and pedantic are warn for now.
  • Reproducibility enforced at release (nix build --rebuild), signed tags required, and every SBOM pushed to Dependency-Track.
cargo ci                          # fmt, clippy, test, deny, audit
nix build .#sbom && jq . result   # the CycloneDX SBOM (native + Rust)
nix build .#cli                   # the auditable release binary

CI runs on a Forgejo runner labelled default: each job runs in a Node container and installs Lix (ci/install-nix.sh). nix build and the release pipeline need Cargo.lock and flake.lock committed.

Development

cargo ci      # fmt --check + clippy + test + deny + audit (the local PR gate)
make hooks    # install pre-commit hooks

See CONTRIBUTING.md. CI (Forgejo Actions) now runs the full security pipeline above — supply-chain scanning, static/dynamic analysis, a Nix build, and the signed SBOM/SLSA release — replacing the old fmt/clippy/test job.

Licence and rulebook material

The code here is MIT-licensed (see LICENSE). This is an unofficial, fan-made helper. Pirate Borg is copyright Limithron LLC; MOERK BORG is copyright Ockult Oertmaestare Games and Stockholm Kartell. No rulebook scans or verbatim text are included — the creation data under crates/core/data/ is a paraphrased reference for the tools.