Tus API keys están en texto plano ahora mismo: jit las guarda y deja decoys en su lugar

Abre una terminal y corre cat ~/.aws/credentials. Si algo salió, acabas de leer un token de producción vivo desde tu propio disco sin autenticación, sin prompt y sin una sola línea de log. Ahora haz lo mismo con el .env del proyecto que tocaste la semana pasada, con ~/.npmrc, con ~/.netrc, con el config del MCP server que tu agente carga al arrancar. La mayoría lo sabemos y acordamos en silencio convivir con eso, porque la alternativa —un secrets manager que cada herramienta de la cadena tiene que aprender a hablar— siempre salió más cara que lo que dolía el riesgo.

jit es una apuesta a que la alternativa puede ser gratis. Su pitch, verbatim desde jitpass.com, son doce palabras: “jit locks the real values away and leaves decoys in your files.” Tus herramientas siguen leyendo los mismos archivos en las mismas rutas. Lo que reciben depende de si el proceso que preguntó estaba autorizado, y el valor real solo existe en la memoria de ese proceso, después de Touch ID.

Llegó a Show HN el 16 de agosto de 2026 con un título que es el argumento completo: “Laptop is the last place your secrets are still in plaintext.” Cincuenta y cuatro puntos, setenta y nueve comentarios — un hilo que discutió más de lo que aplaudió, y a eso llegamos.

El mecanismo, antes de los comandos

Hay dos piezas y conviene entenderlas por separado, porque la segunda es la razón por la que esto no te rompe el workflow.

El vault y los decoys. jit migrate lee la credencial real del archivo, la cifra en un vault local cuya master key vive en tu login keychain, y escribe un decoy donde estaba el valor real. Cualquier cosa que lea ese archivo sin pasar por jit —un postinstall script comprometido, una extensión de IDE troyanizada, un infostealer barriendo rutas conocidas, un agente con prompt injection— recibe el decoy. Recibe un valor que parece una credencial, tiene forma de credencial, y no vale nada.

La inyección. Esta es la parte que decide si una herramienta así es usable. jit usa tres mecanismos según lo que la herramienta destino sepa hacer:

  1. Reemplazo vía execve. jit run -- npm run dev pone el valor real en el environment y después se reemplaza a sí mismo en memoria con tu comando. El secreto vive en ese proceso y en ningún otro lado.
  2. Protocolos nativos de credenciales. El credential_process de AWS, los credential helpers de Docker y Git, los exec plugins de kubectl, los credential helpers de Terraform. Son hooks que las herramientas ya publican exactamente para esto — jit no envuelve ni intercepta a AWS, se registra como aquello que el SDK de AWS ya estaba diseñado para llamar.
  3. Named-pipe mounts. Para herramientas que solo saben leer un archivo, jit pone un mount mkfifo(2) en modo 0600 donde estaba el archivo. El valor descifrado pasa por memoria; no se escribe nada a disco. Un .env migrado es un mount vivo, no un archivo plano.

El punto 2 es la decisión de diseño que importa. Cada intento previo contra este problema le pedía a tu tooling que cambiara. jit usa las salidas de emergencia que ya estaban ahí.

Instalación

Mac con Apple Silicon, vía Homebrew:

brew install jitpass/tap/jitpass

Usa Homebrew y no el tarball. Los releases están firmados con Developer ID y notarizados por Apple; Homebrew pone la descarga en cuarentena y Gatekeeper verifica la notarización antes de que algo se ejecute. El README sí documenta un camino con curl | tar, y un comentarista en HN señaló —con razón— que queda raro al lado de la advertencia del propio proyecto sobre curl | sh. El autor estuvo de acuerdo y dijo que reordenaría los docs para que Homebrew vaya primero.

En un Mac con Intel no hay bottle; compilas desde fuente:

go install github.com/jitpass/jit/cmd/jit@latest

Después confirma qué instalaste realmente:

jit doctor

jit doctor reporta el estado de la firma, entre otras cosas. En una herramienta que está a punto de sostener todas las credenciales de tu máquina, córrelo.

Paso uno: jit scan

Esta es la versión de cinco minutos del artículo, y no cambia nada:

jit scan                            # read-only. changes no file it scans, prints no real value.

Recorre los lugares donde los secretos efectivamente se acumulan: archivos .env y exports de shell, ~/.aws, configs de Terraform, kubeconfig, las Application Default Credentials de GCP, la auth de registries de Docker, .npmrc y .netrc, configs de MCP servers, tokens de CLIs como gh, stripe, vercel y compañía, y tu historial de shell. Imprime los hallazgos sin imprimir valores completos.

Esa última categoría es la que sorprende. ~/.zsh_history es donde un secreto se va a vivir para siempre después de un export STRIPE_KEY=sk_live_... apurado un martes de 2023. No se rota, no se escanea, no se piensa, y está en texto plano.

Corre esto antes de decidir cualquier otra cosa. La salida es el argumento.

Paso dos: el vault y la migración

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

Corre --dry-run primero y léelo. Después acota tu primera migración real a un solo proyecto en lugar de la máquina entera — jit migrate ~/code/myapp. Quieres ver un día completo de trabajo con tu propio tooling pasando por los FIFO mounts y los credential helpers antes de entregarle ~/.aws.

El undo es real, y es la razón por la que un primer intento acotado tiene poco riesgo:

jit migrate undo ~/code/myapp   # every touched file restored, byte-for-byte

Antes de modificar nada, jit cifra un backup del original dentro del vault. Restauración byte a byte, no una reconstrucción con la mejor intención.

Paso tres: correr tus herramientas

jit run -- npm run dev              # real values injected into that process only

Para todo lo que hable un protocolo nativo de credenciales —aws, docker, git, kubectl, terraform— no necesitas el wrapper después de migrar. El helper queda registrado; la herramienta lo llama; ocurre el Touch ID; llega la credencial.

Dos etapas de autenticación. El primer uso después de que se bloquea la sesión pide Touch ID y abre el vault por cinco minutos de actividad, hasta ocho horas continuas. Después, la primera vez que una herramienta dada pide una credencial dada, Touch ID vuelve a preguntar y nombra al proceso que está pidiendo. La misma herramienta más tarde, sin prompt.

La segunda etapa es la que mucha gente va a apagar al segundo día, y también es la que hace el trabajo interesante: es lo que convierte “algo en mi máquina leyó mi AWS key” en “node postinstall.js, parent npm, pidió aws/default y dije que no”. Si te resulta demasiado:

jit service consent off             # keeps stage 1, drops stage 2
jit run --trust -- <command>        # approve all of a command's tools in one gesture

La parte que sí es sobre agentes

Si corres Claude Code, Codex, Gemini CLI, cline o Copilot con permisos amplios, tienes un proceso en tu máquina que lee archivos, ejecuta comandos y puede ser dirigido por texto que encontró en un repositorio. jit trata eso como un caso de primera clase y no como un agregado.

Los configs de MCP servers migrados guardan rutas del vault en lugar de keys. Los CLIs de agentes vienen envueltos, así que el prompt de Touch ID nombra al agente que está pidiendo. Las lecturas no autorizadas reciben el decoy y quedan en el log con un valor placeholder. Y puedes auditar un agente en particular:

jit audit --parent claude

Para corridas desatendidas, un grant en lugar de cuarenta prompts:

jit grant --process claude --profile jamf --for 8h

Un solo Touch ID, mientras estás sentado ahí. El grant lo heredan únicamente los descendientes de la terminal que lo otorgó, expira en el plazo, se revoca con jit grant revoke, y cada acceso bajo ese permiso queda logueado.

El audit log es la feature subestimada

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

Lee de nuevo la tercera línea. Eso es el postinstall script de un paquete estirando la mano hacia tus credenciales de AWS y recibiendo un no, con timestamp. Hoy no tienes ningún equivalente de esa línea, porque hoy no hay nada que denegar: el archivo simplemente se puede leer.

jit audit --since 1h --parent claude --status denied
jit audit --follow                  # stream live
jit audit --format json             # machine-parseable

Lo que no hace, dicho sin vueltas

La objeción más fuerte en HN vino del usuario hypfer: “If someone or something is executing code on your machine, you have already lost.” El autor nunca la respondió de frente, y no creo que se pueda refutar tal como está planteada — solo se puede acotar.

El proyecto la acota con honestidad en su propio README. jit no protege una cuenta ya comprometida, y no protege un secreto una vez que entró a la memoria de un proceso: el proceso es el límite, punto. Y sobre identidad, el README dice algo que la mayoría de las herramientas de seguridad habría omitido:

“Caller identity explains and audits, it never decides. Process names are forgeable, and a fast-closing FIFO reader can evade identification entirely.”

(“La identidad del que llama explica y audita, nunca decide. Los nombres de proceso se pueden falsificar, y un lector de FIFO que cierre rápido puede evadir la identificación por completo.”)

Así que la afirmación honesta es más angosta que el marketing y aun así vale algo: esto no impide la ejecución de código en tu Mac, elimina el botín gratis. El caso dominante en el mundo real no es un atacante dirigido que ya es dueño de tu máquina y escala con paciencia — es un infostealer o una dependencia maliciosa haciendo un barrido rápido de rutas conocidas y exfiltrando lo que encuentre. Ese ataque se lleva decoys, y queda en el log.

El estado del proyecto, que conviene tener en cuenta

El post de Show HN se lee como un lanzamiento. No es un 1.0.

El repo se creó el 13 de julio de 2026. Hoy va en la v0.98.0, publicada el 17 de agosto — unos noventa y ocho releases en cinco semanas, varios por día. La línea de estado del propio README no deja lugar a dudas: “macOS-only (Apple Silicon), and still in development.” Linux es “soon”, según el autor en HN.

Y las release notes de la v0.98.0 son lo más útil que leí en la semana, porque documentan una falla en vez de una feature. Un brew upgrade en un Mac real dejó el servicio de fondo de jit muerto durante 71 minutos. jit service restart reportaba éxito y no hacía nada. Causa raíz: reemplazar el binario hacía que el agente saliera limpiamente, por diseño, y después esperara a que launchd lo reiniciara, cosa que nunca pasó; launchctl kickstart, el único mecanismo que funcionaba de forma confiable, había sido eliminado el 20 de julio y nadie lo notó durante cuatro semanas.

Ese release lo arregla: ahora los restarts exigen un arranque en lugar de registrarse y esperar, los upgrades confirman que el servicio responde en el build esperado, y el reporte de estado distingue “aceptado pero nunca arrancado” de “detenido con exit code” de “descartado por completo”, en vez de informar un crash genérico.

Tómalo como un dato sobre dónde está esto en la curva de madurez, no como una razón para saltártelo. Un daemon en el camino entre tus herramientas y tus credenciales tiene un modo de falla que un password manager no tiene: cuando se cae, las cosas no fallan de forma ruidosa y evidente, fallan de forma confusa. Lo cual es un argumento para correr jit scan hoy, migrar un proyecto esta semana, y darle un mes antes de que sostenga ~/.aws.

La licencia, con precisión

PolyForm Perimeter License 1.0.0 — gratis solo para uso personal e interno de empresa. El autor en HN: “Yes, but it’s completely free for internal Personal and Company use.”

Vale ser exacto, porque el repo tiene el cuidado de nunca decir otra cosa: esto es gratis, y no es open source. PolyForm Perimeter no es una licencia aprobada por la OSI — es una licencia source-available construida para impedir el uso que compite con quien la otorga. Para tu equipo usándola internamente en sus laptops, esa distinción no cambia nada. Para cualquiera que esté pensando en construir sobre ella o distribuirla dentro de un producto, lo cambia todo. Lee la licencia antes de asumir.


Nada de esto es una idea nueva. Vaults, biometría, credential helpers y audit logs ya existían. Lo que hizo jit fue notar que la parte aburrida, universal y vergonzosa del problema —la laptop— es la parte para la que nadie había hecho un fix de cinco minutos, y después cablear ese fix a hooks que AWS, Docker, Git, kubectl y Terraform ya habían publicado y casi nadie usaba.

Empieza por jit scan. No cambia nada, no imprime nada secreto, y te va a decir en aproximadamente un segundo qué tan mal está la situación en tu propia máquina.

¿Cuántos secretos crees que tienes en texto plano en tu máquina ahora mismo? Corre jit scan y cuéntame si el número te sorprendió.

https://news.ycombinator.com/item?id=49317546