Skip to main content

invariant

ci

https://maximo000.github.io/invariant/

Declare what must stay true. Get more than pass/fail back.

Most CI tells you a build is green. It rarely tells you why it trusts that, or leaves anything behind for someone else to check without re-running it themselves. invariant runs a list of named checks against real systems and writes a proof bundle next to the report — evidence, not just a verdict.

invariants:
  - name: no_negative_payments
    check: sql
    args: {dsn: prod.db, query: "SELECT COUNT(*) FROM payments WHERE amount < 0", must_equal: 0}

  - name: latest_backup_restores
    check: postgres_restore
    args: {dump: backups/latest.dump}

  - name: repo_security_controls_fire
    check: security_scan
    args: {repo: .}
$ invariant run invariant.yaml --evidence proof/
  [PASS] no_negative_payments               0.00s  'SELECT COUNT(*)...' = 0
  [FAIL] latest_backup_restores             4.31s  firedrill restored the archive and found problems
  [????] repo_security_controls_fire        0.00s  carabiner is not installed (pip install carabiner-sec)

  1 passed, 1 failed, 1 unverified

  evidence written to proof/

Three statuses, not two

A check that could not run — the tool isn't installed, Docker is down, a credential is missing — is not the same fact as a check that ran and found a problem. Folding both into "fail" throws away the difference; folding the first into "pass" is worse, because now the report is lying. invariant keeps unverified as its own status everywhere, and it fails the build, same rule as firedrill and carabiner: an invariant nobody could check is not one you get to call satisfied.

Why another one

Because the interesting part isn't the runner — it's what backs each check type. invariant doesn't reimplement backup verification or security scanning; it wraps tools that already do those honestly:

  • postgres_restore shells out to firedrill, which restores a Postgres dump into a disposable, version-matched container and proves it's usable — rather than trusting that a backup job exiting 0 means anything.
  • security_scan shells out to carabiner, which runs multiple scanners against a repo, ratchets existing findings, and reports only what's new.
  • sql is the trivial case, built in: one query, one expected scalar. stdlib sqlite3 needs nothing extra; a postgres:// dsn uses psycopg if pip install invariant[postgres] put it there, and reports unverified rather than crashing if it didn't. Not every invariant needs a whole tool behind it. See examples/recur_invariants.yaml for two real ones run against recur's actual Postgres schema — "no subscription has a negative amount" and "every logged price change actually changed the price" — neither enforced by a schema constraint, which is the point.

Four repos that individually prove "this backup works" or "this repo is secure" become one config file that proves all of it, with one report and one evidence directory, and a fifth check type is one new file in checks/, not a rewrite.

Install

Not on PyPI yet -- install from a release or straight from git. Once published it'll be pip install invariant-verify (PyPI blocks the plain name "invariant" -- see pyproject.toml -- but the command below and the invariant import stay the same either way):

pip install git+https://github.com/MaXiMo000/invariant
                                 # + `pip install firedrill` / `carabiner-sec`
                                 # for the checks that need them

Use

python examples/make_demo_db.py         # or --break negative_payment / orphan_order
invariant run examples/invariant.yaml --evidence proof/

--evidence DIR writes manifest.json (name, status, timing, sha256 per check) plus one <name>.json per check holding its raw evidence — the exact command run, stdout/stderr, exit code, and the wrapped tool's own report where there is one. Enough for someone else to see what actually happened without taking the exit code's word for it.

Add a check type

A check is a function args: dict -> (status, detail, evidence) where status is "pass", "fail", or "unverified". Add the module under invariant/checks/, register it in invariant/checks/__init__.py. That's the whole extension point — the runner, evidence writer, and CLI don't change.

What's deliberately not built

No plugin SDK, no YAML schema validator, no dashboard, no signing of the evidence bundle (a sha256 catches an edited file; a real signature is a separate, later problem). No http, migration_diff, or reconcile check types yet — they're the natural next ones, added the same way as the three above, when there's a real invariant to run them against.

MIT licensed.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

invariant_verify-0.1.1.tar.gz (12.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

invariant_verify-0.1.1-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

Details for the file invariant_verify-0.1.1.tar.gz.

File metadata

  • Download URL: invariant_verify-0.1.1.tar.gz
  • Upload date:
  • Size: 12.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for invariant_verify-0.1.1.tar.gz
Algorithm Hash digest
SHA256 21b493b7d6d6b240934ecfef5e9f6e7a0de6a3de831b26140c0636551da26d36
MD5 3350a292a53d49a5f040a1ce081a45e9
BLAKE2b-256 d5e9de2ab849ded09ace246daec4884d54efe2eb7fec64377bc063df48959cda

See more details on using hashes here.

Provenance

The following attestation bundles were made for invariant_verify-0.1.1.tar.gz:

Publisher: release.yml on MaXiMo000/invariant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file invariant_verify-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for invariant_verify-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f9ed0eb3929c167070135205cd0d9a7ecc6c374f63cd0e0c312fbb7569465def
MD5 3a7ba30ee60eb42f147efd4b8df47e58
BLAKE2b-256 5502ed79d3e4bab4739f43657f52546488e60e1a81334ee290659caa6c4636c9

See more details on using hashes here.

Provenance

The following attestation bundles were made for invariant_verify-0.1.1-py3-none-any.whl:

Publisher: release.yml on MaXiMo000/invariant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page