{"slug": "show-hn-laptop-is-the-last-place-your-secrets-are-still-in-plaintext", "title": "Show HN: Laptop is the last place your secrets are still in plaintext", "summary": "Jitpass released jit, a macOS-only (Apple Silicon) developer tool that moves plaintext secrets from files like .env and ~/.aws/credentials into a local encrypted vault gated by Touch ID, rewriting files so tools keep working while leaving a decoy on disk. The tool, installable via Homebrew or curl, requires a biometric prompt before handing credentials to a process or AI agent, and is signed and notarized by Apple. It is still in development.", "body_md": "**Just-in-time credentials for your dev machine.**\n\n** Documentation** ·\n\n[Quickstart](/jitpass/jit/blob/main/docs/getting-started/quickstart.md)·\n\n[Supported tools](/jitpass/jit/blob/main/docs/tools.md)·\n\n[Command reference](/jitpass/jit/blob/main/docs/reference/commands/jit.md)·\n\n[Security](/jitpass/jit/blob/main/docs/security/architecture.md)\n\nStatus:macOS-only (Apple Silicon), and still in development.\n\nYour secrets live in plaintext all over your machine: `.env`\n\nfiles,\n`~/.aws/credentials`\n\n, `~/.zshrc`\n\nexports, `.npmrc`\n\ntokens, MCP configs. Anything\nrunning as you can read them. A bad `curl | sh`\n\n, a sketchy `npm install`\n\n, or one\nof the AI agents now running in your editor with your full permissions.\n\n`jit`\n\nmoves each secret into a local encrypted vault gated by Touch ID, and\nrewrites the files so your tools keep working. On disk there's now a decoy. The\nreal value only appears, in memory, for the specific process that asked for it,\nafter a biometric prompt. The result: you unlock once, `jit`\n\nasks before handing\na credential to a tool (or an agent), and there's a decoy on disk the rest of\nthe time.\n\n| launched by Code | launched by claude |\n|---|---|\n\n```\nbrew install jitpass/tap/jitpass\n```\n\nOr without Homebrew:\n\n```\ncurl -sL https://dl.jitpass.com/jitpass/jit/releases/latest/download/jitpass_darwin_arm64.tar.gz | tar -xz jit\nsudo mv jit /usr/local/bin/\n```\n\nApple Silicon only — on an Intel Mac, build from source with\n`go install github.com/jitpass/jit/cmd/jit@latest`\n\n.\n\nPick one route. If you installed from the tarball before and are switching to\nHomebrew, remove the old copy after the `brew install`\n\n(`sudo rm /usr/local/bin/jit`\n\n); otherwise two jits sit on PATH upgrading separately, and\n`jit doctor`\n\nwill flag it.\n\nReleases are signed with a Developer ID and notarized by Apple, so both paths\nrun without a Gatekeeper prompt: Homebrew quarantines its downloads and\nGatekeeper clears them against the notarization ticket, while `curl`\n\n(and `go install`\n\n) set no quarantine flag at all. To check what you got rather than\ntake our word for it, run `jit doctor`\n\n— its `jit`\n\nline reports\n`signed CZC6BH93GJ`\n\n, using the same check `jit upgrade`\n\nruns before it will\ninstall anything.\n\n**Upgrading:** `brew upgrade jitpass`\n\n, or `jit upgrade`\n\n— a verified\nself-update (Developer-ID signature and checksum both checked before the\nswap, restarts the service). Either way your vault is untouched.\n\nHomebrew installs shell completion with the binary, so `jit <TAB>`\n\ncompletes\nsubcommands, flags, vault paths, and wrappable tool names out of the box.\nInstalled from the tarball or from source, add it yourself:\n\n```\necho 'source <(jit completion zsh)' >> ~/.zshrc && exec zsh\n```\n\nEither way, `jit doctor`\n\ntells you if completion isn't reaching your shell.\n\n```\njit scan                            # read-only. changes no file it scans, prints no real value.\njit vault init                      # make the vault (master key in your login keychain)\njit migrate --dry-run               # preview the whole machine-wide fix plan\njit migrate                         # apply it: shows plan, asks [y/N], one Touch ID\njit migrate ~/code/myapp            # or fix just one project\njit run -- npm run dev              # run your tool; real values injected into that process only\n```\n\n`jit scan`\n\nwith no path sweeps your whole home directory, so give it a moment on\na large one. To go straight at one place, point it at a path: `jit scan ~/.aws`\n\n.\n\nDay to day it's mostly `jit run -- <cmd>`\n\n. For CLIs that carry their own login\ntoken (`gh`\n\n, `glab`\n\n, `stripe`\n\n, and more) you `jit wrap gh`\n\nonce and then keep\ntyping `gh`\n\nas normal forever.\n\nNot sure whether something needs `jit wrap`\n\n, `jit migrate`\n\n, or nothing? You\ndon't have to know. `jit scan`\n\nsplits everything it finds into what jit will\nprotect (one command - the wraps included) and what only you can fix, and\nbare `jit migrate`\n\nruns that whole plan:\n\n``` bash\n$ jit scan\n  YOUR SECRETS: 7 — 0 protected by jit (0%)\n  ▱▱▱▱▱▱▱▱▱▱  to 100%: one command +71% · 2 secrets only you can fix +29%\n\n  jit will protect these — 5 secrets in 4 files, 0% → 71%\n      → jit migrate\n        ~/.zshrc            STRIPE_API_KEY, DB_PASSWORD\n        ~/.config/gh/hosts.yml  GitHub CLI token · wraps gh\n        ...\n\n  only you can protect these — 2 secrets, 71% → 100%\n\n    [rotate, then delete every copy]\n    ! A production database password in 2 files\n      → rotate it now, then delete every copy\n```\n\n(`jit scan --full`\n\nstill gives the classic per-category inventory with\nseverities, including the **Wrappable CLI Tokens** section.)\n\nMigrate the credential once, then keep using the tool the way you always have.\n\n```\n# AWS (and Terraform, and every AWS SDK)\njit migrate ~/.aws/credentials       # keys move to the vault; no plaintext file left\naws s3 ls                            # resolves from the vault on demand. no prefix, no flag.\nterraform apply                      # same creds, same command\n\n# GCP application-default credentials (a machine-wide credential)\njit migrate ~/.config/gcloud/application_default_credentials.json\nterraform apply                      # google provider reads ADC; works after a Touch ID prompt\n\n# Docker / docker-compose\njit migrate ~/.docker/config.json    # registry logins move to the vault\njit run -- docker compose up         # jit injects them for this run\ndocker login ghcr.io                 # still works; the helper stores to the vault\n\n# Shell exports that used to sit in ~/.zshrc\njit migrate ~/.zshrc                 # leaves a one-line hook; new shells just have the vars\n./deploy.sh                          # scripts that read those vars work unchanged\n\n# Tokens you once typed at the prompt, now sitting in your shell history\njit migrate ~/.zsh_history           # each one moves to the vault; your commands stay, the secrets don't\njit guard history                    # and stop the next one being recorded at all (zsh)\n                                     # (bare `jit migrate` offers this too, in the plan it asks you to confirm)\n\n# A CLI that carries its own token (gh, stripe, glab)\njit wrap gh                          # one time\ngh pr list                           # token injected per call, forever\n```\n\nThe first time each tool reaches for a real credential, `jit`\n\nasks once and\nremembers your answer until the vault locks. See [Two Touch ID moments](#two-touch-id-moments-not-one)\nfor how that sits on top of the vault unlock, what `--trust`\n\ndoes, and how to\nturn the per-tool prompts off.\n\nWhy do some tools need no setup while others take a `jit run`\n\n? One rule: **can\nthe tool ask jit for the secret itself?** AWS (via `credential_process`\n\n), your\nshell at login, and docker's registry logins (via a credential helper) all can,\nso you type nothing extra. Tools that only read a file at runtime (docker\ncompose, plain SDKs) can't ask, so `jit run`\n\nhands them the value.\n\nThe machine-global credential files (GCP ADC, `sops`\n\n, `npm`\n\n, `netrc`\n\n) work the\nsame everyday way: run your tool and approve the per-process prompt. Add `jit run --with <name>`\n\nonly when you want it explicit: for scripts and CI where there's\nno prompt to answer, or when you want a hard gate a project's own config can\nnever reach. ** Supported tools** lists exactly what to type\nfor every tool, and how each is delivered.\n\n`jit`\n\nasks for your fingerprint at two different moments, doing two different jobs:\n\n**Unlocking your vault.** The first time you use`jit`\n\nafter it locks, one Touch ID opens the vault for the whole session (5 minutes of activity, then it re-locks; and never longer than 8 hours, however busy you are). You unlock once, not once per command.**Handing a credential to a tool.** On top of that, the first time a given tool reaches for a real credential,`jit`\n\nasks before handing it over and names what's asking. This is what stops a program you didn't run from quietly using your keys while the vault is open.\n\n``` php\n$ aws s3 ls\n  Touch ID  ->  unlock your vault              # gate 1: opens the vault for 5 min\n  Touch ID  ->  aws wants your aws credential   # gate 2: this tool, this credential\n  ...your buckets...\n\n$ aws s3 cp ./file s3://bucket/   # same tool, same session: no prompt\n\n$ terraform apply\n  Touch ID  ->  terraform wants your aws credential   # a different tool: it asks on its own\n```\n\nGate 2 is what keeps an unlocked vault from being a free-for-all: even after\nyou've used `aws`\n\nyourself, a sketchy `npm install`\n\nreaching for those same keys\nstill triggers a prompt naming it, so you can say no.\n\nDon't want the second gate? Turn it off; the vault lock stays (turning it off itself takes a Touch ID, since it reopens the window it closes):\n\n```\njit service consent off   # tools resolve silently while the vault is unlocked\njit service consent on    # ask per tool again (the default)\n```\n\nKicking off something that needs several credentials at once? `jit run --trust -- terraform apply`\n\napproves that whole run's tools in one gesture. Full details:\n[per-process consent](/jitpass/jit/blob/main/docs/service/consent.md).\n\nBoth gates assume a human is there to answer. An AI agent working overnight, a\nlong build, a scheduled job: the screen locks, the session drops, and the run\nstalls on a prompt nobody will see. A **process grant** moves your decision\nearlier instead of removing it - one Touch ID, given while you're still there,\nthat names exactly what you're signing:\n\n``` php\n$ jit grant --process claude --profile jamf --for 8h\n  Touch ID  ->  let claude under iTerm2 use 2 secrets (jamf) unattended for 8h\n✓ granted g-7f3a2c81   claude -> jamf   until 17:42\n  └ covers claude under iTerm2: 1 running now, any started before 17:42\n```\n\nFor the next 8 hours, every `claude`\n\nunder **the terminal you typed that in**\n(and what it launches) gets those secrets with no prompts - through screen lock\nand all, including sessions you start later: a new tab, the next `claude`\n\n, a\nscript that fires at 3am. It's your terminal being named, not a name being\ntrusted: a program calling itself `claude`\n\nsomewhere else on the machine\ndoesn't descend from that tree and inherits nothing. The grant ends at its\ndeadline, when you quit that terminal, or the moment you type `jit grant revoke`\n\n(which needs no fingerprint - taking access away is always free). Want\none exact process instead, gone when it exits? `--pid`\n\n. Every serve lands in\nthe audit trail as its own event, so the morning after you can read exactly\nwhat your agent touched while you slept. Full details:\n[process grants](/jitpass/jit/blob/main/docs/service/grants.md).\n\nEvery jit command and every unlock lands in a durable log you read back with\n`jit audit`\n\n, newest first, one `key=value`\n\nline per event, so it greps like a\nreal service log. Command arguments are masked, so the log proves a command ran\nwithout ever storing the secret it carried.\n\n``` bash\n$ jit audit --since 1h\ntime=2026-07-24 10:15:04 level=info kind=cmd status=ok dur=312ms cmd=\"jit migrate ~/.aws/credentials\" user=meni parent=claude\ntime=2026-07-24 10:16:22 level=info kind=use op=\"read a secret\" cmd=\"aws s3 ls\" parent=claude secrets=aws/default\ntime=2026-07-24 10:31:09 level=warn kind=unlock status=denied method=touchid-or-passcode cmd=\"node postinstall.js\" parent=npm secrets=aws/default\n```\n\nThe middle line is the story jit exists to tell: `aws/default`\n\nwas read by `aws s3 ls`\n\n, launched by `claude`\n\n. The last is a prompt you declined: a `node postinstall.js`\n\nunder `npm`\n\nreaching for those same keys, refused. jit also logs\nwhat the service turned away at its socket (a process the kernel says isn't\nyours, probing the agent) as `kind=error`\n\n.\n\nNarrow it with flags instead of grep: `--kind`\n\n, `--status ok|failed|denied`\n\n,\n`--since`\n\n/`--until`\n\n(an age like `2h`\n\n/`3d`\n\nor a date), `--parent claude`\n\n,\n`--secret aws`\n\n, `--user`\n\n, `--grep <regexp>`\n\n. Add `--follow`\n\n(`-f`\n\n) to stream new\nevents live like `tail -f`\n\n, or `--format json`\n\nfor a machine-parseable dump. Both\nhalves are durable files beside the vault, so it answers for last week as readily\nas the last hour.\n\n`.env`\n\nfiles, shell exports, AWS and Terraform, kubeconfig, Docker registry\nlogins, GCP ADC, `.npmrc`\n\n/ `.netrc`\n\ntokens, MCP server configs, bare token\nfiles, credentials recorded in your shell history, wrappable CLIs (`gh`\n\n,\n`stripe`\n\n, `vercel`\n\n, …), and SSO CLIs that mint credentials at login\n(`clisso`\n\n). In every case the file keeps working and the real value comes\nfrom the vault on demand.\n\nThe full catalog, grouped by exactly what to type for each tool, is\n** Supported tools**: it tracks the code as tools are added or\nremoved. Anything not listed can still be wrapped with\n\n[.](/jitpass/jit/blob/main/docs/wrap/custom-tools.md)\n\n`jit wrap add`\n\n`jit`\n\nnever destroys a credential. Migrate **moves** the value into the vault and\nleaves a **working hook** where it was (a decoy `.env`\n\n, an `eval \"$(jit export)\"`\n\nline in your shell config, `credential_process = jit …`\n\nin `~/.aws/config`\n\n, or a\n`PATH`\n\nshim), so your tools keep resolving it on demand. The credential still\nexists, just encrypted instead of sitting in plaintext.\n\nAnd every change is reversible. Before touching a file, jit backs it up encrypted\ninto the vault, so `jit migrate undo`\n\nputs it back byte-for-byte:\n\n```\njit migrate ~/code/myapp        # applied the fix, one Touch ID\n# changed your mind, or something broke?\njit migrate undo ~/code/myapp   # every touched file restored, byte-for-byte\n```\n\nThe docs live under ** docs/**, organized by task:\n\n: setup, migrating, living with the fix, step by step[Quickstart](/jitpass/jit/blob/main/docs/getting-started/quickstart.md): the vault, the service, mounts, and shims in one page[How it works](/jitpass/jit/blob/main/docs/getting-started/how-it-works.md): developer and security questions, answered bluntly[FAQ](/jitpass/jit/blob/main/docs/faq.md): what the per-tool prompts do, and how to tune or turn them off[Per-process consent](/jitpass/jit/blob/main/docs/service/consent.md): pre-approve a running tool to work unattended for a bounded, revocable, audited window[Process grants](/jitpass/jit/blob/main/docs/service/grants.md): read back every command, unlock, and refusal, filterable and followable[Audit trail](/jitpass/jit/blob/main/docs/reference/commands/jit_audit.md): every command and flag, generated from the CLI[Command reference](/jitpass/jit/blob/main/docs/reference/commands/jit.md): the threat model and the honest limits[Security architecture](/jitpass/jit/blob/main/docs/security/architecture.md): build/test setup; sign-off via DCO ([CONTRIBUTING.md](/jitpass/jit/blob/main/CONTRIBUTING.md)`git commit -s`\n\n), no CLA\n\n[PolyForm Perimeter License 1.0.0](/jitpass/jit/blob/main/LICENSE) - free for personal and internal\ncompany use only.", "url": "https://wpnews.pro/news/show-hn-laptop-is-the-last-place-your-secrets-are-still-in-plaintext", "canonical_source": "https://github.com/jitpass/jit", "published_at": "2026-08-16 06:55:04+00:00", "updated_at": "2026-08-16 07:10:35.453580+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-safety"], "entities": ["Jitpass", "jit", "Homebrew", "Apple"], "alternates": {"html": "https://wpnews.pro/news/show-hn-laptop-is-the-last-place-your-secrets-are-still-in-plaintext", "markdown": "https://wpnews.pro/news/show-hn-laptop-is-the-last-place-your-secrets-are-still-in-plaintext.md", "text": "https://wpnews.pro/news/show-hn-laptop-is-the-last-place-your-secrets-are-still-in-plaintext.txt", "jsonld": "https://wpnews.pro/news/show-hn-laptop-is-the-last-place-your-secrets-are-still-in-plaintext.jsonld"}}