Skip to content

Language Maturity

Language Maturity — an evolutionary specimen plate of a service as it transitions from organic Python sapling to fully-armored ferric pillar

The rule: organic stuff is Python. Once it hardens, it rusts.

Every service in Sanctum starts its life as a Python script because Python is what you write while you’re still discovering what the service is. The curfew engine didn’t know it needed credits, or homework mode, or guest approvals, or a weekly digest, until the first time somebody’s kid found a loophole. Each of those features was a hot-fix-turned-design-decision, and doing that iteration in Rust — where every schema change costs an hour of borrow-checker yoga — would have meant shipping less, slower, and with fewer features your family actually needed.

But once a service has stopped discovering what it is, the game changes. Now the cost function flips: the rewards of Python (fast iteration) are irrelevant because nothing is iterating, and the costs of Python (no compile-time guarantees, requirements.txt drift, 400 MB of .venv, process that silently OOMs at 3 AM) are paid every single day. That’s the moment to port it to Rust, where the single-binary deploy, the compile-time catches, and the launchd-friendly lifecycle pay off forever afterward.

This page is the doctrine for that transition, plus the tool that tells us which service is where.

organic ← Python, features
│ still evolving
hardening ← velocity slowing
ready ← quiet for months,
│ port scheduled
graduated ← sanctum-rs/services/

Services move left-to-right over time, usually slowly. A service can regress (a dormant service getting a new feature ticket moves from hardening back to organic), and that’s fine — the point of the doctrine is to not rewrite in Rust anything that’s still in motion.

Each stage has a precise, testable definition. No vibes.

StageCriterionVerdict
organic≥ 5 commits touching the main source file in the last 30 daysKeep iterating in Python
hardening1–4 commits in the last 30 days, or activity in the last 90 daysWatch — shape still settling
ready0 commits in the last 30 days and ≤ 2 commits in the last 90 daysPort to sanctum-rs/services/<name>/
portingRust source exists in sanctum-rs/services/, but the binary is not built or the deployed runtime is still the original (Python/Node)Finish the build + cutover, then graduate
graduatedRust binary built and running as the deployed launchd unit on the target host(terminal — precedent)
skipCriticality below mediumStays Python forever — rewrite cost not justified

The thresholds live in tools/rust_readiness.yaml and can be tuned per policy. They’re tuned to be conservative: “hardening” is the default verdict for anything that’s not firmly in either corner, which is the correct bias — do not rewrite what is still changing.

Before a service actually gets ported, it must clear all four:

  1. Velocity floor. The ready signal from rust_readiness must have held for at least 60 days. One quiet month can be a coincidence; two is a trend.
  2. Criticality justifies the rewrite. Low-criticality scripts (a one-off backup utility, a fun demo) stay in Python. Rust buys reliability, and reliability has a price; spend it where it matters.
  3. Interface surface is documented. The HTTP routes, CLI flags, env vars, and on-disk state format must be written down (usually in this architecture section) before anyone opens a Rust editor. If you can’t describe it, you can’t port it.
  4. Behavioural parity tests exist. At minimum, a black-box HTTP/CLI test suite the Python version passes today — which the Rust version will also have to pass before it takes over the launchd slot.

Two services are currently serving production traffic from a Rust binary: sanctum-watchdog and sanctum-proxy. Both are recent and both are worth being honest about.

sanctum-watchdog — Sanctum itself was first committed on 2026-03-14. A shell-script watchdog (~/.sanctum/scripts/watchdog.sh) appeared on 2026-03-20 as part of the Sanctum rebrand. The Rust implementation was imported into the sanctum-rs workspace on 2026-04-09 (commit 83a376a, “Import Mac Mini Sanctum Rust services” — meaning the Rust code already existed in another location before being consolidated). Within four days of the import, the binary absorbed the bandwidth monitor (84027b4) and had its self-health reporting normalised in stack checks (8a0ee66). The Rust process is the live sanctum-watchdog running on the host today.

sanctum-proxy — the cloud-tier model router on :4040. Live process is the Rust binary; deployed copy at ~/.sanctum/sanctum-proxy/sanctum-proxy was last replaced on 2026-04-18 during the smart-routing changes (the Brainy category, the general → council-mlx privacy reroute, the Opus-4.6 → 4.7 rename). Launchd dispatches via ~/.sanctum/scripts/proxy-launcher.sh.

The crucial observation isn’t a timeline — it’s that both rewrites were boring. No surprises, no redesign, no “while we’re at it” features. That’s what you want. If the Rust port is interesting, the service wasn’t ready.

Forward-looking: with Sanctum only weeks old, the doctrine on this page is mostly aspirational. The 30-day and 60-day windows in the readiness rules below assume a system mature enough to have 60 days of quiet for a service to point at. Most services here don’t yet. That’s a feature, not a bug — the rules will become biting as the codebase ages, which is exactly when you want them to bite.

The source of truth is tools/rust_readiness.yaml. As of this writing:

ServiceLanguageCriticalityStage
force-flowPythoncriticalhardening
screen-timePythoncriticalorganic
memory-vault-mcpPythonmediumhardening
sanctum-watchdogRustcriticalgraduated
sanctum-proxyRustcriticalgraduated
sanctum-firewallaRust source / Node runtimehighporting
sanctum-ttsRust source / Python runtimemediumporting

To get the live view, run the tool:

Terminal window
python3 tools/rust_readiness.py # pretty table
python3 tools/rust_readiness.py --json # for the dashboard
python3 tools/rust_readiness.py --strict # exit 1 if any service is "ready" (CI gate)
python3 tools/rust_readiness.py --find living-force # resolve an alias to its service

Services can carry aliases in the registry to reconcile doctrine names with runtime names. The Living Force, for example, is documented on its own architecture page but executes as the Rust binary sanctum-watchdog — the alias means --find living-force lands exactly where it should, and the table shows the alias dimly under the service name so the connection is visible at a glance.

tools/rust_readiness.py is a single-file Python script with no deps beyond PyYAML. It reads the registry, runs git log against each service’s source path in its declared repo, computes loc, and derives a stage per the policy in the YAML. Output is an aligned table when connected to a TTY (with stage colours) and JSON otherwise, so dashboards can ingest it directly.

The intention is for this to be:

  • Cheap to run — no network calls, only local git and file reads. Takes under a second.
  • Cheap to trust — the stage rule is three lines of code in derive_stage(); there’s no ML, no heuristic stack, no per-service override magic beyond an explicit stage_override: graduated field for already-ported services.
  • Cheap to wire up--strict returns exit 1 if any service is ready, which means a nightly cron can page someone when a service quietly finishes cooking.

Future work: surface the table in the Holocron UI so the current state is visible at a glance, and emit a weekly “language maturity report” into Jocasta’s Monday digest summarising which services moved stages this week.

Because the most tempting way to lose a weekend is to rewrite a working service in a language you like more. This doctrine is the speed bump between “I have a weekend” and “I have a production outage.” Every service that is still growing limbs is a service that isn’t ready. Every service that has stopped growing limbs is a service that will pay you back, for years, if you make it boring and ferric. The trap is “just one more turn” — one more refactor, one more Rust crate, one more abstraction that looks clean in the diff and breaks something invisible on the machine. The harness exists to tell you when the turn is actually free.

Python while the shape still moves. Rust once it stops. Watch the tool.