Skip to content

Circuit-Stitch/Janitor

Repository files navigation

Janitor

CI core coverage aws coverage aws-auth coverage mock coverage ssm coverage

An ephemeral desktop client onto your AWS secrets — Secrets Manager Sets and remote .env files 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


What it is

Janitor is a cross-platform desktop tool designed for two jobs that the AWS console makes awkward and risky:

  1. 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.
  2. 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 Configwhere Secret Sets live, never a Value. The domain vocabulary (Secret Set, Entry, Value, Environment, Application) is defined in CONTEXT.md.

Why it exists

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.

What drift looks like

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.

Status

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-writeADR 0001
Remote .env over SSM write — non-stomping, hash-guarded ⚙️ Built & tested; read-only, reachable via live-verify-ssm-writeADR 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.

Install

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.

Build & test

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.

Architecture

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 the Provider port 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.

Non-negotiable invariants

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).

Docs & decisions

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-verify harness (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.

License

GPL-3.0-only. The GUI builds on Slint under its GPL terms, so the project is GPL throughout (ADR 0003).

About

No description, website, or topics provided.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors