Un asistente de código tiene que leer el código para completar una función o arreglar un error. Abres un repositorio y asumes que entregaste los pocos archivos de este árbol de trabajo que esta tarea puede usar. Si el .env ya está en .gitignore, mucha gente lo lee como «el asistente no lo ve, y la nube tampoco». El 18 de septiembre de 2026 el desarrollador ferstar escribió la capa de abajo: tras iniciar sesión, el cliente de escritorio oficial de Zhipu, ZCode, arma en esta máquina una instantánea del espacio de trabajo. El grueso de esa lista no es src/. Es el directorio .git completo.

Una nota anterior cubría qué enmascarar antes de pegar una contraseña en ChatGPT o Gemini: eso es una cadena que tú decidiste pegar. Aquí cambia la pregunta. Nunca pegaste el .env en el chat. ¿El almacén de objetos de Git, la caché LFS y el reflog dentro de una instantánea del espacio de trabajo siguen llevándose claves que ya habías borrado? Si hay que mover una clave, usa un enlace de un solo uso con la forma s.html?id=…#…. Crear y leer funcionan sin cuenta. Las herramientas de MyPassGen se abren sin registro. El resto de esta página no desmonta un cliente ni explica cómo bloquear la subida de otro. Alinea las cifras ya escritas en la reconstrucción de ferstar del 18 de septiembre de 2026 y en la reproducción de ITHome del comunicado oficial del mismo día, más el directorio de checkpoints que puedes abrir en este ordenador.

Separa primero los dos trabajos

«Ya actualicé a una versión que ya no sube» corta el próximo empaquetado. Las instantáneas ya armadas, ya fuera de esta máquina, y que la empresa dice «destruir en cuanto se genera el Wiki» no se pueden contrastar desde fuera. No puedes abrir el servidor y confirmar que desapareció una copia, ni quién sigue teniendo la clave privada. Mira primero si hay un .enc pendiente; luego decide qué contraseñas que alguna vez entraron en Git hay que rotar. No sustituyas un número de versión por «¿sigue habiendo una instantánea en esta máquina, y git log aún imprime la clave de prueba?».

Abrir el repo no es entregar solo este árbol

Una función que pegas en el chat es el contexto que elegiste. Una instantánea del espacio de trabajo es otro camino: el cliente empaqueta el repositorio que abriste, y el alcance puede ser mayor que «los archivos que necesita este prompt». Cuando ferstar partió la lista por tamaño, .git/lfs/, .git/objects/ y .git/logs/ sumaban unos 86,6 %. El código fuente y la documentación, unos 13,4 %. Así que aunque este directorio ya hubiera borrado el .env, un blob viejo del almacén de objetos puede ir de polizón.

Eso no es el mismo camino que «¿esta web envió como dato de negocio la contraseña que estoy generando?». En la página del generador de MyPassGen puedes abrir Red y ver que el texto plano no sale como dato de negocio. Un asistente de escritorio que empaqueta una instantánea usa su propio directorio local y su propia conexión de salida. Un archivo al que haces git rm suele ser «no está en este árbol», no «nunca apareció en el historial». Escribir una clave sin enmascarar en una confirmación es el mismo tipo de problema que escribir una contraseña en el cuerpo de un correo: el otro lado mira una copia que tú dejaste a propósito. Ese camino está en si escribes una contraseña temporal en el correo, qué dejan Enviados, el reenvío y la vista previa del móvil.

Hay otra capa fácil de mezclar. .gitignore bloquea «no vuelvas a seguir esta ruta». No bloquea «esta ruta se confirmó una vez». La documentación de GitHub en español lo dice igual: si confirmas datos confidenciales, borrarlos del árbol actual no los saca del historial; el primer paso con una contraseña o un token es rotarlos. Si el asistente solo lee archivos de este árbol, las reglas de ignore aún ayudan. Si la instantánea empaqueta todo el directorio .git, esas reglas no sirven. Una rama local que nunca empujaste, operaciones que siguen en el reflog y una URL interna en .git/config pertenecen al conjunto «esta pestaña del editor no lo ve, el almacén de objetos sí lo tiene».

Las cifras que quedaron escritas el 18 de septiembre

ferstar escribió que el punto de partida era ~/.zcode ocupando más de 700 MB. v2/checkpoints/ eran unos 303 MB. Dentro había un .enc de unos 313 MB. El archivo de estado registró unos 345 MB antes del empaquetado, 313070842 bytes después del cifrado, kind baseline y un failureCount de 564. Esa instantánea de un proyecto comercial se quedó local en pending; la reconstrucción dice que no se subió. Un repositorio público mucho más pequeño era otra historia: 538 archivos, unos 15 KB tras comprimir y cifrar, estado recibido por el servidor. Así que «¿salió algo de verdad?»: al menos esa vez, el repo pequeño sí.

La misma lista del proyecto comercial: 42.411 archivos; .git/lfs/ unos 196,1 MB (56,8 %), .git/objects/ unos 102,2 MB (29,6 %), .git/logs/ unos 0,6 MB (0,2 %). Otra reproducción de la comunidad escribió que en ZCode 3.12.3 una instantánea de proyecto pesaba unos 748 MiB, con .git en torno al 98,91 %. Sobre los disparadores, la reconstrucción nombra captureBeforePrompt antes de una pregunta y repo-wiki-update al terminar una tarea. En el registro de una sesión activa, la captura de instantáneas apareció hasta 62 veces.

El comunicado oficial salió a las 17:44 del 18 de septiembre. ITHome lo reprodujo esa noche. Los puntos: el asunto estaba en la «indexación del repositorio de código», usada para un índice local, la reversión de checkpoints de sesión y el Repo Wiki; generar una página Wiki en la nube «puede» disparar una subida de datos del repositorio; tras generar el Wiki, esos datos subidos se destruyen al momento y no se conservan; la función estaba activa por defecto en el lanzamiento temprano, algunos usuarios se vieron afectados y el problema «ya está corregido»; ZCode se abriría en breve, con una revisión de terceros; cada usuario recibiría un reinicio extra de la cuota semanal. La empresa no negó que hubiera habido una subida. Lo que sigue en duda es el alcance, si entonces podías apagarla, y cómo contrastar desde fuera eso de «destruido al momento». El 20 de septiembre, InfoQ reprodujo una carta de Taiyuan Chengming Technology a Beijing Zhipu Huazhang pidiendo borrado, flujo, registros y quién responde. Eso es una pregunta de empresa. No es un recibo de destrucción que puedas abrir en esta máquina.

El grueso es .git: las claves borradas siguen en objects

Que no haya .env en el árbol de trabajo actual solo prueba que este checkout no tiene esa ruta. Git guarda blobs por contenido. Si una confirmación escribió alguna vez DATABASE_URL= o AWS_SECRET_ACCESS_KEY=, y luego editaste el archivo, volviste a confirmar o incluso lo borraste de esta rama, el blob viejo suele seguir en .git/objects hasta que un gc lo tire de verdad. Una caché LFS puede conservar archivos grandes históricos. El reflog anota qué ramas moviste en esta máquina. Empaqueta esas tres cosas en una instantánea y la nube no tiene «esta pantalla del editor». Tiene el historial que este repositorio acumuló aquí.

La reconstrucción también dijo que los filtros del espacio de trabajo excluyen algunos archivos de secretos, pero .git igual entra en el paquete. Esa frase merece una pausa. Hoy sacas el .env del repo y dejas solo .env.example. Este árbol se ve limpio. La confirmación equivocada del año pasado sigue en el almacén de objetos. Un filtro que solo mira rutas del árbol de trabajo no la quita. Un hostname interno de GitLab en .git/config, y el nombre de una rama que nunca salió de este portátil, viven en los refs y en el reflog. «Ya lo puse en gitignore» no los recupera.

Puedes compararlo con «que se filtre un hash de contraseña no es lo mismo que alguien ya lea el texto plano». Un hash es un almacén de un solo sentido, y aun así se rota. Una clave en el historial de Git suele ser el texto plano mismo. Contrastar esta máquina con una lista habitual de contraseñas débiles solo prueba un acierto en una lista pública. No prueba si una instantánea concreta se llevó tu confirmación vieja. Esa diferencia está en lista local de contraseñas filtradas vs Have I Been Pwned: qué prueba cada comprobación. Rota el conjunto que alguna vez entró en una confirmación, no la mirada que dice «este árbol ahora está limpio».

Cifrado no significa que solo tú puedas descifrar

La forma de salida que reconstruyó ferstar es esta: el cliente pide a zcode.z.ai credenciales de subida de instantánea, y recibe una clave de objeto, un límite de tamaño y una clave pública RSA. Esta máquina empaqueta el espacio de trabajo como tar.gz, lo cifra con AES-256-CTR, envuelve la clave simétrica con esa clave pública y envía el tar.gz.enc directo a Aliyun OSS. La clave privada se queda en la nube todo el tiempo. Los cientos de megabytes de .enc en disco no se te abren, ni se le abren al cliente. El nombre del algoritmo dice AES-256. Eso resuelve «el camino y el cubo no son un archivo en claro». No te entrega el derecho a descifrar.

Es la misma frontera que una página de disco que dice «cifrado». Cuando el proveedor guarda la clave, el lado del almacenamiento aún puede abrir el archivo. Cuando cifras primero con una contraseña que tú tienes, el otro lado debería ver solo texto cifrado. La diferencia no es si aparecen las cuatro letras AES. Es quién tiene la clave privada o la contraseña. La caja de cifrado de MyPassGen hace cifrado en flujo AES-256-GCM en el navegador, un archivo de hasta 5 GB, salida .lock / .enc, y se abre sin cuenta. La contraseña la envías por otro camino. El archivo no se sube como dato de negocio. La clave de sobre de aquella instantánea de ZCode usaba una clave pública emitida por el servidor y una clave privada que el servidor se queda. El objetivo es el contrario: que el servidor pueda descifrar. Véase antes de dejar un archivo en la nube, quién puede leer el texto plano y por qué canal debe ir la contraseña.

El comunicado dice que la subida se destruye al momento tras generar el Wiki y no se conserva. Si esa destrucción ocurre en el servidor, desde este ordenador no puedes contrastar copias de seguridad, versiones del almacén de objetos ni réplicas de la clave privada. ferstar escribió que en la 3.14.0 el código de la ruta de subida ya no estaba en el cliente, upload-credential devolvía 404 y solo quedaba un checkpoint local. Eso puede probar «este cliente nuevo ya no pide esas credenciales». No puede probar «la instantánea del repo pequeño que el servidor ya recibió desapareció físicamente de todas las copias». Leer «iba cifrado» como «solo yo lo veo» se salta la rotación que aún tienes que hacer.

Apagar un ajuste no detiene el empaquetado

La reconstrucción alineó dos interruptores con la ruta de código. optimizeAgentExperienceEnabled (optimizar la experiencia) cubre si los datos pueden usarse para entrenar. Tras apagarlo, la instantánea sigue empaquetándose. repoSnapshotIndexingEnabled (indexación de instantáneas del repo) cubre si el servidor construye un índice después de tener la instantánea. Tras apagarlo, el empaquetado local sigue. En la 3.12.3, la lógica de captura y subida se levantaba cuando el inicio de sesión le entregaba un JWT. La interfaz no tenía un interruptor aparte de «no empaquetar ni subir». El comunicado no listó una casilla que el usuario pueda desmarcar y luego verificar, en el momento, que el tráfico de salida se detuvo. Dijo que la función estaba activa por defecto al principio, y que el problema ya estaba corregido.

Así que «nunca activé el Repo Wiki» y «apagué el entrenamiento» no son una exención para la 3.12.3. Fíate de si tu directorio de checkpoints tenía entonces un .enc nuevo, y de si el estado era pending o recibido. Tras actualizar a la 3.14.0 que nombra la reconstrucción, mira el mismo directorio otra vez: ¿aparece un paquete pendiente nuevo, y la URL de credenciales sigue devolviendo éxito? El cliente puede actualizarse en caliente. El número de versión y ese directorio son dos cosas que puedes mirar más de una vez. Un titular no.

Una captura de la política de privacidad oficial guardada el 18 de septiembre seguía mostrando una última actualización del 15 de junio de 2026. La política decía que el producto recoge texto, archivos y código enviados en una conversación: el alcance habitual de un asistente que llama a un modelo. La reconstrucción señala que, en ese momento, la página nunca decía que una instantánea de repo completo o todo el historial de Git irían a la nube. Cuando una frase de la política y una lista local de archivos no coinciden, fíate de la lista y del archivo de estado. No trabajes al revés desde «acepté la política de privacidad» hasta «así que solo salió el párrafo del cuadro de chat».

En paralelo: este árbol, el historial de Git y el chat

La misma clave de prueba que alguna vez entró en un repo puede partirse en al menos cuatro caminos de «quién aún puede leerla». La diferencia no es la marca del asistente. Es dónde se hizo una copia, y quién tiene el derecho a descifrar.

Qué hiciste Qué sigue en esta máquina Resto que el comunicado o la reconstrucción ya nombraron
Solo abriste el repo, no iniciaste sesión en el asistente El árbol de trabajo y .git Nada (la ruta de instantánea aún no tenía sesión)
Entraste en la 3.12.3; este árbol ya había borrado el .env Los blobs viejos siguen en el almacén de objetos La lista de la instantánea aún puede llevar un .git completo; un repo pequeño tuvo un registro de recibido en el servidor
Apagaste entrenamiento / indexación de instantáneas No tiene que ver con el almacén de objetos Reconstrucción 3.12.3: esta máquina sigue empaquetando; los interruptores no cubren la subida
Actualizaste a una versión que ya no sube, nunca miraste checkpoints Los .enc viejos y los archivos de estado pueden seguir aquí La próxima salida se corta; si se destruyó una instantánea ya recibida no se puede contrastar desde fuera
La clave nunca entró en Git; la entrega usó un enlace de un solo uso con el canal partido Los archivos de prueba se pueden borrar Una búsqueda en la instantánea no encuentra una credencial completa; hacen falta las dos mitades para descifrar

No mezcles la quinta fila con las cuatro primeras. Si escribes un s.html?id=…#… completo en el repo y luego abres el asistente, el historial y una instantánea aún pueden recoger la credencial entera. El anfitrión que aparca texto cifrado sigue sin ver la clave. Parte el id y la clave, y una búsqueda de texto completo no encuentra un enlace que abra. Un enlace completo sigue siendo una contraseña. Cifrar y luego sincronizar es para un archivo. Una vez que el almacén de objetos de Git ha visto texto plano, añadir un .lock después no borra el blob viejo.

Compruébalo en el momento

Estos pasos no dependen de ninguna promesa de marca. Usa una contraseña de prueba y un repo de usar y tirar que no inicie sesión en una cuenta de trabajo real y no apunte a un repositorio de la empresa. En un directorio vacío, confirma una línea como orange-lake-7 y luego bórrala. No ensayes con una maestra que sigues usando, una clave API de producción o un enlace de un solo uso vivo.

  1. Lee la versión en la página Acerca de ZCode o en el instalador. La reconstrucción nombra la 3.12.3 como la versión del problema y la 3.14.0 como la que quitó la ruta de subida. Fíate del número que lees ahora. No sustituyas un anuncio de grupo por esa mirada.
  2. Abre v2/checkpoints bajo la raíz de datos local (en macOS y Linux suele ser ~/.zcode/v2/checkpoints; en Windows, el directorio de usuario tras la instalación). ¿Hay un .enc, y un JSON de estado al lado? Los campos que puedes abrir: si workspacePath es un proyecto que creías no haber abierto, encryptedSizeBytes, failureCount, kind. Una ruta que coincide significa que esta máquina empaquetó ese repo. Un failureCount alto solo dice que ese paquete no salió. No prueba que ningún otro repo hubiera tenido éxito.
  3. Crea un repo de prueba vacío, confirma una contraseña de ensayo y luego bórrala de este árbol. Usa git log -p o git log --all --full-history -- nombre-del-archivo y mira si la confirmación vieja sigue ahí. Si sigue, «ya la borré» no salva el almacén de objetos. Eso es el comportamiento de Git, con o sin asistente. Si una instantánea empaqueta .git, esta es la capa que lee.
  4. Si la 3.12.3 llegó a abrir ese repo de prueba: vuelve a checkpoints y mira si un paquete nuevo coincide con esa ruta. Tras una actualización, observa un rato: ¿aparece un .enc pendiente nuevo? Los archivos locales son evidencia que puedes mirar más de una vez. No necesitas, y no debes, desmontar el cliente de otro.
  5. Trata toda contraseña, token y dirección interna que alguna vez entraron en un repo real como «ya salieron de esta máquina»: rótalos en el servicio original y genera una contraseña aleatoria nueva. El generador de contraseñas de MyPassGen usa un modo aleatorio de 6–128 caracteres, 16 por defecto, y avisa por debajo de 8. Se abre sin cuenta. El resultado no se sube como dato de negocio. Si reutilizaste una cadena, usa Fortaleza para contrastar esta máquina con una lista habitual de contraseñas débiles. Eso puede probar un acierto en una lista pública. No puede probar el alcance de una instantánea concreta.
  6. Crea un enlace de un solo uso de MyPassGen con la misma frase de prueba, caducidad 24 horas, lecturas en 1. En el chat o en un ticket, pega solo la parte anterior a la almohadilla, s.html?id=…. Di la clave por teléfono o en persona. Se abre sin cuenta. Con solo el id, el destinatario debería ver un enlace incompleto; hacen falta las dos mitades para descifrar. Tras leer, sobrescribe el portapapeles. No escribas en un repo una dirección que aún tenga #, y no pegues la página de resultado en el chat de ningún asistente.

En un repo de empresa, añade medio paso: pregunta qué raíces abre el asistente por defecto, si un directorio de secretos puede quedarse fuera del espacio de trabajo, y si las instantáneas históricas tienen un recibo de borrado empresarial. MyPassGen no va a decidir si una nube concreta guardó otra copia. Fíate de las ventanas que acabas de abrir y del registro de Git.

Si hay que entregar una clave, parte el canal

Si es uno a uno y el otro puede abrir ahora, no escribas la contraseña en un archivo que va a entrar en Git, en el espacio de trabajo de un asistente y a quedarse en el almacén de objetos. Genera la contraseña en este dispositivo y conviértela en un enlace de un solo uso. Al crearlo, el navegador cifra con AES-256-GCM. El texto plano admite hasta 32 KB. Las lecturas empiezan en 1 y no pasan de 10. La caducidad puede ser 1 hora, 24 horas, 7 días, o solo por número de lecturas, sin TTL. El servidor guarda solo texto cifrado. La clave va detrás del # de la URL; los registros de acceso y el Referer no ven ese trozo. Un repo y el cuadro de chat de un asistente sí. Por eso no escribas el enlace completo en una confirmación.

Si el repositorio o el ticket tienen que dejar una puerta, parte el canal. El archivo lleva solo al responsable, el número y la frase «la clave va por teléfono». El teléfono, la mesa o otra cuenta de mensajería llevan solo el trozo detrás de #. Sin las dos mitades no se descifra. Eso es un uso, no una división por defecto del producto: la página de creación sigue generando un enlace completo, pensado para un intercambio uno a uno. En un canal que arma tarjeta de vista previa, prueba primero con un enlace de ensayo si esa tarjeta cuenta una lectura, como en si pegas un enlace de un solo uso en Slack o WeChat, ¿la vista previa lo quema primero. Enmascara extractos de error y de configuración en Limpiar URL antes de decidir si aún tienen que entrar en un asistente.

Un paquete de claves o una tabla exportada de más de 32 KB no cabe en un enlace de texto de un solo uso. Usa la caja de cifrado: cifrado en flujo AES-256-GCM en el navegador, un archivo de hasta 5 GB, salida .lock / .enc, la contraseña por otro canal. Cifras aquí y sincronizas después; el otro lado debería ver solo texto cifrado. Empujar un .env sin enmascarar a Git es el mismo tipo de problema que empujar a un disco un paquete de certificados sin cifrar: la copia que prueba «esta es la clave» ya salió del árbol que creías limpio. Cómo comprobar en el momento que el cifrado en el navegador no envió texto plano como dato de negocio está en cómo comprobar que el cifrado en el navegador no sube el texto plano.

Cuando hayas contrastado tres veces —si checkpoints tiene un .enc de esta ruta, si git log aún imprime la contraseña de prueba, si actualizar solo el cliente vacía una instantánea vieja—, ya puedes responder a la pregunta de este texto. Borrar el .env de este árbol no vacía el almacén de objetos, ni una instantánea ya empaquetada. La corrección oficial cubre la ruta del cliente que ellos pueden cambiar. Un paquete que el servidor ya recibió, y el texto plano de tus confirmaciones viejas, no se vacían porque actualices una vez. El directorio local y el registro de Git sirven como puerta de comprobación. No como prueba de que «nunca pegué la clave en el chat y se acabó».

Preguntas frecuentes

Ya actualicé. ¿Las instantáneas viejas también desaparecieron?

No es la misma acción. Una versión nueva corta la próxima petición de credenciales y la subida directa. Un .enc viejo en esta máquina, el archivo de estado y la copia en la nube que la empresa dice «destruir tras generar» no entran en ese clic de actualizar. Cuenta si los checkpoints siguen aquí y si una clave viva ya se rotó.

Este árbol ya tiene el .env en gitignore. ¿Aun así hay que rotar?

Lee el historial, no este árbol. Una regla de ignore solo bloquea el seguimiento futuro. Si una confirmación vieja tenía texto plano, trátalo como ya copiado. Contrastar una vez con git log en un repo de prueba es más directo que fiarte de un filtro.

La instantánea iba cifrada. ¿Puedo tratarla como si no se hubiera filtrado?

No. La reconstrucción dice que la clave privada se queda en la nube. Tú no puedes abrir el texto cifrado local. El cifrado evita «un tar en claro sentado en el cubo». No significa «solo el dueño del repo puede leer». Si la destrucción no se puede probar desde fuera, trata las claves que alguna vez entraron en Git como material que hay que rotar.

¿Hace falta registrarse para crear o leer? Si me equivoco de canal, ¿hay un correo de soporte que recupere el secreto?

No hace falta registrarse. Crear y leer están abiertos al visitante. Cuando el texto cifrado se quema por lecturas o por caducidad, no queda una copia en claro en el servidor ni un buzón de soporte que la devuelva. Si el canal fue el equivocado, genera otra contraseña y otro enlace. No recargues el mismo a ver si hay suerte, y no escribas la página de resultado en un repo para «reenviarla».