No description
  • Rust 97.7%
  • Nix 2.2%
  • PLpgSQL 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
JMARyA 846b37a281
feat(mir-api): cover the whole API the browser client uses
mir_client carried its own hand-written copy of every request and response
shape. mir-api now has them all, so the client can depend on it instead:
sessions (login/logout/password), users and tokens, admin reload, barcode
and location detail, lots, instance history (Transition), shopping list,
condition warnings, order/arrival, lend/return, transit, damage/repair,
retire/lost/found, unexpire, split/merge, correct/revert, flow runs with
notes, demand by id, and price/usage/spend statistics.

Model types are PartialEq, and responses Serialize, so a UI can use them as
props and cache them. Role is ordered (an unknown role sorts lowest), State
predicts the state machine (checked against the server's table), and an API
error displays as Mir's own message; status() and is_unauthorized() /
is_forbidden() expose the rest. A client without a token sends no
credentials, which is what login needs.

every_client_call_speaks_the_servers_language runs each new call against
the real router.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:24:49 +02:00
.moira/envs ci: migrate pipelines to moira 2026-08-27 23:19:43 +02:00
crates feat(mir-api): cover the whole API the browser client uses 2026-10-20 23:24:49 +02:00
deploy/kubernetes feat: serve the API under /api, leaving the domain to the UI 2026-10-20 18:17:17 +02:00
docs feat: mir-operator SDK and flow-gap, a reference operator 2026-10-20 23:05:17 +02:00
example feat: webhooks — wake operators when the ledger grows 2026-10-20 22:13:20 +02:00
migrations feat: webhooks — wake operators when the ledger grows 2026-10-20 22:13:20 +02:00
operators/flow-gap feat: mir-operator SDK and flow-gap, a reference operator 2026-10-20 23:05:17 +02:00
src feat(mir-api): cover the whole API the browser client uses 2026-10-20 23:24:49 +02:00
.gitignore rewrite: carve-off engine, layered itemdb, db-backed auth, pinned docs 2026-06-12 23:14:11 +02:00
Cargo.lock refactor: split mir-api out of mir-operator, buildable for wasm 2026-10-20 23:12:54 +02:00
Cargo.toml refactor: split mir-api out of mir-operator, buildable for wasm 2026-10-20 23:12:54 +02:00
devenv.lock fixes 2026-06-15 23:39:20 +02:00
devenv.nix chore: rename CDB to Mir 2026-10-18 23:16:31 +02:00
devenv.yaml fixes 2026-06-15 23:39:20 +02:00
flake.lock feat: nix builds 2025-12-14 17:34:57 +01:00
flake.nix refactor: split mir-api out of mir-operator, buildable for wasm 2026-10-20 23:12:54 +02:00
README.md feat: mir-operator SDK and flow-gap, a reference operator 2026-10-20 23:05:17 +02:00
renovate.json Add renovate.json 2025-06-28 00:00:18 +00:00
zensical.toml docs: align docs with Mir identity and actual behavior 2026-10-20 14:58:26 +02:00

Mir

Mir is a primitive ledger of the material economy. It tracks the flow of things — what exists, where it is, where it came from, where it went.

What does Mir mean? In Russian, мир is the village commune. It is also the world, and peace. Rust people will tell you it's an IR, and they're right too: Mir is the Material Intermediate Representation — the form the real economy takes between what exists and what operators do with it. And in German, mir means to me — as in "das gehört mir", that belongs to me — which is the whole point of a self-hosted ledger. All of them are right.

What it is

  • Small. Five primitives — items, states, transactions, locations, flows — that compose into whatever inventory situation you have.
  • Scale-invariant. One person's pantry, a shared household, a maker space, a community.
  • API-first. Mir is infrastructure. Automation and integrations live outside as operators and talk to it over HTTP. The API describes itself: GET /api/openapi.json is a generated OpenAPI 3.1 document covering every endpoint, so clients are generated rather than guessed.
  • Yours. Self-hosted, no cloud, no analytics. The data about what you own and consume belongs to you.
  • Federated (eventually). Sovereign instances exchanging items and demand signals, peer to peer.

Operators in Rust get a head start from crates/mir-operator — a typed client plus a reconcile loop — and operators/flow-gap, a reference operator that turns planned flow runs into demand.

The full documentation lives in docs/ — concepts, architecture, data model.

Running

Mir is a single binary plus PostgreSQL. An instance is configured by a config.toml and a set of definition directories (itemdb sources, locations, flows) — see Storage. Users and tokens live in the database, not in files.

Development

devenv up                              # starts postgres (and provides nightly rust)
cargo run -- example/config.toml       # migrate + serve on 127.0.0.1:8080

Create the first admin (also works as lockout recovery):

cargo run -- example/config.toml bootstrap <name>

Log in with the printed one-time password via POST /auth/login, then manage users and operator tokens through the API — see Users.

The API lives under /api — the rest of the domain is left to the UI, mir_client. The full surface is at GET /api/openapi.json; GET /api/health is liveness and GET /api/ready also checks the database.

Deployment

The container image is built with nix — no Dockerfile:

nix build .#containerImage
skopeo copy docker-archive:result docker://registry.example.com/mir:latest

deploy/kubernetes/ is a deployment template, one manifest per resource (apply with kubectl apply -f deploy/kubernetes/): itemdb sources are cloned by git init containers (fetching and pinning repos is the deployment's job — core only reads directories), config lives in a Secret, and a minimal postgres is included for small instances.