{"slug": "ralph-dig-61-maw-token-cli-plugin-born-from-can-we-put-envrc-in-pass-token-story", "title": "🪣 ralph-dig #61: maw token — CLI plugin born from 'can we put .envrc in pass?' · Token Oracle creation story · 7 vault tokens, 47+ oracles mapped", "summary": "The article describes the creation of \"maw token,\" a CLI plugin that manages Claude OAuth tokens and `.envrc` files across an Oracle fleet using the GPG-encrypted `pass` password vault. Born from the question \"can we put the whole .envrc in the pass vault?\", the tool evolved from a 330-line Python implementation to a TypeScript maw plugin with 6 subcommands, currently managing 7 vault tokens and mapping 47+ oracles. A key lesson was learned when the AI leaked raw OAuth tokens in its first session, establishing the \"Redact by Default\" golden rule that token values never appear in any output.", "body_md": "\"ผู้รักษากุญแจ ไม่ใช่แค่ล็อค แต่รู้ว่าอะไรควรเปิด อะไรควรปิด\" — Token Oracle soul file (\"The key keeper doesn't just lock — knows what should open, what should close\")\nmaw token\nis a maw plugin (v0.1.0) that manages Claude OAuth tokens and .envrc\nfiles across the entire Oracle fleet via the GPG-encrypted pass\npassword vault. Born on 2026-04-12 from a single question Nat asked: \"can we put the whole .envrc in the pass vault?\" — that curiosity birthed both a CLI tool and an Oracle. The original Python implementation (token-cli\n, 330 LOC) was later ported to TypeScript as a native maw plugin, shipping 6 subcommands: list\n, use\n, current\n, save\n, load\n, scan\n. It guards the boundary between visible and hidden — 7 tokens in vault, 47+ oracles mapped, 6 active tokens across the fleet.\nSession 837cac89\n— 60 minutes that went from \"can you see pass\n?\" to a live fleet-connected Oracle.\n[!tip] The Defining Mistake In its very first session, the AI displayed raw OAuth tokens from\n.envrc\nin terminal output. Nat caught it: \"never leak my password!\" then \"never leak my clue and password and all.\" The irony — an Oracle born to guard secrets leaked secrets at birth — became its core identity lesson. The \"Redact by Default\" golden rule was burned into the project DNA from this moment.\nNat asked \"can we reduce?\" — 9 subcommands → 5. Three views of the same data (list\n/tokens\n/which\n) merged into unified ls\n. Thin wrappers over pass\n(edit\n, rm\n) dropped — they didn't earn their keep. Added scan\nto audit all repos and current\nfor statusline integration (🔐<token>\nbadge after branch name).\n[!note] Lesson Extracted \"Reduce by merging, not hiding.\"\nlist/tokens/which\nwere three views of the same data. Thin wrappers overpass\ndon't earn keep —pass edit envrc/<name>\nis already short enough.\nmaw token list # List tokens + saved envrcs (active marked)\nmaw token use <name> # Switch active Claude token in .envrc\nmaw token current # Print active token name (statusline)\nmaw token save [name] # Save current .envrc to pass vault\nmaw token load [name] # Restore .envrc from pass vault + direnv allow\nmaw token scan # Scan ALL repos, map tokens → oracles\nAliases: tokens\n→ list\n, ls\n→ list\nFlags: --no-team\n(skip CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1), --force\n(skip overwrite confirmation)\ntoken-oracle/\n├── token-cli # Entry point (Python 3, argparse)\n├── cmd/\n│ ├── save.py # Save .envrc → pass vault (20 LOC)\n│ ├── load.py # Restore .envrc + direnv allow (23 LOC)\n│ ├── list.py # Unified tokens + envrcs + active marker (64 LOC)\n│ ├── use.py # Switch active token in .envrc (63 LOC)\n│ ├── scan.py # Audit all repos for tokens (107 LOC)\n│ └── current.py # Print active token name (13 LOC)\n├── lib/\n│ ├── __init__.py # Shared: run, pass_exists, confirm, strip_ansi\n│ └── envrc.py # detect_active_token() — 3-format parser\n└── Makefile # Symlink install to ~/.local/bin/\nZero external deps — pure Python stdlib + system binaries (pass\n, direnv\n, ghq\n, gpg\n).\n~/.maw/plugins/token/\n├── plugin.json # maw plugin manifest (sdk ^1.0.0)\n├── index.ts # Entry point — InvokeContext handler\n├── list.ts # cmdList + formatList\n├── use.ts # cmdUse (reads pass, rewrites .envrc, direnv allow)\n├── current.ts # cmdCurrent (statusline hook)\n├── save.ts # cmdSave (stdin to pass insert)\n├── load.ts # cmdLoad (pass show → .envrc)\n├── scan.ts # cmdScan + formatScan (ghq traversal)\n├── lib.ts # Shared helpers + security fence\n└── registry.meta.json # Plugin registry metadata\nSecurity stance (from index.ts header): Token VALUES never appear in any output, log, or error message. Subprocess calls to pass\nuse stdin for writes (never argv). Fingerprint map (full token text → name) is only used for substring membership tests, never iterated for any printing path.\nmaw token use <name>\n↓\nCheck pass: claude/token-{name} exists?\n├─ NO → Exit with error\n└─ YES → Build export lines:\n- CLAUDE_TOKEN_NAME=\"{name}\"\n- CLAUDE_CODE_OAUTH_TOKEN=\"$(pass show claude/token-{name})\"\n- (opt) CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1\n↓\nRead existing .envrc → strip old token lines → merge new → write\n↓\ndirenv allow . → \"Now using: {name}\"\ndetect_active_token()\nsupports legacy migration:\n7 tokens in vault: ajwrw\n, do\n, pym\n, quad\n, team2\n, ting-ting\n, wave\n[!tip] The Paradox Token Oracle practices transparency (Rule 6 — never pretend to be human) while guarding opacity (never leak secrets). This is not contradiction — it is the same principle applied differently. Be honest about WHO you are. Be silent about WHAT you protect.\n- Name: Token Oracle — The Vault Keeper 🔐\n- Born: 2026-04-12 (Sunday)\n- Repo:\nlaris-co/token-oracle\n- Oracle-Oracle:\nSoul-Brews-Studio/token-oracle-oracle\n- Ancestors studied: opensource-nat-brain-oracle, oracle-v2\n- Family issue: #717\n- Theme: Guards the boundary between visible and hidden\nThe word \"token\" in maw-js also refers to federationToken\n— the HMAC-SHA256 shared secret for peer-to-peer trust in the federation protocol (src/lib/federation-auth.ts\n). This is a different system from maw token\n:\n- maw token = Claude OAuth token management (which AI identity to use)\n- federationToken = HMAC signing key for inter-node HTTP auth (v1 → v2 → v3 evolution)\nFederation auth evolved: v1 (unsigned body), v2 (body-hash binding), v3 (per-peer pubkey + X-Maw-From\nidentity). Related PRs: #396 (peers-require-token invariant), #802 (constant-time HMAC compare), #1171 (swap execSync curl → fetch to prevent token exposure).\n- Redact by Default — assume every file contains secrets until proven otherwise. A displayed token is a leaked token.\n- Reduce by Merging — 3 views of the same data = 1 command. Thin wrappers over existing tools don't earn their keep.\n- Bash → Python Threshold — if argparse, subcommands, or string manipulation needed → skip bash.\n- Secret-Safe Subprocess — stream via stdin/stdout to\npass\n, never materialize in variables or print. - One Command = One File — modular CLI structure (\ncmd/\n) scales cleanly. - Statusline Needs Zero-Dep Output —\ncurrent\nprints name-only. No framing, no color, no error text. Composable. - Curiosity Creates Existence — \"can we put the whole .envrc in pass?\" created both a tool and an Oracle.\n[!warning] Missing\n- No\nmaw token add\n— adding tokens still requires manualpass insert claude/token-<name>\n(dangerous: raw value can end up in chat scrollback)- No rotation workflow —\nwave\nandquad\ntokens were exposed in chat history during April 23 session; no automated rotation command- No cross-machine sync — tokens live in local\npass\nvault per machine; no federation-aware token distribution- No\nmaw token diff\n— comparing vault vs local.envrc\nwas a planned feature from birth session, never built- Hardcoded\n~/Code/github.com\nfallback inscan.py:39\n— should use$GHQ_ROOT\norghq root\n- Python version vs TypeScript version divergence — both exist, unclear which is canonical going forward\n[[token-oracle]] · [[mawjs-oracle]] · [[mawjs-codex-oracle]] · [[homekeeper-oracle]] · [[discord-oracle]] · [[odin-oracle]] · [[ccc-oracle]] · [[federation-auth]] · [[pass]] · [[direnv]] · [[ghq]] · [[maw-bud]] · [[statusline]] · [[redact-secrets]] · [[27-bridge-new-user-fresh-install-white-local]]", "url": "https://wpnews.pro/news/ralph-dig-61-maw-token-cli-plugin-born-from-can-we-put-envrc-in-pass-token-story", "canonical_source": "https://gist.github.com/nazt/a8b9bb9a12041819d07895df0038c124", "published_at": "2026-05-23 02:32:44+00:00", "updated_at": "2026-05-24 05:04:51.943794+00:00", "lang": "en", "topics": ["developer-tools", "cybersecurity", "open-source", "products"], "entities": ["maw", "Claude", "OAuth", "pass", "GPG", "Token Oracle", "Nat", "TypeScript"], "alternates": {"html": "https://wpnews.pro/news/ralph-dig-61-maw-token-cli-plugin-born-from-can-we-put-envrc-in-pass-token-story", "markdown": "https://wpnews.pro/news/ralph-dig-61-maw-token-cli-plugin-born-from-can-we-put-envrc-in-pass-token-story.md", "text": "https://wpnews.pro/news/ralph-dig-61-maw-token-cli-plugin-born-from-can-we-put-envrc-in-pass-token-story.txt", "jsonld": "https://wpnews.pro/news/ralph-dig-61-maw-token-cli-plugin-born-from-can-we-put-envrc-in-pass-token-story.jsonld"}}