An ephemeral desktop client onto your AWS secrets — Secrets Manager Sets and remote
.envfiles reached over SSM. It stores no secrets and no credentials of its own — it borrows them on demand and forgets them. The name is the thesis: the janitor holds the most keys, yet keeps none.
License: GPL-3.0-only · Status: v0.1.4 released (Linux ·
macOS · Windows MSIX with auto-update). The masked drift matrix reads real AWS —
Secrets Manager Sets and remote .env files over SSM (offline mock behind
JANITOR_MOCK=1). Both write engines are built and tested but the app ships
read-only by default, with the write paths reachable only via human-gated
live-verify binaries (details below) · CI: lint · test · coverage
Janitor is a cross-platform desktop tool designed for two jobs that the AWS console makes awkward and risky:
- Drift detection — line up the same logical Secret Set across N Environments (prod / staging / dev — possibly different AWS accounts and regions) in one masked matrix, so missing or mismatched Entries jump out.
- Safe mutation — change a few Entries without ever risking an accidental overwrite of the whole Set.
By design it is an ephemeral client: Values and Credentials are kept in memory only and zeroized after use; the only thing written to disk is non-secret Config — where Secret Sets live, never a Value. The domain vocabulary (Secret Set, Entry, Value, Environment, Application) is defined in CONTEXT.md.
The dangerous operation in AWS Secrets Manager is the everyday one. A Secret
Set's value is a single JSON blob, so "change one Entry" easily becomes a
PutSecretValue of a whole in-memory blob that silently drops a teammate's
concurrent edit. Janitor's reason to exist is to make that structurally
impossible — every write is designed to go through an op-based,
replay-on-fresh-fetch, atomic compare-and-swap engine
(ADR 0001), never
a naive overwrite. The drift matrix is the other half: see the holes — the
Gap finding — before they page someone.
This is how the matrix reads in the running GUI (see Status).
Janitor compares Values masked: it shows presence, Value length, and equality grouping (by hash) without revealing plaintext. Each Entry lands in exactly one state:
Entry prod staging dev
───────────────────── ───────── ───────── ─────────
SENTRY_DSN ••• 61 #a ••• 61 #a ••• 61 #a ✓ Aligned
POSTHOG_API_KEY ••• 47 #b ••• 41 #c ••• 47 #d ~ Drift
ZITADEL_CLIENT_SECRET ••• 36 #e ••• 36 #e — ! Gap
- Aligned — present everywhere with identical Values (same hash group). The healthy, boring case.
- Drift — present everywhere, but Values differ. Sometimes intended (a
per-Environment
DATABASE_URL), sometimes a bug. - Gap — present in some Environments, missing in others. The highest-signal finding — usually a Terraform / compose hole.
••• is the masked Value (plaintext is shown only on an explicit, momentary
per-cell reveal), the number is its length, #x is the hash-equality group
(same letter ⇒ identical Value), and — means the Entry is absent. Value
length is a deliberate, accepted side-channel — see the
threat model.
The GUI is live: it signs in to IAM Identity Center in a browser, runs guided
Discovery, and renders the masked drift matrix from real AWS — across two
Providers (Secrets Manager Sets and remote .env files over SSM) and across
regions. Both write engines are built and tested behind fakes + replay, but the
shipped app stays read-only; the write paths are reachable only via
human-gated live-verify-*-write binaries while live verification finishes.
| Area | State |
|---|---|
| Secret-shape model — comparable Entries + lossless flatten / unflatten | ✅ Implemented & tested — ADR 0008 |
Zeroizing secret types — Value kept out of Debug / Display / logs |
✅ Implemented & tested |
Config load / save — atomic TOML write, locations only |
✅ Implemented & tested |
| Comparison matrix (Aligned / Drift / Gap) + masked read model | ✅ Implemented & tested — ADR 0009 / 0014 |
janitor-gui — masked matrix, per-cell reveal, guided Discovery wizard, Manage window, region picker, in-app diagnostic log |
✅ Live on real AWS (JANITOR_MOCK=1 for offline mock) — ADR 0003 / 0013 / 0015 / 0017 |
| Identity Center sign-in + per-Environment Credentials + GUI↔AWS worker bridge | ✅ Implemented & tested (logic vs. fakes; browser/SDK shell untested by design) — ADR 0002 / 0010 / 0012 |
| Guided sign-in + Discovery — auto-discovered account / role / secret, remembered picks | ✅ Implemented & tested — ADR 0011 / 0026 |
| Secrets Manager Provider (read) | ✅ Live in the matrix |
Remote .env over SSM Provider (read) — pure-Rust MGS data channel, no session-manager-plugin |
✅ Live-verified 2026-06-07 — ADR 0025 |
| Non-stomping Secrets Manager write — staged-put + atomic CAS | ⚙️ Built & tested behind fakes/replay; read-only, reachable via live-verify-sm-write — ADR 0001 |
Remote .env over SSM write — non-stomping, hash-guarded |
⚙️ Built & tested; read-only, reachable via live-verify-ssm-write — ADR 0029 |
| Read-write lock — writes unreachable until deliberately unlocked | ✅ Worker-enforced invariant — ADR 0032 |
| Packaging + releases — Linux deb/rpm/AppImage, macOS dmg, Windows MSIX auto-update | ✅ v0.1.4 — ADR 0007 / 0022 / 0034 |
The workspace is six crates: janitor-core (offline bedrock — model, compare,
Config, the Provider port, the Discovery orchestrator), janitor-gui
(Slint), janitor-aws-auth (shared Identity Center auth base; ADR 0024),
janitor-aws (Secrets Manager Provider), janitor-ssm (remote .env over SSM
Provider; ADR 0025), and janitor-mock (offline canned-data Provider; ADR
0019). cargo test --workspace runs them all; ≥80% coverage gates cover the
non-GUI crates, where correctness is proven (ADR 0010 §5, ADR 0016) — the
browser / SDK / socket shells stay untested by design.
Prebuilt bundles ship on the Releases page:
- Linux —
.deb,.rpm, or.AppImage - macOS —
.dmg(Apple Silicon) - Windows —
.msix, installed via App Installer with auto-update (ADR 0034)
The app ships read-only: it reads and compares secrets but makes no mutating AWS calls. You bring your own IAM Identity Center org — see docs/iam_setup.md.
Standard Cargo across the six-crate workspace. Linux needs a few Slint system dependencies first; full commands (build, test, coverage, the GUI, and the human-gated live-verify binaries) live in docs/building.md.
Crates split along a trust boundary
(ADR 0003). The
security-critical logic lives in core and the Provider crates behind a
Provider port (ADR 0019);
the GUI is a thin, softer-trust view:
janitor-core(trusted) — no GUI deps: the secret-shape model, zeroizing in-memory types,Config, the comparison engine, the write-seam types, the provider-agnostic Discovery orchestrator (ADR 0026), and theProviderport every backend implements. This is where correctness is proven.janitor-aws-auth/janitor-aws/janitor-ssm/janitor-mock(trusted) — the Providers: a shared Identity Center auth base, the Secrets Manager backend, the remote-.env-over-SSM backend, and the offline mock. Network / SDK / socket I/O sits behind seams so the logic stays mockable and the coverage gates stay reachable.janitor-gui(softer-trust) — a thin Slint view: the masked comparison matrix, momentary per-cell reveal, the Discovery wizard, the Manage window, and an in-app diagnostic log. No auth / AWS / compare / write logic lives here.
These are the spine of the project; the threat model explains what each one defends against.
- Nothing secret touches disk — no Values, no Credentials, no SSO-token
cache. Config (locations only) is the only thing persisted. Secret material
lives in zeroizing types and stays out of
Debug/Display/ logs / errors. - Never stomp a Secret Set — all writes go through the op-based, replay-on-fresh, atomic compare-and-swap engine (ADR 0001).
- Read-only by default — mutating AWS calls are unreachable until the user deliberately switches into a lockable read-write mode; v1 ships read-only (ADR 0004).
- Memory-only auth — IAM Identity Center Sign-in each launch; no static keys; role Credentials refreshed silently from the SSO token (ADR 0002).
This README is only the front door — the depth lives here:
- CONTEXT.md — the domain glossary (Secret Set, Entry, Value, Environment, Application, the Aligned / Drift / Gap states). Read this first.
- docs/THREAT-MODEL.md — what Janitor defends against, the explicit non-goals, and the trust boundaries.
- docs/iam_setup.md — set up an IAM Identity Center org
and permission set to run the live
live-verifyharness (Milestone B). - docs/building.md — build, test, coverage, and the human-gated live-verify binaries.
- Architecture Decision Records — every hard-to-reverse choice,
indexed in
docs/adr/README.md. - CLAUDE.md — working agreements and invariants for contributors (and AI assistants).
New hard-to-reverse decisions get an ADR; new domain terms go in CONTEXT.md. See CLAUDE.md for the conventions.
GPL-3.0-only. The GUI builds on Slint under its GPL terms, so the project is GPL throughout (ADR 0003).