Show HN:笔记本电脑是你唯一的明文秘密存放地
Show HN: Laptop is the last place your secrets are still in plaintext

原始链接: https://github.com/jitpass/jit

**jit** 是一款专为 macOS(Apple Silicon)设计的工具,旨在保护通常以明文形式存储在计算机上的开发者凭据,如 AWS 密钥、`.env` 文件和 API 令牌。 **工作原理:** * **保险库存储:** `jit` 将敏感数据转移至受 Touch ID 保护的本地加密保险库中。 * **诱饵替换:** 它会将明文文件替换为诱饵,仅在您授权时才将真实凭据注入内存。 * **生物识别门控:** 使用双重身份验证:首先解锁会话保险库,其次批准特定工具(如 `aws` 或 `npm`)访问您的密钥。 * **可审计性:** 每次凭据访问、解锁尝试或被拒绝的请求都会被记录,以备安全审查。 **主要功能:** * **进程授权:** 允许特定工具(如 AI 代理或长时间运行的脚本)获得临时的、可审计且可撤销的密钥访问权限。 * **无缝集成:** 大多数工具可通过 `jit run` 或 `jit wrap` 直接运行,无需修改。 * **可逆性:** 每次迁移都会自动备份,通过 `jit migrate undo` 可轻松恢复。 `jit` 目前处于积极开发阶段,旨在通过确保凭据在静态存储时绝不泄露,从而防止密钥被盗。

Hacker News 社区正在讨论一款名为“Jitpass”的工具,该工具声称能够保护笔记本电脑上的密钥安全。社区对此反应不一,用户既指出了其潜在的实用性,也提出了严重的安全性担忧。 批评者认为,该工具的前提——即本地密钥普遍以明文形式存储——被夸大了,并指出大多数开发者已经在使用全盘加密和受密码保护的 SSH 密钥。人们对该工具的加密实现及其无法抵御内存抓取攻击的能力持高度怀疑态度。 争议的一个主要焦点在于其安装方式。一些用户指出,一个安全产品竟然推荐“curl 到 tar”的安装流程,这与该工具所警告的危险行为如出一辙,颇具讽刺意味。尽管有些人欣赏其精致的用户体验和生物识别加密的概念,但另一些人对于在没有更广泛验证或容器化的情况下运行此类软件仍持谨慎态度。总而言之,虽然社区认为这一概念很有趣,但许多人仍不相信其目前的实现方式能有效解决所宣称的问题。
相关文章

原文

Just-in-time credentials for your dev machine.

Documentation · Quickstart · Supported tools · Command reference · Security

Status: macOS-only (Apple Silicon), and still in development.

Your secrets live in plaintext all over your machine: .env files, ~/.aws/credentials, ~/.zshrc exports, .npmrc tokens, MCP configs. Anything running as you can read them. A bad curl | sh, a sketchy npm install, or one of the AI agents now running in your editor with your full permissions.

jit moves each secret into a local encrypted vault gated by Touch ID, and rewrites the files so your tools keep working. On disk there's now a decoy. The real value only appears, in memory, for the specific process that asked for it, after a biometric prompt. The result: you unlock once, jit asks before handing a credential to a tool (or an agent), and there's a decoy on disk the rest of the time.

brew install jitpass/tap/jitpass

Or without Homebrew:

curl -sL https://dl.jitpass.com/jitpass/jit/releases/latest/download/jitpass_darwin_arm64.tar.gz | tar -xz jit
sudo mv jit /usr/local/bin/

Apple Silicon only — on an Intel Mac, build from source with go install github.com/jitpass/jit/cmd/jit@latest.

Pick one route. If you installed from the tarball before and are switching to Homebrew, remove the old copy after the brew install (sudo rm /usr/local/bin/jit); otherwise two jits sit on PATH upgrading separately, and jit doctor will flag it.

Releases are signed with a Developer ID and notarized by Apple, so both paths run without a Gatekeeper prompt: Homebrew quarantines its downloads and Gatekeeper clears them against the notarization ticket, while curl (and go install) set no quarantine flag at all. To check what you got rather than take our word for it, run jit doctor — its jit line reports signed CZC6BH93GJ, using the same check jit upgrade runs before it will install anything.

Upgrading: brew upgrade jitpass, or jit upgrade — a verified self-update (Developer-ID signature and checksum both checked before the swap, restarts the service). Either way your vault is untouched.

Homebrew installs shell completion with the binary, so jit <TAB> completes subcommands, flags, vault paths, and wrappable tool names out of the box. Installed from the tarball or from source, add it yourself:

echo 'source <(jit completion zsh)' >> ~/.zshrc && exec zsh

Either way, jit doctor tells you if completion isn't reaching your shell.

jit scan                            # read-only. changes no file it scans, prints no real value.
jit vault init                      # make the vault (master key in your login keychain)
jit migrate --dry-run               # preview the whole machine-wide fix plan
jit migrate                         # apply it: shows plan, asks [y/N], one Touch ID
jit migrate ~/code/myapp            # or fix just one project
jit run -- npm run dev              # run your tool; real values injected into that process only

jit scan with no path sweeps your whole home directory, so give it a moment on a large one. To go straight at one place, point it at a path: jit scan ~/.aws.

Day to day it's mostly jit run -- <cmd>. For CLIs that carry their own login token (gh, glab, stripe, and more) you jit wrap gh once and then keep typing gh as normal forever.

Not sure whether something needs jit wrap, jit migrate, or nothing? You don't have to know. jit scan splits everything it finds into what jit will protect (one command - the wraps included) and what only you can fix, and bare jit migrate runs that whole plan:

$ jit scan
  YOUR SECRETS: 7 — 0 protected by jit (0%)
  ▱▱▱▱▱▱▱▱▱▱  to 100%: one command +71% · 2 secrets only you can fix +29%

  jit will protect these — 5 secrets in 4 files, 0% → 71%
      → jit migrate
        ~/.zshrc            STRIPE_API_KEY, DB_PASSWORD
        ~/.config/gh/hosts.yml  GitHub CLI token · wraps gh
        ...

  only you can protect these — 2 secrets, 71% → 100%

    [rotate, then delete every copy]
    ! A production database password in 2 files
      → rotate it now, then delete every copy

(jit scan --full still gives the classic per-category inventory with severities, including the Wrappable CLI Tokens section.)

Migrate the credential once, then keep using the tool the way you always have.

# AWS (and Terraform, and every AWS SDK)
jit migrate ~/.aws/credentials       # keys move to the vault; no plaintext file left
aws s3 ls                            # resolves from the vault on demand. no prefix, no flag.
terraform apply                      # same creds, same command

# GCP application-default credentials (a machine-wide credential)
jit migrate ~/.config/gcloud/application_default_credentials.json
terraform apply                      # google provider reads ADC; works after a Touch ID prompt

# Docker / docker-compose
jit migrate ~/.docker/config.json    # registry logins move to the vault
jit run -- docker compose up         # jit injects them for this run
docker login ghcr.io                 # still works; the helper stores to the vault

# Shell exports that used to sit in ~/.zshrc
jit migrate ~/.zshrc                 # leaves a one-line hook; new shells just have the vars
./deploy.sh                          # scripts that read those vars work unchanged

# Tokens you once typed at the prompt, now sitting in your shell history
jit migrate ~/.zsh_history           # each one moves to the vault; your commands stay, the secrets don't
jit guard history                    # and stop the next one being recorded at all (zsh)
                                     # (bare `jit migrate` offers this too, in the plan it asks you to confirm)

# A CLI that carries its own token (gh, stripe, glab)
jit wrap gh                          # one time
gh pr list                           # token injected per call, forever

The first time each tool reaches for a real credential, jit asks once and remembers your answer until the vault locks. See Two Touch ID moments for how that sits on top of the vault unlock, what --trust does, and how to turn the per-tool prompts off.

Why do some tools need no setup while others take a jit run? One rule: can the tool ask jit for the secret itself? AWS (via credential_process), your shell at login, and docker's registry logins (via a credential helper) all can, so you type nothing extra. Tools that only read a file at runtime (docker compose, plain SDKs) can't ask, so jit run hands them the value.

The machine-global credential files (GCP ADC, sops, npm, netrc) work the same everyday way: run your tool and approve the per-process prompt. Add jit run --with <name> only when you want it explicit: for scripts and CI where there's no prompt to answer, or when you want a hard gate a project's own config can never reach. Supported tools lists exactly what to type for every tool, and how each is delivered.

Two Touch ID moments, not one

jit asks for your fingerprint at two different moments, doing two different jobs:

  1. Unlocking your vault. The first time you use jit after 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.
  2. Handing a credential to a tool. On top of that, the first time a given tool reaches for a real credential, jit asks 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.
$ aws s3 ls
  Touch ID  ->  unlock your vault              # gate 1: opens the vault for 5 min
  Touch ID  ->  aws wants your aws credential   # gate 2: this tool, this credential
  ...your buckets...

$ aws s3 cp ./file s3://bucket/   # same tool, same session: no prompt

$ terraform apply
  Touch ID  ->  terraform wants your aws credential   # a different tool: it asks on its own

Gate 2 is what keeps an unlocked vault from being a free-for-all: even after you've used aws yourself, a sketchy npm install reaching for those same keys still triggers a prompt naming it, so you can say no.

Don'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):

jit service consent off   # tools resolve silently while the vault is unlocked
jit service consent on    # ask per tool again (the default)

Kicking off something that needs several credentials at once? jit run --trust -- terraform apply approves that whole run's tools in one gesture. Full details: per-process consent.

Leaving the keyboard? Approve the work before you go

Both gates assume a human is there to answer. An AI agent working overnight, a long build, a scheduled job: the screen locks, the session drops, and the run stalls on a prompt nobody will see. A process grant moves your decision earlier instead of removing it - one Touch ID, given while you're still there, that names exactly what you're signing:

$ jit grant --process claude --profile jamf --for 8h
  Touch ID  ->  let claude under iTerm2 use 2 secrets (jamf) unattended for 8h
✓ granted g-7f3a2c81   claude -> jamf   until 17:42
  └ covers claude under iTerm2: 1 running now, any started before 17:42

For the next 8 hours, every claude under the terminal you typed that in (and what it launches) gets those secrets with no prompts - through screen lock and all, including sessions you start later: a new tab, the next claude, a script that fires at 3am. It's your terminal being named, not a name being trusted: a program calling itself claude somewhere else on the machine doesn't descend from that tree and inherits nothing. The grant ends at its deadline, when you quit that terminal, or the moment you type jit grant revoke (which needs no fingerprint - taking access away is always free). Want one exact process instead, gone when it exits? --pid. Every serve lands in the audit trail as its own event, so the morning after you can read exactly what your agent touched while you slept. Full details: process grants.

The audit trail: what happened, and who did it

Every jit command and every unlock lands in a durable log you read back with jit audit, newest first, one key=value line per event, so it greps like a real service log. Command arguments are masked, so the log proves a command ran without ever storing the secret it carried.

$ jit audit --since 1h
time=2026-07-24 10:15:04 level=info kind=cmd status=ok dur=312ms cmd="jit migrate ~/.aws/credentials" user=meni parent=claude
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/default

The middle line is the story jit exists to tell: aws/default was read by aws s3 ls, launched by claude. The last is a prompt you declined: a node postinstall.js under npm reaching for those same keys, refused. jit also logs what the service turned away at its socket (a process the kernel says isn't yours, probing the agent) as kind=error.

Narrow it with flags instead of grep: --kind, --status ok|failed|denied, --since/--until (an age like 2h/3d or a date), --parent claude, --secret aws, --user, --grep <regexp>. Add --follow (-f) to stream new events live like tail -f, or --format json for a machine-parseable dump. Both halves are durable files beside the vault, so it answers for last week as readily as the last hour.

.env files, shell exports, AWS and Terraform, kubeconfig, Docker registry logins, GCP ADC, .npmrc / .netrc tokens, MCP server configs, bare token files, credentials recorded in your shell history, wrappable CLIs (gh, stripe, vercel, …), and SSO CLIs that mint credentials at login (clisso). In every case the file keeps working and the real value comes from the vault on demand.

The full catalog, grouped by exactly what to type for each tool, is Supported tools: it tracks the code as tools are added or removed. Anything not listed can still be wrapped with jit wrap add.

jit never destroys a credential. Migrate moves the value into the vault and leaves a working hook where it was (a decoy .env, an eval "$(jit export)" line in your shell config, credential_process = jit … in ~/.aws/config, or a PATH shim), so your tools keep resolving it on demand. The credential still exists, just encrypted instead of sitting in plaintext.

And every change is reversible. Before touching a file, jit backs it up encrypted into the vault, so jit migrate undo puts it back byte-for-byte:

jit migrate ~/code/myapp        # applied the fix, one Touch ID
# changed your mind, or something broke?
jit migrate undo ~/code/myapp   # every touched file restored, byte-for-byte

The docs live under docs/, organized by task:

  • Quickstart: setup, migrating, living with the fix, step by step
  • How it works: the vault, the service, mounts, and shims in one page
  • FAQ: developer and security questions, answered bluntly
  • Per-process consent: what the per-tool prompts do, and how to tune or turn them off
  • Process grants: pre-approve a running tool to work unattended for a bounded, revocable, audited window
  • Audit trail: read back every command, unlock, and refusal, filterable and followable
  • Command reference: every command and flag, generated from the CLI
  • Security architecture: the threat model and the honest limits
  • CONTRIBUTING.md: build/test setup; sign-off via DCO (git commit -s), no CLA

PolyForm Perimeter License 1.0.0 - free for personal and internal company use only.

联系我们 contact @ memedata.com