- Rust 93.2%
- Shell 3.6%
- Nix 1.9%
- TeX 1.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
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> |
||
| .agents/session-notes | ||
| .cargo | ||
| .forgejo/workflows | ||
| .githooks | ||
| ci | ||
| crates | ||
| fuzz | ||
| scripts | ||
| .editorconfig | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CHANGELOG.md | ||
| character-sheet.yaml | ||
| clippy.toml | ||
| config.example.toml | ||
| CONTRIBUTING.md | ||
| deny.toml | ||
| flake.lock | ||
| flake.nix | ||
| install.sh | ||
| LICENSE | ||
| Makefile | ||
| osv-scanner.toml | ||
| README.md | ||
| rust-toolchain.toml | ||
| rustfmt.toml | ||
| SECURITY.md | ||
| sources.bib | ||
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/kmove the selection and spill into the panel above/below at the edges.Enterrolls 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:
aadvantage,ddisadvantage,+/-adjust DR,rreroll,Escclose. On the Money panel,+/-adjust silver;ssaves.non 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 aCustom…entry that falls back to free text. Added entries mark the sheet dirty;ssaves.
- Notes — a
git log-style chronology of the character's notes (newest first).Enteropens the selected note in$EDITOR(→$VISUAL→vi);ncreates a new dated session note from a template and opens it.tcycles the view density:compact(one line per note, no spacers),standard(a summary truncated tolog.summary_lenchars, default), andcomfy(the full note rendered with light markdown, scrolls to follow the selection). - Meta — manage characters and campaigns together: two panels
(Characters / Campaigns),
h/lto switch focus,j/kto select,Enterto make the selected character or campaign active,pto place the selected character into the selected campaign (creates its notes there for bookkeeping across campaigns), andnto 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 thanh, becausehis the vim "move left" binding; sethelp = ["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-auditablewrites the crate graph into a.dep-v0section duringnix build([profile.release]keepsstrip = "none"so it survives); onesyftpass over the binary's runtime closure (closureInfoinflake.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), acargo fuzztarget on the same oracle, Miri (nightly), Kani scaffolded off. The workspace forbidsunsafeand denies clippy'sallgroup; the restriction lints andpedanticarewarnfor 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.