All projects
Active2026

Tenno Ledger

A local-first desktop application for tracking Mastery-bearing equipment in Warframe.

  • Python
  • Qt
  • SQLite

What it is

Tenno Ledger is a personal, local-first desktop application for tracking Mastery-bearing equipment in Warframe. It runs on my Arch Linux machine, keeps all personal progress in a local SQLite database, and needs neither a Warframe account integration nor an online backend.

Version 0.1 is a complete offline tracker. It ships with a bundled catalog of 848 equipment records built from the Digital Extremes Public Export and the Warframe Wiki, and a Qt interface with a dashboard, a filterable catalog, a per-item editor, and settings for backups and export.

Tenno Ledger dashboard showing tracked equipment Mastery, remaining Mastery, and catalog counts.

The dashboard, on the small demo catalog built from the test fixtures, with example progress.

A principle I kept from the start is that the application only claims what it can actually know. It reports Mastery earned and remaining within the tracked equipment catalog. It does not claim to know total account Mastery, the distance to the next Mastery Rank, or whether an item can currently be obtained: those depend on sources the catalog does not cover, and the dashboard says so explicitly.

The numbers on screen describe the catalog, not the account.

How it works

Two kinds of data that never mix

The application keeps two things strictly apart: a public catalog of equipment and the player’s personal progress.

The catalog is replaceable. It is rebuilt from public sources, versioned by snapshot, and can gain, lose, or rename items between releases. Personal progress is the only data that matters to me and cannot be recreated. So the rule is simple: installing or updating the catalog may never delete or rewrite a progress row.

The database enforces this structurally. The progress table has no foreign key to the catalog, on purpose: progress for an item that the current catalog does not know, or no longer knows, is kept and marked as unresolved instead of being dropped.

What “progress” means

For each item I store the highest rank that actually awarded Mastery, not the item’s current rank. In Warframe an item can be reset to rank 0 with a Forma and levelled again, and it can be sold, but the Mastery it already granted stays. Ownership is a separate, present-tense flag, and no implication is enforced between owning, crafting, gilding, and credited rank.

Tenno Ledger catalog page: a table of equipment with category, Mastery, rank, owned, mastered, and priority columns, with filters above it.

The catalog page with filters and bulk ownership edits. Skana is mastered but no longer owned: credited Mastery and inventory are independent.

Mastery rules as data

Mastery is not uniform across equipment, so each catalog item carries its own Mastery policy instead of the application hard-coding exceptions:

  • points per rank, which depend on the equipment category;
  • maximum rank, 30 or 40;
  • how ranks above 30 unlock: for rank-40 equipment each rank beyond 30 needs two Forma, so the credited rank can be at most min(40, 30 + 2 × Forma);
  • a credit gate: some modular equipment and some companions award Mastery only after being gilded;
  • whether the item counts at all. Exalted weapons, for example, gain ranks but never award Mastery, so they stay visible with no Mastery attached.

Progress is validated against the item’s policy before it is saved, and every rejected value is reported with the rule it broke.

Item details dialog for Kuva Ogris: counted, 100 Mastery per rank, max rank 40, ranks above 30 need two Forma each; credited rank 36 with 3 Forma.

The item editor shows the policy it validates against: rank 36 on a rank-40 weapon is accepted because three Forma unlock up to rank 36.

Offline by default

Version 0.1 makes no network requests at runtime. The catalog is built ahead of time by developer tooling and bundled with the application. When a manual catalog refresh arrives in a later version, it will be an explicit action, and personal progress will still never be sent anywhere.

Under the hood

Tenno Ledger is written in Python 3.14 with PySide6 for the interface and the standard sqlite3 module for storage. requests is used only by the catalog tooling.

A fail-closed catalog pipeline

The catalog combines two public sources: the Digital Extremes Public Export, which is authoritative for item identities, and the Warframe Wiki data modules, which fill in categories, Mastery data, and ranks.

Records are matched by their internal game identifier, never by display name. Candidate matches based only on the name are reported for review and never applied automatically.

The build is fail-closed: every source record must either become a catalog item or carry a reviewed disposition explaining why it does not. A record that fits neither stops the build instead of being silently dropped or guessed. The dispositions live in a reviewed overrides file, and each one records a reason and, where possible, a link to its evidence. A few examples:

  • for Zaws, only the strike awards Mastery; grips and links are components and stay out of the catalog;
  • Sirocco is granted already gilded, so its gilding gate is lifted;
  • a Wiki entry for Corvas Prime reuses the base weapon’s internal name, so a reviewed exception keeps the real item counted;
  • MOAs and other companion types must be gilded before they award Mastery.

The file currently holds 46 reviewed entries across 12 kinds of decision. Coverage gaps I have not resolved yet (Plexus, Grimoire, Mandonel) are declared in the data contract rather than approximated.

A separate report tool runs the same pipeline without writing anything and lists every record that would be discarded, so each release of the catalog can be reviewed before it is built.

Installing a catalog safely

Installing a catalog snapshot follows a fixed sequence:

  1. hash the exact snapshot bytes with SHA-256 and skip the install if that snapshot is already recorded;
  2. parse and validate the complete candidate before touching any table;
  3. compute the difference against the installed catalog;
  4. refuse a candidate that would change the identity of existing items;
  5. refuse a candidate that would deactivate a large share of the installed catalog, unless it carries a reviewed acknowledgement whose count matches the actual reduction;
  6. apply items, aliases, and the snapshot record in a single transaction.

Any failure leaves the previous catalog in place, and none of these steps can write personal progress.

Retrieving sources

All network access lives in one module, the only one allowed to import requests; the parsers stay pure. Every request goes over HTTPS to an approved host, one at a time, with connect and read timeouts, a response size limit, a memory limit on the compressed export index, a bounded number of redirects, and retries only for transient failures.

Every payload is validated before the local source cache is touched. Files are written next to their targets and swapped in atomically, with the provenance manifest written last. The cache is treated as untrusted input even though my own tooling wrote it: paths are confined to the cache directory and each file’s digest is checked before it is parsed.

Personal data

The database schema evolves through numbered migrations, and a timestamped backup is created before the first pending migration runs. Progress can be exported to a versioned JSON file that contains personal state and identity hints, never the catalog. Restoring is deliberately a full replacement, not a merge: the whole file is parsed and validated first, a backup is taken, and only then are the rows replaced.

Tests

The repository has 364 tests, covering the source parsers, normalization, Mastery rules, catalog installation, migrations, backup and restore, and the Qt pages. Tests never contact the network: they run on small checked-in source fixtures, which is also the catalog shown in the screenshots on this page.

Where the project is now

Tenno Ledger is in active development. I started it at the end of August 2026, and version 0.1 is functional: it tracks the full bundled catalog offline, with backups, export, and restore.

The next version adds an explicit, manual catalog refresh from inside the application. It will reuse the same candidate pipeline and the same network policy as the developer tooling, so a refresh inside the app is held to the same checks as a catalog built by hand.

Features that need data beyond the equipment catalog, such as total account Mastery or the distance to the next Mastery Rank, belong to later releases.

The source code is not public.

All projects