You have API keys and tokens in plaintext on your Mac.
Use JitPass to protect them.
Download for Mac ·
brew install jitpass/tap/jitpass ·
Docs ·
jitpass.com
Free for personal and internal company use · Source-available · No account · No telemetry · Nothing leaves your Mac · Secure Enclave ready · Every change can be undone
What's the number on your Mac? The scan only reads, and changes nothing until you say so.
Get started
The problem ·
Three steps ·
Install ·
The menu bar
Protect
Findings and decoys ·
Your tools keep working ·
Undo anything
Approve
Two Touch ID moments ·
Grants ·
The audit
AI agents
Built for AI agents ·
AI jobs ·
A grant or an AI job?
More
How it compares ·
How it works ·
What it does not do ·
Docs
API keys in .env, cloud credentials in ~/.aws/credentials, tokens in
.npmrc, exports in ~/.zshrc, your shell history, the MCP configs your
agents read. Nothing has to be hacked for them to leak. A compromised npm
package, a trojanized IDE extension or a prompt-injected agent runs as you, so
it can simply open the file.
JitPass moves each secret into a local vault that opens with Touch ID, and leaves a decoy where the plaintext was.
No terminal needed: open JitPass and setup walks you through steps 1 and 2.
Prefer the terminal? The same three steps as commands
jit scan # read-only: every exposed secret, file and line
jit migrate --dry-run # preview the whole fix plan
jit migrate # apply it: shows the plan, asks [y/N], one Touch ID
jit audit # afterwards: every request, and what you answeredjit scan with no path sweeps your home folder; point it somewhere to go
faster (jit scan ~/.aws). Everything the app does is one of these commands.
Download for Mac ·
brew install jitpass/tap/jitpass
Free · No account · Every change can be undone
| Protect | Approve | AI agents |
|---|---|---|
| Decoys on disk A program that reads .env or ~/.aws/credentials without asking gets placeholder values, and the read is logged. |
Every request named The first time a program reaches for a real secret, you see which one and what launched it, then decide. |
AI Jobs Approve a script once. Your AI tool runs it and sees what it prints, never the key. |
| Findings What is still in the open, from 100+ token formats, each with the one thing to do about it. |
Grants for when you are away Let one agent work overnight without prompts, for an hour, a week, or until you revoke it. |
Claude Desktop and Cursor Connect them in one click. They run your AI jobs through a local MCP server, even from a sandbox. |
Your tools keep workingaws, gh, docker, terraform, kubectl and your shell get their secrets the way they always did. |
A full audit trail Every use, unlock, refusal and decoy read, with the program that asked and the one that launched it. |
Keys out of AI transcripts Searches the transcripts and edit history agents keep for copies of your keys and 100+ vendor token formats, and redacts them. |
| A clean shell history A zsh hook keeps any command you type with a token in it out of your history file. |
Undo anything Every file is backed up before it changes. Put one project back, or remove JitPass and get every file back. |
MCP keys out of configs MCP configs hold vault paths instead of keys, and agents' own API keys move to the vault too. |
| A key that never leaves your Mac Keep the vault key in the Secure Enclave, the chip that holds keys and never lets them out. |
Locks when you walk away The vault locks after 5 minutes idle, when your screen locks, and when your Mac sleeps. |
Every agent you use Claude Code, Codex, Gemini, Cursor, Copilot, Cline, OpenCode and Kiro, each named when it asks. |
Your coding agent runs as you, with your shell and your files. One poisoned
README, issue or web page can tell it to cat .env and paste the result
somewhere. With JitPass, there is nothing real in that file to paste.
- A cold read gets a decoy, and the read is logged.
- Every request is named. When an agent runs
awsor an MCP server, you see which program and which agent, then decide. - Keys out of configs. MCP server keys and the agents' own API keys move to the vault.
- Keys out of transcripts. The deep scan finds the copies agents kept, across 100+ token formats, and Redact clears them.
The AI Agents window shows each agent on your Mac: keys in its files, what it can reach, and what it did this week.
Prefer the terminal? The same, as commands
jit migrate ~/.claude.json # MCP server keys move to the vault
jit wrap claude # the agent's own API key too
jit migrate caches # clear copies of vaulted keys from agent transcripts
jit migrate redact # and tokens in them that were never vaulted
jit audit --parent claude # what the agent touchedMore in MCP and AI tools and per-process consent.
Some work needs a key and a script, and you want an agent to do it: export a report, sync a list, call an internal API. Handing the agent the key means it is in the agent's context, its transcript and its sandbox. An AI Job hands it the result instead.
A job is a command you approved, with the secrets it gets. When an AI tool asks for one, the jit service runs it: it decrypts, starts the command in its folder, hides every secret value in the output, and hands back the text.
![]() |
![]() |
| 1. Pick a profile, pick a script, approve with Touch ID | 2. Every job, who ran it, and which AI apps can ask |
Open AI Jobs from the menu bar and press New AI Job…. Pick the profile whose secrets the script needs, then the script, and approve. Connect adds jit to Claude Desktop or Cursor; terminal agents like Claude Code and Codex need nothing.
Prefer the terminal? The same, as commands
$ jit job allow notion-export -- .venv/bin/python export_pages.py
Touch ID -> let AI run notion/export_pages.py with 3 secrets, 1 shown
✓ Approved notion-export · 41 files fingerprintedjit job run notion-export # what Claude Code, Codex or Gemini CLI types
jit mcp install # Claude Desktop, whose Cowork shell can't run jit itself
jit mcp install --client cursor # CursorWhat the tool gets back is the script's own output, then what jit adds:
Exported 42 pages to out/notion_20260925.csv
[jit] new file: out/notion_20260925.csv
[jit] hidden values: noneIf the script had printed its key, the line would read
[hidden: NOTION_API_KEY] in its place, and the count would say so.
Three rules make it safe to leave running:
- Only you approve a job, with Touch ID. An agent can propose one; it arrives as a filled-in sheet you read before anything runs.
- A changed file stops the job. jit fingerprints the folder when you approve it. Edit the script or a library it loads, and the job is refused until you look at what changed and approve it again.
- Commands that hand values back are refused.
python -c,sh -c,env,cat: anything that would just print the secrets it is given.
A job asks for Touch ID on every run by default. --ask never lets it run
while you are away, until you remove it; removing a job never asks. Add
--dry-run to see the whole job without approving anything. Details:
AI jobs. Wondering whether you need a grant
instead? See A grant or an AI job?
An agent working overnight or a 3 a.m. job stalls on a question nobody is there to answer. A grant moves your decision earlier instead of removing it: one Touch ID while you are still there, naming exactly which program may use which secrets, and for how long.
![]() |
![]() |
| 1. Say who, which secrets and how long, then Touch ID | 2. Every grant, how often it was used, and Revoke |
Open New Grant… from the menu bar. Pick the program (every copy started under a terminal or editor, or one running process), tick the profiles it may use, and choose how long: 1 hour to 7 days, or until you revoke it. A grant ends at its deadline, when that one process exits, or when you press Revoke, which never asks: taking access away is always free.
Prefer the terminal? The same, as commands
$ jit grant --process claude --profile myapp --for 8h
Touch ID -> let claude use "myapp" for 8 hours
✓ granted g-7f3a2c81 claude -> myapp until 17:42It covers claude under the terminal you typed that in, through screen lock,
and nothing called claude anywhere else. Swap --for 8h for
--until-revoked and it has no deadline: it holds a key of its own, survives
a restart and a reboot, and ends only on jit grant revoke. Details:
process grants.
Both let an agent work without asking you each time. The difference is whether the agent ever holds the key.
| Grant | AI job | |
|---|---|---|
| The agent gets | The real secret values | Only the script's output, with every value hidden |
| It can run | Anything it likes, with those secrets | One command you approved, in one folder |
| Who it covers | One program: every copy under a terminal, or one process | Any AI tool you connected: terminal agents, Claude Desktop, Cursor |
| If a file changes | Keeps working | Stops until you look and approve again |
| Touch ID | None while it lasts | Every run, or never until you remove it |
| Ends | At its deadline, when its process exits, or on Revoke | When you remove it |
| Use it when | The agent needs the key itself: aws, terraform, an MCP server |
The agent needs a result, not the key, or runs in a sandbox like Claude Desktop |
No new commands to learn. Protect a credential once, then keep typing what you always typed.
The Tools window shows every CLI jit protects, and the proof that it is working: when its key was last read, by what, and whether any other program touched it.
aws s3 ls # AWS and Terraform: from the vault, no prefix, no flag
gh pr list # CLIs with their own token (gh, stripe, glab): wrapped once
docker login ghcr.io # registry logins are stored through a credential helper
jit run -- docker compose up # tools that only read a file get it for one run
./deploy.sh # exports that lived in ~/.zshrc: new shells just have themPrefer the terminal? The Tools window, as commands
jit wrap list # every wrapped tool, and whether its shim works
jit wrap add gh --env GH_TOKEN=wrap-gh/GH_TOKEN # wrap a tool jit doesn't know yet
jit audit --kind use --since 7d # which program read which key this week
jit guard history # keep typed tokens out of your zsh history fileYour shell history stays clean too. Turn on Keep typed secrets out of
zsh history in Settings › Protection, or run jit guard history. A
command you type with a token in it still works, and up-arrow still finds it,
but it is never written to your history file, so it can't end up in a backup
or a dotfiles repo. The hook fails open: if anything goes wrong, the
line is saved as normal and your shell never waits on it.
Supported: .env files, shell exports, AWS and Terraform, kubeconfig,
Docker registries, GCP ADC, .npmrc and .netrc, MCP configs, bare token
files, tokens in your shell history, wrappable CLIs (gh, stripe, vercel
and more), SSO CLIs that mint credentials at login, and the logins CLIs keep
for themselves: gcloud and the Azure CLI (jit wrap gcloud, jit wrap az),
AWS SSO and aws login sessions, kubelogin's tokens (moved to the keychain). The full list, with
exactly what to type for each, is Supported tools;
anything else can be wrapped with jit wrap add.
Other tools keep secrets out of your repo. JitPass is the only one that decides which program on your Mac gets each secret, and it is built for the AI agents that run on it.
| JitPass | 1Password | Varlock | Doppler | dotenvx | |
|---|---|---|---|---|---|
| Asks per program, even while unlocked | ✅ | ❌ | ❌ | ❌ | ❌ |
| Finds plaintext keys already on your Mac | ✅ | 🟡 | 🟡 | ❌ | ❌ |
| Finds your keys in AI agent transcripts, and redacts them | ✅ | ❌ | ❌ | ❌ | ❌ |
A cold read of .env gets a decoy, and is logged |
✅ | ❌ | 🟡 | 🟡 | 🟡 |
| AI can use a secret without ever seeing it | ✅ | 🟡 | ✅ | ❌ | ❌ |
| Local only, no account, no cloud | ✅ | ❌ | ✅ | ❌ | ✅ |
✅ yes · 🟡 partly, see below · ❌ no. From each tool's own documentation, September 2026.
What each mark means
- Asks per program. JitPass names the program and what launched it, and
asks again for a different one. 1Password's local
.envis readable by "every process" while it is unlocked. Varlock, Doppler and dotenvx give the values to whatever you start through their command. - Finds plaintext keys. JitPass sweeps your home folder, shell history
and MCP configs. 1Password's Developer Watchtower finds
.envfiles.varlock scansearches your codebase. dotenvx blocks committing a plaintext.env, which stops a leak but does not find one. - A cold read. JitPass leaves a decoy and logs the read. Varlock's schema holds no values, Doppler keeps no file, and dotenvx's file is ciphertext; none of them logs who read it.
- AI without the key. JitPass runs an AI job and hands back the output. Varlock's credential proxy gives an agent placeholders and adds the real values at the network boundary. 1Password's Secure Agentic Autofill signs a browser agent in to websites without showing it the password; it does not cover API keys or scripts.
- Local only. 1Password syncs through your account, and Doppler is a cloud service. Varlock can encrypt locally, and dotenvx keeps an encrypted file in your repo.
Teams that share secrets across machines need one of the others; JitPass protects the Mac you work on, and works with them.
Already use 1Password? Keep it as your source of truth.
jit migrate links instead of copying: a value
that lives in 1Password is vaulted as its op:// reference, and JitPass
decides which program gets it.
JitPass never destroys a credential. It moves the value into the vault, leaves a working hook where it was, and backs the file up, encrypted, before it touches it.
Changed your mind about all of it? Settings › Reset › Remove JitPass… shows you the plan before anything changes, then runs it with one Touch ID:
- Your files go back to plain files. Every secret is written back where it came from, readable as it was before JitPass.
- Everything JitPass installed is removed: the vault and its key, the background service, its lines in your shell config, its settings and permissions.
Tokens it cleaned out of your shell history and AI caches stay cleaned. If a file cannot be put back, it stops and asks before deleting anything. Moving the app to the Trash is your last click.
Prefer the terminal? The same, as commands
jit migrate undo ~/code/myapp # one project back, every file byte for byte
jit uninstall --restore --dry-run # the whole removal plan, changing nothing
jit uninstall --restore # run itbrew install jitpass/tap/jitpassThat installs JitPass into /Applications with the jit command line inside
it, linked onto PATH with shell completions. Without Homebrew,
download the app,
drag it into Applications and open it: it offers to link jit onto your PATH
and checks for a newer release once a day.
Either way you get the same build, signed with a Developer ID and notarized by
Apple, and Gatekeeper checks it before it first runs. Check it yourself:
jit doctor reports signed CZC6BH93GJ. To update, brew upgrade jitpass,
or the app tells you when a release is out. Installs and updates never touch
your vault.
JitPass needs macOS 14 or later on Apple Silicon. On an Intel Mac, build the
CLI from source: go install github.com/jitpass/jit/cmd/jit@latest.
Only the command line, for a Mac with no app (the weaker path, and why)
curl -sL https://dl.jitpass.com/jitpass/jit/releases/latest/download/jitpass_darwin_arm64.tar.gz | tar -xz jit
shasum -a 256 jit # compare against checksums.txt on the release page
codesign -dv --verify --verbose=2 ./jit # expect: Developer ID, TeamIdentifier=CZC6BH93GJ
sudo mv jit /usr/local/bin/
echo 'source <(jit completion zsh)' >> ~/.zshrc && exec zshcurl sets no quarantine bit, so Gatekeeper never consults the notarization
ticket; the same is true of go install. The binary is still signed and
notarized, so the lines above let you check both, but you have to run them.
Update it with jit upgrade, a verified self-update. Pick one route: if you
switch to Homebrew later, remove this copy (sudo rm /usr/local/bin/jit),
and jit doctor flags two jits on PATH if you forget.
After setup, JitPass is a ring in your menu bar: green unlocked, red locked, amber not set up yet.
Click it to see your vault, your agents and tools, active grants, AI jobs,
today's decoy reads, and what is left to do. Lock Now, New Grant…,
New Scan… and Open Audit are one click away, and every window runs
the same commands as the jit CLI.
Scans run on a schedule too. A scheduled scan that finds something new tells you once, and the result waits in Findings.
Prefer the terminal? The panel, as commands
jit status # the vault, the service, grants and what is protected
jit lock # Lock Now
jit doctor # the Doctor row: what is broken, and the fix
jit scan # New Scan
jit audit # Open AuditMost security tools hand you a number. JitPass hands you what to do, and every number opens the thing it counts.
![]() |
![]() |
| Findings: what is still in the open, and the one fix for each | Decoys: who opened a protected file, and what they got |
Prefer the terminal? The same, as commands
jit scan --deep # Findings: what is in the open, including copies of vaulted keys
jit migrate # protect them
jit migrate caches # clear the copies agents kept
jit migrate redact # redact other tokens in agent transcripts
jit audit --status decoy # Decoys: every read that got a decoyUnlocking the vault once opens it for your session. Handing a secret to
a program asks again, by name, the first time each program reaches for one.
The second question is what keeps an unlocked vault from being a free-for-all:
you used aws a minute ago, and a sketchy npm install reaching for the same
keys still has to ask.
Details: how long a session lasts, and turning the second question off
A session ends after 5 minutes without use, and never lasts longer than 8 hours. The second question is a Touch ID prompt that names the program and what launched it. If the vault is locked when a program first asks, the two are one Touch ID: the dialog says it will also unlock the vault.
Only want the vault lock? Turn off Ask before a tool's first use in
Settings › Protection, or run jit service consent off. Starting something that needs several secrets at
once? jit run --trust -- terraform apply approves that whole run in one
gesture. Details: per-process consent.
Every use, unlock and refusal lands in a durable log, and so does every time a program read a decoy. Arguments are masked, so the log proves a command ran without storing the secret it carried.
Read it from the bottom up. At 07:41 you said no to terraform apply. At
07:59 an npm install script opened ~/myapp/.env and got the decoy. At
08:00 you asked Claude to upload the build, and aws used your key, with
claude named as the program that launched it. Open Audit in the menu
bar shows it; filter by kind, time, or what launched it.
Prefer the terminal? The same log, and its filters
$ jit audit --since 1h --format logfmt
time=2026-07-24 10:16:22 level=info kind=use op="read a secret" cmd="aws s3 ls" parent=claude secrets=aws/default
time=2026-07-24 10:31:09 level=warn kind=unlock status=denied method=touchid-or-passcode cmd="node postinstall.js" parent=npm secrets=aws/defaultPlain jit audit shows the same events as a grouped timeline. Filter with
--parent claude, --secret aws, --status denied or --since 3d, and
stream with --follow.
Where secrets live. Each secret is sealed with its own AES-256-GCM key, and those keys are wrapped by a master key kept in your login keychain, on this Mac only. Nothing is stored in plaintext, and nothing syncs anywhere.
Or in the Secure Enclave. Move the master key into your Mac's Secure Enclave, the chip that holds keys and never lets them out. Then the key cannot be copied off this Mac, and no other program running as you can read it. Opening it takes Touch ID or your password, enforced by the Secure Enclave. It is opt-in and off by default: in JitPass, open Settings › Protection and choose Move to Secure Enclave… on the Vault key row. It saves a recovery file first, and Move Back to Keychain… is in the same row's ··· menu. Your secrets, grants and AI jobs stay as they are, and nothing is re-encrypted.
Prefer the terminal? Moving the key, and its limits
jit vault export <file> # a current recovery file is required first
jit vault rekey --wrapper secure-enclave # move the vault key into the Secure Enclave
jit vault rekey --wrapper keychain # move it back, any timeIt needs an Apple Silicon Mac and the jit inside JitPass.app; a jit
installed on its own cannot reach the Secure Enclave, and says so. The key
cannot follow you to a new Mac, so jit refuses the move until you have a
current recovery file. The Secure Enclave page
covers moving back, a new or erased Mac, and what jit doctor may report.
How a program gets one. No kernel extension, no filesystem driver, no FUSE. Three mechanisms, picked by what the tool can do:
- Environment variables into one process, then
execve. jit's own image is replaced by your command, so the value lives in that one process and jit is gone from memory. - The tool's native credential protocol, where one exists: AWS
credential_process, docker and git credential helpers, kubectl exec plugins, Terraform's credentials helper. The tool asks, jit answers, no file involved. - A named-pipe mount, for tools that can only read a file.
The named-pipe mount, and why caller identity never decides
The mount is a POSIX FIFO, created with mkfifo(2) at mode 0600. A program
calling open(".env") blocks in the kernel until a writer connects. The
background service is that writer: it opens the path O_WRONLY, which
releases the reader, writes the decrypted bytes from memory into the kernel
pipe buffer, closes, and loops back to open(2) for the next reader. Nothing
touches the disk. What gets written is decided per read: decoys for an ambient
reader, real values only inside a run you authorized.
Caller identity explains and audits, it never decides. Process names are
forgeable, and a fast-closing FIFO reader can evade identification entirely.
The human answering the prompt is the gate; the process name only tells you
what to answer. The app is a thin client of the same service: every action is
a request the jit CLI can also send, and the app never sees a secret value or
a key.
Full detail in how it works and live mounts.
- It does not make an already-compromised account safe.
- It does not protect a secret once it is in the memory of the program you gave it to.
- An AI job's output is only as safe as its script. jit hides the values it injected; it cannot hide something the script computed from them.
- It is not a team secrets manager or a cloud vault: nothing syncs, and each Mac has its own vault.
- It runs on macOS 14+ on Apple Silicon only.
Every boundary is stated on one page: the deliberate limits.
- Quickstart: setup, migrating, living with the fix
- How it works: the vault, the service, mounts and shims on one page
- FAQ: developer and security questions, answered bluntly
- Supported tools: what to type for every tool
- Command reference: every command and flag, generated from the CLI
- Security architecture: the threat model and the honest limits
- The app: the menu bar app's source
- CONTRIBUTING.md: build and test setup; sign-off via DCO (
git commit -s), which also accepts the CLA
PolyForm Perimeter License 1.0.0: source-available, and free for personal and internal company use.














