ESS Orrery
Documentation

Every command, every check, grounded in the source.

Install, verify the install, write a profile, and read what each of the six reconciler checks declares and proves. Nothing below is illustrative unless it says so, every command and output is real.

pip install orrery && orrery doctor
01

Install

The engines (reconciler, context) are stdlib-only: zero runtime dependencies, Python 3.11 or newer. pyproject.toml declares dependencies = []; the only optional extra is tui, the themed terminal deck (textual>=8,<9), so the core install never has to pull in a TUI framework it doesn't need. Current version 0.1.0; the console entry point is orrery = "orrery.cli:main".

core
$ pip install orrery
optional: the deck
$ pip install 'orrery[tui]'  # themed terminal deck (textual)
from a clone
$ pip install .            # core, from the repository root
$ pip install '.[tui]'    # core plus the deck extra

Either path installs the same thing: a package sourced from PyPI and a package built from a local checkout behave identically, because the core carries no identity of its own. See the licensing model: the core is AGPL-3.0-or-later.

02

Quickstart

Two commands cover the whole loop: verify the install, then measure a box against a declared profile.

1. Let it check its own install

doctor confirms the right Python is present, the package imports, every reconciler check is registered, and every subcommand is reachable. It never asks you to trust a note that says the install is fine.

verify
$ orrery doctor

Real output, captured from a fresh install (your Python and package versions will differ):

  [ok ] python>=3.11: 3.12
  [ok ] stdlib tomllib: present
  [ok ] import orrery: 0.1.0
  [ok ] reconciler checks: all present
  [ok ] import context engine: ok
  [ok ] cli entry point: orrery.cli:main
  [ok ] tui extra (optional): installed

healthy

Exit code is 0 when every probe passes ("healthy"), 1 if any fails ("PROBLEMS FOUND"). The tui probe is the one exception: "not installed (optional)" still counts as a pass. Add --json for a machine-readable result a CI job can check.

2. Reconcile a box against a profile

A fact here is not "declared", it is declared and its probe passes, continuously. Reconcile is report-only: it measures and prints findings, it never changes a box. Run it against the example profile shipped in the repo, unedited: every path in it is a placeholder, so every check reports drift or failure, which is the honest, correct behavior.

the shipped example, unedited
$ orrery reconcile --profile profiles/example.toml

Real output, unedited, run against the example profile as shipped:

reconciler: example

[declared-presence]
  FAIL  /path/that/must/exist: required member is missing  (expected 'present', observed 'absent')
  info  /path/that/should/not/exist/yet: absent as planned

[fleet-reach]
  FAIL  some-box: declared-reachable link is DOWN  (expected 'reachable', observed 'unreachable')
  ok    a-deliberately-closed-box: correctly denied

[floors]
  DRIFT example content floor: floor path is missing  (expected 'present', observed 'absent')
  DRIFT example digest budget: floor path is missing  (expected 'present', observed 'absent')

[managed-settings]
  FAIL  /path/to/an/enforcement/file: required enforcement file is missing  (expected 'present', observed 'absent')
  info  /path/to/a/planned/managed-settings/file: not yet in place (planned)

[org-map]
  warn  org-map: source of truth unreadable: /path/to/orgs.tsv

[secret-edges]
  DRIFT some-store/tokens/example -> /path/to/a/consumer/that/references/it: declared consumer file is missing or unreadable  (expected 'references the secret', observed 'file absent')
  warn  some-store/tokens/example: discover_root does not exist: /path/to/scan/for/undeclared/references

summary: OK=1 INFO=2 WARN=2 DRIFT=3 FAIL=3  ->  DRIFT DETECTED (exit 1)

Every finding came from a live probe, not from a note that once said something was true. Once your own profile names real paths and real boxes, point at it the same way: orrery reconcile --profile my-fleet.toml. Full flags and exit codes are in the CLI reference below.

03

Writing a profile

A profile is a TOML file that declares what a box should be. It is per-installation PROFILE data, not part of the core, and it never leaves your box: write it locally, keep real profiles out of git, since they carry install-specific identity (real hosts, real paths). The engine and its checks do not change between installs, only the profile does. The full, commented reference ships in the repo at profiles/example.toml; everything below is that file, explained field by field.

Top-level fields
schemaInteger format marker for the profile file itself. The example ships schema = 1.
boxA string naming the installation this profile declares, for example box = "example". Printed as the run's identity in output (reconciler: <box>).
[[checks]]One array-of-tables entry per check. Each entry needs an id matching a registered check (org-map, fleet-reach, declared-presence, secret-edges, floors, managed-settings); the rest of its keys are that check's own options.

org-map

Declares one canonical bucket-to-account table and the consumer files that re-encode it in three supported shapes.

org-map block
[[checks]]
id = "org-map"
source = "/path/to/orgs.tsv"                 # tab-separated, header row with bucket/host/account

  [[checks.consumers]]
  path = "/path/to/a/python/consumer.py"
  parser = "python-dict"                     # ast-parse a named dict literal
  symbol = "BUCKET_ACCT"                     # {bucket: (host, account)}

  [[checks.consumers]]
  path = "/path/to/a/bash/consumer"
  parser = "bash-assoc"                      # declare -A NAME=( [bucket]=host/account )
  name = "ACCT"

  [[checks.consumers]]
  path = "/path/to/another/bash/consumer"
  parser = "bash-case"                       # func(){ case "$1" in bucket) echo account;; esac }
  func = "org_of"
  implied_host = "github.com"                # this encoding stores account only; host is implied
source
Path to the source-of-truth TSV. Must have a header row containing bucket, host, and account columns; a header missing any of the three makes the check WARN, not crash.
checks.consumers[].path
File that re-encodes the mapping.
checks.consumers[].parser
One of python-dict (ast-parses a named dict literal, no code executed), bash-assoc (a declare -A block), or bash-case (a shell function with a case statement).
symbol / name / func
The parser-specific handle to look for: the dict variable name, the associative-array name, or the function name.
implied_host
Optional, bash-case only: a host to assume when that encoding stores only the account.

fleet-reach

Declares the ssh edges FROM this box and what each should be.

fleet-reach block
[[checks]]
id = "fleet-reach"
timeout = 5

  [[checks.edges]]
  to = "some-box"
  expect = "ok"                              # reachable as declared; unreachable -> FAIL

  [[checks.edges]]
  to = "a-deliberately-closed-box"
  expect = "denied"                          # unreachable is correct; reachable -> DRIFT (regression)
timeout
Seconds allowed per ssh probe. Defaults to 5 if omitted.
checks.edges[].to
An ssh alias or user@host. Must match a plain-alias shape (no leading dash, no shell metacharacters); anything else is refused, never probed.
checks.edges[].expect
"ok" (must be reachable) or "denied" (must stay unreachable).

secret-edges

Declares each secret by its non-secret identity and the files that should reference it. Never a value.

secret-edges block
[[checks]]
id = "secret-edges"
discover_root = "/path/to/scan/for/undeclared/references"   # optional

  [[checks.secrets]]
  ref = "some-store/tokens/example"        # a vault path or logical name, not the value
    [[checks.secrets.consumers]]
    path = "/path/to/a/consumer/that/references/it"
ref
The secret's identity: a slash-separated vault path (up to 8 short segments) or an UPPER_SNAKE env var name. Checked against a fail-closed allowlist plus a denylist of known secret-value shapes (private keys, GitHub and AWS tokens, Slack, Vault, JWT, Stripe, Resend, Google API keys); anything that doesn't clear the gate is refused and never scanned or echoed.
name
Optional label. When set, it replaces ref in every output line, so even the identity stays out of the report.
checks.secrets[].consumers[].path
A file expected to contain the literal ref.
discover_root
Optional directory to scan (up to 2000 files) for files that reference the secret but were never declared as consumers.

floors

Declares a calibrated floor and/or ceiling for a measured count.

floors block
[[checks]]
id = "floors"

  [[checks.floors]]
  name = "example content floor"
  path = "/path/to/a/directory"
  kind = "file_count"          # file_count (a dir) | line_count (a file)
  min = 100
  recalibrate_ratio = 3.0      # actual > min*ratio -> INFO, floor is stale

  [[checks.floors]]
  name = "example digest budget"
  path = "/path/to/a/file"
  kind = "line_count"
  max = 250                    # above this -> WARN (ceiling exceeded)
kind
file_count counts files under a directory (capped at 100,000); line_count counts lines in a file.
min
The floor. Below it is FAIL, treated as real data loss.
max
The ceiling. Above it is WARN, a budget exceeded.
recalibrate_ratio
Optional. When the actual count exceeds min × recalibrate_ratio, the finding is INFO: the floor is stale and worth resetting.

managed-settings

Declares that an enforcement file must stay root-owned and unwritable by anyone but its owner.

managed-settings block
[[checks]]
id = "managed-settings"

  [[checks.files]]
  path = "/path/to/an/enforcement/file"
  require_owner_uid = 0
  max_mode = "0644"

  [[checks.files]]
  path = "/path/to/a/planned/managed-settings/file"
  require_owner_uid = 0
  max_mode = "0644"
  planned = true
require_owner_uid
The uid that must own the file, usually 0 (root).
max_mode
The most permissive octal mode allowed. "0644" means no group or other write bit.
planned
When true, an absent file reports INFO (a promotion not yet done) instead of FAIL.

declared-presence

Declares required members that must exist, and paths that should stay absent until a planned change lands.

declared-presence block
[[checks]]
id = "declared-presence"
required = [
  "/path/that/must/exist",
]
planned_absent = [
  "/path/that/should/not/exist/yet",         # absent -> INFO; present -> WARN (profile stale)
]
required
A list of paths that must exist. Missing is FAIL.
planned_absent
A list of paths that should stay absent, a hardening step not yet taken. Absent is INFO; present is WARN, because it means the profile itself has gone stale.
04

The six checks

One idea generalized six ways: a thing is not "declared", it is declared AND its probe passes, continuously. Every check is read-only. Findings carry a severity (ok, info, warn, drift, fail); the worst finding across a run decides the exit code.

org-map

Declares one source-of-truth table (a TSV of bucket, host, account) plus every consumer file that re-encodes that mapping.
Proves each consumer agrees with the source. A bucket a consumer maps that the source doesn't list is DRIFT; account comparison is case-insensitive on purpose (different consumers store case differently by design), host is compared only when a consumer encodes one; buckets a consumer legitimately doesn't cover are reported INFO, not a problem.

fleet-reach

Declares the ssh edges that should exist from this box, and whether each should be reachable or deliberately denied.
Proves the live matrix, probed over ssh with a timeout, matches what was declared. A declared-reachable link that's down is FAIL; a deliberately-closed link that has reopened is DRIFT, a security regression. A closed edge going quiet is the failure mode nothing else watches for.

declared-presence

Declares required members that must exist, and paths declared planned-absent, not yet in place by design.
Proves required members are present (FAIL if missing) and planned-absent paths stay absent (INFO while absent, WARN if they appear, since their appearance means the profile is now stale).

secret-edges

Declares secrets by non-secret identity only (a vault path or an UPPER_SNAKE env var name, never a value) and the consumer files that should reference each.
Proves each declared edge holds; a consumer that no longer references the secret, or is missing or unreadable, is DRIFT. With discover_root set, it also surfaces undeclared references: a file that mentions the secret but was never declared as a consumer, also DRIFT. Value-blind by construction: a fail-closed identity gate (an allowlist of vault-path and env-name shapes, backed by a denylist of known secret-value patterns) refuses to scan or print anything that doesn't clear it, so a misdeclared secret value is never grepped across the box or echoed into a report.

floors

Declares a floor and/or ceiling for a measured count: files under a directory, or lines in a file.
Proves the count stays in bounds. Below the floor is FAIL, treated as real data loss; above the ceiling is WARN, a budget exceeded; far above the floor (past min × recalibrate_ratio) is INFO, a floor that has gone stale and is worth resetting. A missing measured path is DRIFT, never reported as a false zero.

managed-settings

Declares each enforcement file's expected owner uid and the most permissive mode it may carry.
Proves the file is present, owned by that uid, and not writable by anyone but the owner, no group or other write bit, so the guard layer cannot be quietly disabled from inside. A file marked planned that isn't in place yet reports INFO, not FAIL, keeping a promotion path visible without failing the run.

05

CLI reference

The whole surface is five subcommands. orrery with no arguments, or orrery --help, prints this same list; each subcommand hands its remaining arguments straight to an existing module, so the CLI itself adds no logic of its own.

orrery <command> [args]
CommandWhat it does
reconcileRun a reconciler profile (drift report).
contextResolve the context for a working directory.
deckLaunch the themed terminal deck (needs the tui extra).
doctorVerify this installation.
versionPrint the version.

reconcile

python -m orrery.reconciler --profile PATH [--ssh HOST] [--json] [--check ID], reached as orrery reconcile .... Report-only: it never changes a box.

reconcile flags
FlagMeaning
--profile PATHRequired. Path to a TOML profile.
--jsonEmit JSON instead of the human-readable text report.
--check IDRun only the named check instead of every check in the profile.
--ssh HOSTMeasure a remote host (an ssh alias) read-only, instead of the local filesystem.
Exit codes
CodeMeaning
0Clean: the worst finding is at or below info.
1Drift or worse: the worst finding is DRIFT or FAIL.
2The profile itself could not be loaded (a TOML syntax error or a bad path). Printed as profile error: ... to stderr, never a raw traceback.

The severity ladder

Ordered worst-last, so the worst finding across a run decides the verdict and the exit code.

Severity, ok to fail
SeverityMeaning
okDeclared and observed agree.
infoAn acknowledged, expected state that is not a problem, for example a planned-absent path that is still absent.
warnA soft issue: a consumer could not be parsed, so it was skipped, not passed.
DRIFTDeclared and observed disagree. The core product signal.
FAILA declared invariant is broken, for example a required member is missing.

context

python -m orrery.context --config C [--cwd D] [--json], reached as orrery context .... Prints the context block to inject for a working directory; exits 2 with context config error: ... if the config cannot be loaded, 0 otherwise. --config is required; --cwd defaults to the process's own working directory.

doctor

orrery doctor [--json]. Verifies this installation (see Quickstart). Exit 0 when every probe passes, 1 otherwise.

deck

orrery deck --profile PATH --context-config PATH. Launches the themed terminal deck. Needs the tui extra; without it, the CLI never imports Textual at all (a lazy import), so the core install runs every other command regardless. The exact message if it's missing: the deck needs the 'tui' extra (textual not installed). install with: pip install 'orrery[tui]', exit 2.

version

orrery version. Prints orrery 0.1.0 and exits 0.

06

Remote reconcile, and the context engine

Reconcile a box over ssh, read-only

Add --ssh HOST to measure a remote box instead of the local filesystem, so the same profile and the same checks run unchanged against any reachable box:

remote, read-only
$ orrery reconcile --profile my-fleet.toml --ssh runtime-01

Every remote command is a read (test, head, find, stat), run under ssh -o BatchMode=yes -o ConnectTimeout=<timeout>, and never raises: a failure returns nothing found rather than crashing, exactly like a local run. Paths must be absolute and are shell-quoted, so a value from the profile can never become a remote flag or get injected into the remote shell. Reads are bounded (large files are capped, file listings are capped) so a large or unusual file cannot stall a run. HOST is an ssh alias; your own ssh config resolves the address, user, and key, so Orrery itself never holds or needs a credential.

The context engine: per-directory isolation

orrery context --config C [--cwd D] [--json] resolves what context should be injected for a given working directory, and the isolation rule is the whole point: a global baseline is always included; when the working directory is outside the declared projects root ("ops" scope), a fleet-wide digest is added; when the working directory is inside it, at least two levels down ("project" scope), only that one project's own state is added, never another project's and never the fleet digest. A project directory that would resolve outside the declared root, for example through a symlink, is refused and never read, not silently followed. That single rule is what stops project context from bleeding across projects.

The context engine, the guided install walkthrough, and the licensing model live on their own pages: see Get started for a step-by-step first run, and the open-core model for what is AGPL and what is commercial.

In service to Life