# Cómo dar acceso a archivos a un agente de IA sin entregarle todo tu sistema de archivos

> Una guía con mentalidad de seguridad para aislar el acceso a archivos de un agente de IA con un servidor MCP de sistema de archivos. Cómo Octofs acota una raíz de trabajo, respeta gitignore, fuerza un shell no interactivo y elimina categorías enteras de errores que el bash y el rm crudos ponen en manos del agente, además de las protecciones que no tiene y cómo configurar a su alrededor.

El directorio había desaparecido antes de que nadie leyera la línea de log que explicaba por qué.

Un agente que ejecutábamos sobre un repositorio de prueba había recibido la orden de "limpiar los artefactos de compilación". Razonó hasta llegar a `rm -rf` con una ruta que ensambló a partir de una variable que, dos turnos antes, se había resuelto silenciosamente a una cadena vacía. `rm -rf /` es la versión de manual de este chiste. La real es más silenciosa: `rm -rf "$BUILD_DIR/"` donde `$BUILD_DIR` es `""`, y ahora estás borrando desde el directorio de trabajo hacia arriba. Tuvimos suerte: era un clon desechable, y la peor baja fue una tarde. Pero he visto suficientes transcripciones de agentes como para conocer la diferencia entre "esto no nos hizo daño" y "esto no puede hacernos daño", y un modelo con un shell crudo está firmemente en la primera categoría.

Ese incidente es la razón entera por la que me importa cómo un agente toca un disco. La lección no es "los modelos son peligrosos". Los modelos no son maliciosos; se equivocan con total confianza, y equivocarse con confianza teniendo `rm -rf` a mano es un problema de seguridad, haya o no intención de daño. La solución es la misma que hemos usado para código no confiable durante cincuenta años: **no le entregues la máquina entera.** Dale una interfaz estrecha, tipada y observable, acota lo que esa interfaz puede alcanzar y haz que las operaciones peligrosas sean o bien imposibles o bien ruidosas.

Esa interfaz, para los archivos, es un servidor MCP de sistema de archivos. Esta entrada trata sobre [Octofs](/blog/introducing-octofs) — el que construimos y ejecutamos — desde un ángulo de seguridad: qué restringe de verdad, qué deliberadamente no restringe y cómo configurarlo para que el próximo `rm -rf` simplemente no tenga nada que agarrar.

---

## El modelo de amenazas: qué concede realmente un shell crudo

Empieza por ser honesto sobre lo que estás cediendo cuando le das a un agente `bash`.

Un shell no es una "herramienta de archivos". Es un lanzador de programas arbitrarios con toda la autoridad ambiental del proceso que lo generó: tu cuenta de usuario, tus variables de entorno, tu agente SSH, tus credenciales en la nube esperando en `~/.aws`, tu kubeconfig, cada volumen montado, el árbol entero desde `/` hacia abajo. Cuando un modelo emite `curl ... | sh`, el shell lo ejecuta. Cuando emite `rm -rf` contra una ruta que alucinó, el shell la borra. Cuando ejecuta `git push` contra el remoto equivocado, el shell empuja. El trabajo del shell es hacer exactamente lo que se le dice, al instante, sin ninguna noción de "¿de verdad querías eso?".

Superpón los modos de fallo del modelo sobre esa autoridad y obtienes un conjunto concreto y recurrente de incidentes:

- **Deriva de rutas.** El agente construye una ruta a partir de una variable, de un resultado de herramienta previo o de su propia memoria del repositorio. La variable está vacía, el resultado fue truncado, la memoria está obsoleta, y la ruta ahora apunta a algún sitio no deseado. Al shell le da igual.
- **Verbos destructivos sobre rutas equivocadas.** `rm -rf`, un `mv` sobre un archivo existente, truncamiento con `> file`, `git checkout -- .` borrando trabajo sin confirmar. Cada uno está a un token de la versión benigna.
- **Leer lo que no debería.** Un `cat ~/.ssh/id_ed25519` o un volcado de `env` que arrastra secretos a la ventana de contexto, donde se registran, se cachean y posiblemente se envían a una API de modelo. La exfiltración no necesita malicia; necesita un agente que "amablemente" incluya el archivo equivocado.
- **Bloqueos interactivos.** Un comando que pide una contraseña o abre un paginador, sin TTY que lo conteste, y la sesión se atasca.
- **Procesos huérfanos.** Un servidor en segundo plano que el agente lanzó y olvidó, todavía ocupando un puerto después de que termine la sesión.

Ninguno de estos requiere un jailbreak. Son el comportamiento _esperado_ de un sistema probabilístico cableado directamente a uno imperativo. La mitigación es arquitectónica: reemplaza el lanzador abierto por un conjunto cerrado de operaciones con nombre, y acota la superficie que esas operaciones pueden alcanzar.

---

## Por qué una herramienta de archivos acotada le gana a `bash` crudo

El argumento a favor de un servidor MCP de sistema de archivos dedicado frente al acceso a shell crudo es el mismo que hicimos para [mantener las herramientas personalizadas en tu repositorio](/blog/custom-mcps-belong-in-your-repo): una herramienta estrecha y con nombre le da al modelo menos formas de equivocarse y te da a _ti_ un lugar donde imponer reglas.

En concreto, cambiar `bash` por una herramienta de archivos cambia cuatro cosas a la vez:

1. **Los verbos son finitos y con nombre.** En lugar de "cualquier programa en el PATH", el modelo ve `view`, `text_editor`, `batch_edit`, `extract_lines`, `shell`, `workdir`. Cada uno tiene un esquema. No hay ninguna operación de `text_editor` que signifique "borra recursivamente un árbol", porque esa operación nunca se definió. No puedes pegarte un tiro en el pie con un arma que no está en el cajón.
2. **La superficie por defecto es tu proyecto, no tu directorio personal.** Octofs toma una raíz de trabajo y resuelve las rutas relativas contra ella. El modo natural de operar del agente — "edita `src/main.rs`" — nunca sale de esa raíz.
3. **Las lecturas están filtradas.** El recorrido de directorios respeta `.gitignore` y omite los dotfiles por defecto, así que `.env`, `.ssh`, `node_modules` y todo lo que ya le dijiste a git que ignorara no aparecen en listados ni en búsquedas de contenido. El agente no lee lo que no puede ver.
4. **Cada operación es observable y reversible.** Las ediciones pasan por escrituras atómicas con hasta diez niveles de deshacer por archivo. No existe el estado "el archivo está a medio escribir y el proceso murió", y hay un `undo_edit` para "esa edición estuvo mal".

El shell no desaparece — los agentes genuinamente necesitan ejecutar compilaciones y pruebas — pero deja de ser la interfaz de archivos _principal_, y eso es casi toda la batalla. Cuantas menos operaciones de archivos pasen por un shell crudo, menos oportunidades tiene el modelo de derivar una ruta hacia algo destructivo.

---

## Qué expone Octofs realmente

Seis herramientas MCP, y esa es la superficie entera. Conocer la lista exacta importa, porque el argumento de seguridad es "estas y nada más".

| Herramienta     | Qué hace                                                                                                                 | Por qué es más segura que el equivalente en shell                                                           |
| --------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `view`          | Leer archivos (uno solo, con rangos de líneas), listar directorios con patrones glob, buscar en el contenido de archivos | Respeta gitignore y filtra dotfiles — reemplaza `cat`/`grep`/`find`/`ls`, sin exponer rutas ignoradas       |
| `text_editor`   | `create`, `str_replace`, `delete`, `undo_edit` sobre un archivo                                                          | Escrituras atómicas, historial de deshacer, coincidencia difusa en lugar de corrupción silenciosa con `sed` |
| `batch_edit`    | Múltiples operaciones de inserción/reemplazo en un archivo, aplicadas de forma atómica                                   | La detección de conflictos rechaza ediciones solapadas o ambiguas antes de tocar el disco                   |
| `extract_lines` | Copiar un rango de líneas de un archivo y anexarlo a otro                                                                | Una copia acotada y tipada en lugar de un pipe hecho a mano con `head`/`tail`                               |
| `shell`         | Ejecutar un comando, en primer plano o en segundo plano                                                                  | Refuerzo de no interactividad, limpieza de grupos de procesos, avisos de uso indebido                       |
| `workdir`       | Obtener, fijar o restablecer el directorio de trabajo de la sesión                                                       | Hace el alcance explícito e inspeccionable en lugar de implícito en un `cd`                                 |

Dos flags de configuración dan forma a todo el conjunto, fijados cuando lanzas el servidor:

```bash
# acotar la raíz de trabajo y usar IDs de línea estables basados en hash
octofs mcp --path /path/to/your/project --line-mode hash
```

`--path` fija la raíz de trabajo. `--line-mode hash` cambia los identificadores de línea de números secuenciales a hashes de 4 caracteres derivados del contenido que se mantienen estables entre ediciones — una característica de fiabilidad, pero también adyacente a la seguridad, porque las referencias estables significan menos errores del tipo "edité la línea equivocada porque los números se desplazaron". También existe `--bind host:port` para ejecutar sobre Streamable HTTP en lugar de stdio cuando necesitas una configuración remota o multicliente.

En una configuración de Claude Desktop, Cursor o Windsurf se ve así:

```json
{
	"mcpServers": {
		"octofs": {
			"command": "/path/to/octofs",
			"args": ["mcp", "--path", "/path/to/your/project", "--line-mode", "hash"]
		}
	}
}
```

Ese `--path` es la decisión de seguridad más importante que tomas con esta herramienta. Es la diferencia entre "el agente opera sobre este proyecto" y "el agente opera sobre mi portátil". Fíjalo en el directorio más estrecho que la tarea necesite.

---

## Las protecciones, ordenadas por la frecuencia con que te salvan

Esto es lo que Octofs impone realmente, fundamentado en el código y no en el folleto.

### Raíz de trabajo acotada

Las rutas relativas se resuelven contra la raíz de trabajo que pasas a `--path` (o el directorio actual si no lo pasas). El agente que dice `view src/main.rs` resuelve a `<root>/src/main.rs`. El modelo mental entero del agente sobre "el proyecto" queda anclado a esa raíz, y mientras trabaje con rutas relativas — que es su comportamiento por defecto — nunca sale.

### Recorrido que respeta gitignore y filtra dotfiles

El listado de directorios y la búsqueda de contenido usan el walker del crate `ignore` con `git_ignore(true)` y los archivos ocultos excluidos por defecto. El efecto práctico: cuando el agente lista un directorio o busca una cadena, no ve `.git/`, `.env`, `.ssh/`, salida de compilación, `node_modules` ni nada en tu `.gitignore`. Los secretos que ya excluiste del control de versiones también quedan excluidos de la vista del agente. Esta es la protección más barata y más infravalorada — el agente no puede filtrar un archivo cuya existencia nunca conoció.

### Escrituras atómicas con historial de deshacer

Cada edición escribe en un archivo temporal en el mismo directorio y luego lo renombra sobre el destino — una operación atómica a nivel de sistema de archivos. El archivo nunca se observa a medio escribir; si el proceso muere a mitad de la edición, el original queda intacto. Los permisos originales del archivo se preservan a lo largo de la escritura (un arreglo que llegó en 0.4.2, específicamente para que una escritura no amplíe silenciosamente el modo de un archivo). Antes de cada escritura, el contenido anterior se guarda como instantánea, hasta diez niveles de profundidad por archivo, accesible a través del `undo_edit` de `text_editor`. "El agente rompió el archivo" deja de ser una operación de recuperación y se convierte en una llamada de herramienta.

### Detección de conflictos y duplicados por lotes

`batch_edit` valida cada operación contra todas las demás _antes_ de escribir. Los rangos de reemplazo solapados se rechazan. Dos inserciones en el mismo anclaje se rechazan por ambiguas. El lote entero tiene un tope de 50 operaciones. Y cada reemplazo se comprueba contra la duplicación de límites — el fallo clásico en el que el modelo incluye una línea extra de "contexto" y la duplica silenciosamente en el archivo. Nada de esto toca el disco hasta que el plan es internamente consistente.

### Refuerzo de shell no interactivo

Cuando el agente sí usa `shell`, los comandos se ejecutan con `stdin` puesto a null, en su propio grupo de procesos, con `GIT_TERMINAL_PROMPT=0`, `DEBIAN_FRONTEND=noninteractive`, `PAGER=cat` y `GIT_PAGER=cat`. Un comando que pediría una credencial falla rápido en lugar de atascar la sesión. Un comando que paginaría la salida la vuelca de forma plana en lugar de invocar `less` contra un TTY inexistente. La sesión no puede atascarse en un aviso interactivo que el modelo no puede contestar.

### Limpieza de grupos de procesos al apagar

Los comandos en primer plano se generan en su propio grupo de procesos y se rastrean por PID. Ante `SIGTERM` o EOF, Octofs envía `SIGKILL` a cada grupo rastreado — incluyendo nietos — así que nada que un comando dejara a medio correr sobrevive a la sesión. Los comandos en segundo plano (`background: true`) son la excepción deliberada: se separan precisamente para poder sobrevivir a la llamada, Octofs le entrega al agente su PID, y matarlos cuando el trabajo termina es tarea del agente — `kill <pid>`.

### Avisos de uso indebido del shell

Este es un empujón, no un muro, pero moldea el comportamiento a lo largo de una sesión. Cuando el agente ejecuta `cat`, `grep`, `find`, `ls`, `sed` o `awk` a través de `shell`, Octofs anexa un aviso a la respuesta dirigiéndolo hacia la herramienta dedicada: usa `view` para leer o buscar, usa `text_editor` para editar. A lo largo de una sesión el agente aprende a encaminar las operaciones de archivos por las herramientas que tienen la red de seguridad, en lugar del shell que no la tiene. Cuantos menos verbos destructivos fluyan por la interfaz abierta, mejor.

---

## Lo que Octofs no hace — y por qué te lo cuento

Una entrada de seguridad que solo enumere protecciones es marketing. Aquí está el límite, dicho claramente, porque configurar a su alrededor correctamente depende de saber dónde está.

**Octofs no es una jaula chroot.** La raíz de trabajo acota las rutas _relativas_. No confina las rutas _absolutas_. `resolve_path` tiene dos ramas: una ruta relativa se une a la raíz de trabajo; una ruta absoluta se usa tal cual. No hay rechazo de recorrido con `..` ni comprobación de que la ruta resuelta permanezca bajo la raíz. Si el modelo emite `view /etc/passwd` o `text_editor` contra `/Users/you/.ssh/config`, Octofs resolverá esa ruta absoluta y operará sobre ella. El alcance es un _default_ fuerte y una _convención_ fuerte — el agente que trabaja con rutas relativas nunca abandona el proyecto — pero no es una frontera endurecida contra una ruta que deliberadamente o accidentalmente se vuelve absoluta.

Este es un punto de diseño deliberado, no un descuido: Octofs es una herramienta de productividad para un agente cooperativo, no una capa de contención para uno hostil. Pero cambia cómo deberías desplegarlo cuando el radio de destrucción importa:

- **Ejecútalo contra un directorio dedicado, no tu hogar.** El `--path` más estrecho que puedas permitirte. Si la tarea solo necesita un repo, apúntalo a ese repo.
- **Para aislamiento estricto, usa el OS.** Una herramienta de archivos no puede vencer al kernel en su propio terreno de sandboxing. Si genuinamente no confías en lo que el agente podría hacer — código no confiable, un bucle autónomo, un runner de CI — ejecuta Octofs y el agente dentro de un contenedor, una VM o una cuenta de usuario que _físicamente no pueda_ ver tus secretos, con solo el directorio del proyecto montado. Entonces la brecha de rutas absolutas es irrelevante, porque no hay nada fuera del montaje a lo que llegar. Defensa en profundidad: la herramienta acotada reduce el caso común; el sandbox del OS atrapa la ruta que se vuelve absoluta.
- **Mantén los secretos fuera de la raíz de trabajo, y fuera de git.** Como el recorrido respeta gitignore, los secretos que ya hayas ignorado en git permanecen invisibles para listados y búsqueda. Esa es una reducción real de exposición accidental — aprópiate de ella. Una credencial que el agente no pueda enumerar es una credencial que no puede pegar en una ventana de contexto.

El resumen honesto en una línea: Octofs elimina los errores _accidentales_ — el `rm -rf` con variable vacía, el `sed` corrompido en silencio, el archivo a medio escribir, el paginador colgado — y reduce la superficie por defecto. No contiene, por sí solo, a un agente determinado o profundamente confundido que construya una ruta absoluta. Empárelo con un sandbox del OS cuando necesites esa garantía.

---

## Configurarlo de forma estricta: una lista de verificación

Si quieres la máxima seguridad que esta herramienta puede ofrecerte, configúrala así:

1. **Fija `--path` al directorio de proyecto más estrecho que la tarea necesite.** No tu hogar, no `/`, no un padre que contenga cinco repos más. Un proyecto, una raíz.
2. **Ignora tus secretos en git antes de apuntar un agente al repo.** `.env`, `*.pem`, archivos de credenciales, configuración local. Se vuelven invisibles para listados y búsquedas de `view`. Es gratis y funciona.
3. **Usa `--line-mode hash` para cualquier agente de edición multipaso.** Las referencias de línea estables significan menos errores de "edité la línea equivocada", lo cual es fiabilidad y seguridad a la vez.
4. **Prefiere las herramientas tipadas sobre `shell` en las instrucciones de tu agente.** Los avisos de uso indebido empujan en esta dirección; refuérzalo. Cada archivo leído a través de `view` en lugar de `cat` es uno que respeta las reglas de ignore.
5. **Controla las operaciones destructivas en la capa de permisos de tu runtime de agente.** Octofs expone las herramientas; tu runtime decide cuáles necesitan aprobación. Autoaprueba `view`. Pregunta siempre para `shell` y `delete` de `text_editor`. Aquí es donde el humano permanece en el bucle para los verbos que no se pueden deshacer con `undo_edit`.
6. **Cuando el radio de destrucción sea real, mete todo en un contenedor.** Monta solo el directorio del proyecto. Ahora la brecha de rutas absolutas está cerrada por el kernel, no por la esperanza.

El cambio de mentalidad es el punto. No estás confiando en que el modelo sea cuidadoso. Estás organizando el mundo para que la descuido tenga un radio de destrucción pequeño — una raíz acotada, lecturas filtradas, escrituras reversibles, un shell no interactivo y un directorio de trabajo explícito.

---

## Preguntas frecuentes

**¿Octofs impide que un agente ejecute `rm -rf`?**

No directamente — `shell` aún ejecuta comandos arbitrarios, incluyendo `rm`. Lo que cambia Octofs es que la _edición_ y _lectura_ de archivos ya no necesitan el shell, así que se generan muchos menos comandos destructivos de shell en primer lugar; y la raíz de trabajo más el recorrido consciente de gitignore hacen que las operaciones naturales del agente se mantengan dentro del proyecto. Para una garantía estricta de que `rm -rf` no pueda alcanzar nada importante, ejecuta Octofs dentro de un contenedor con solo el proyecto montado.

**¿La raíz de trabajo es un sandbox?**

Es un default acotado, no una jaula. Las rutas relativas se resuelven contra la raíz; las rutas absolutas se usan tal cual, sin rechazo de `..`. Trata `--path` como "dónde trabaja el agente", no "a qué está confinado el agente". Para confinamiento, usa un sandbox a nivel de OS.

**¿Cómo evita que los secretos filtren al contexto?**

El listado de directorios y la búsqueda de contenido son conscientes de `.gitignore` y omiten dotfiles por defecto. Los archivos que ya excluiste de git — `.env`, archivos de claves, cualquier cosa en `.gitignore` — no aparecen en listados ni búsquedas, así que el agente no puede leerlos en el contexto. La defensa es tan buena como tu `.gitignore`, así que curátelo antes de apuntar un agente al repo.

**¿Qué pasa con un archivo a medio escribir si el proceso se bloquea durante la edición?**

No puede pasar. Las escrituras van a un archivo temporal en el mismo directorio y se renombran sobre el destino de forma atómica. Si el proceso muere, el original está intacto. Los permisos se preservan a lo largo de la escritura.

**¿Por qué seis herramientas en lugar de simplemente exponer el sistema de archivos?**

Un conjunto finito y nombrado de operaciones es la característica de seguridad. El modelo no puede buscar un verbo "borra-árbol" que nunca se definió, y obtienes un único lugar — el límite de la herramienta — para hacer cumplir escrituras atómicas, deshacer, detección de conflictos y reglas de ignore. Herramientas más pocas y más estrechas también son más fáciles de seleccionar correctamente para el modelo.

**¿Funciona fuera de Octomind?**

Sí. Octofs habla MCP sobre stdio o Streamable HTTP y funciona con cualquier cliente MCP — Claude Desktop, Cursor, Windsurf, Zed — además del runtime de agente de Octomind.

---

El agente que casi borró ese directorio de prueba aún se ejecuta en nuestra configuración todos los días. El modelo no es más cuidadoso de lo que era. Simplemente tiene mucho menos que romper.

Lo que cambió es el mundo que lo rodea: lee a través de `view`, edita a través de `text_editor`, trabaja dentro de un `--path` acotado a un repo, y cuando las apuestas son reales, todo se sienta dentro de un contenedor que no puede ver nada más.

Dale a un agente acceso a archivos. Solo no le des los tuyos.

— Don

---

_[Octofs](https://github.com/muvon/octofs) es código abierto bajo Apache-2.0 — un único binario en Rust, versión actual 0.5.0. El alcance, el recorrido consciente de gitignore, las escrituras atómicas y el shell no interactivo descritos aquí están todos en el código fuente; la advertencia sobre rutas absolutas también. Si necesitas una protección que no está ahí, [abre un issue](https://github.com/muvon/octofs/issues)._
