{"id":37081911,"url":"https://github.com/lydakis/sandfs","last_synced_at":"2026-01-14T09:59:09.141Z","repository":{"id":324562812,"uuid":"1097675080","full_name":"lydakis/sandfs","owner":"lydakis","description":"Composable virtual filesystem sandbox with a UNIX-like command surface for agents.","archived":false,"fork":false,"pushed_at":"2025-11-24T05:41:06.000Z","size":258,"stargazers_count":3,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-28T08:59:59.548Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lydakis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-16T16:20:05.000Z","updated_at":"2025-12-26T10:14:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lydakis/sandfs","commit_stats":null,"previous_names":["lydakis/sandfs"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lydakis/sandfs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydakis%2Fsandfs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydakis%2Fsandfs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydakis%2Fsandfs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydakis%2Fsandfs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lydakis","download_url":"https://codeload.github.com/lydakis/sandfs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydakis%2Fsandfs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28416414,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T08:38:59.149Z","status":"ssl_error","status_checked_at":"2026-01-14T08:38:43.588Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-14T09:59:08.052Z","updated_at":"2026-01-14T09:59:09.134Z","avatar_url":"https://github.com/lydakis.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sandfs\n\n`sandfs` is an experimental Python package that implements an entirely virtual filesystem (VFS) that can be embedded inside AI agent tooling. Each sandbox keeps its own private directory tree, supports dynamic nodes that are hydrated on demand, and exposes a small UNIX-like command surface (`cd`, `ls`, `cat`, `grep`, `rg`, etc.) so agents can reuse familiar workflows.\n\n\u003e **Status**: design prototype. APIs are subject to change.\n\n## Why?\n\nAgent builders frequently need a scratch filesystem for planning, iterating on generated code, and testing hypotheses. Shipping a simple `exec` tool that proxies every bash command to the host disk is unsafe. `sandfs` keeps those operations inside a controlled, in-memory sandbox while still feeling like a mini Linux environment.\n\n## Features\n\n- In-memory directories and files that never touch the host disk unless exported.\n- Dynamic nodes backed by callables (e.g., query a DB, fetch from an API, or generate text on the fly).\n- Pure-Python shell/executor that understands a handful of GNU-style commands and can be extended with your own.\n- Optional Python execution helper that evaluates snippets against the sandbox state only.\n- Bridge to real GNU utilities via `host -p \u003cpath\u003e \u003ccommand\u003e` which materializes the subtree and runs the host binary against it.\n- Node policies (read-only, append-only, visibility labels) plus shell views so different agents see only the nodes they are allowed to.\n- Write hooks and optimistic versions so hosts can flush files to external stores with conflict detection.\n- Storage adapters and snapshots so you can mount external state and roll forward/back within a turn.\n- Serialization helpers to snapshot or hydrate sandboxes (planned).\n\nSee [`docs/roadmap.md`](docs/roadmap.md) for the up-to-date view of supported commands and the upcoming backlog.\n\n## Installation\n\n```bash\npip install git+https://github.com/lydakis/sandfs.git\n# Once a PyPI release is available: pip install sandfs\n```\n\n## Quickstart\n\n```python\nfrom sandfs import VirtualFileSystem, SandboxShell\n\nvfs = VirtualFileSystem()\nvfs.write_file(\"/notes/todo.txt\", \"- build VFS\\n- add rg support\\n\")\n\nshell = SandboxShell(vfs)\nprint(shell.exec(\"ls /notes\").stdout)\nprint(shell.exec(\"rg 'VFS' /notes\").stdout)\n```\n\n### Agent shell usage\n\n```python\nfrom sandfs import ProvidedNode, SandboxShell, VirtualFileSystem\n\nvfs = VirtualFileSystem()\nvfs.write_file(\"/workspace/app.py\", \"print('hello')\\n\")\n\ndef logs_provider(ctx):\n    return {\"latest.log\": ProvidedNode.file(content=\"generated on demand\")}\n\nvfs.mount_directory(\"/workspace/logs\", logs_provider)\n\nshell = SandboxShell(vfs)\nshell.exec(\"cd /workspace\")\nprint(shell.exec(\"ls\").stdout)\nprint(shell.exec(\"tree\").stdout)\n```\n\n### Running host GNU tools\n\n`SandboxShell` exposes `host` to materialize a subtree onto the host disk and invoke any installed command:\n\n```\nhost -p /workspace grep -n TODO app.py\n```\n\nThe example above exports `/workspace` into a temporary directory, runs the system `grep` inside it, then discards the files.\n\n\u003e Tip: unknown commands automatically fall back to the host runner, so `echo`, `python3`, or `bash -lc '…'` work even if they are not native sandfs commands.\n\n### Policies \u0026 views\n\n```python\nvfs.set_policy(\n    \"/blue/identity/persona.md\",\n    NodePolicy(writable=False, classification=\"private\", principals={\"alice\", \"bob\"}),\n)\nshell = SandboxShell(vfs, view=VisibilityView(classifications={\"public\"}, principals={\"alice\"}))\n```\n\n### Agent shell mode\n\n```python\nshell = SandboxShell(\n    vfs,\n    allowed_commands={\"ls\", \"cat\", \"rg\"},\n    max_output_bytes=8_192,\n)\n```\n\nCommands outside `allowed_commands` now return an error immediately, and outputs larger than `max_output_bytes` are rejected so partner-facing agents cannot dump oversized payloads.\n\n### Storage adapters\n\n```python\nfrom sandfs import MemoryStorageAdapter, VirtualFileSystem\n\nadapter = MemoryStorageAdapter(initial={\"a.txt\": \"seed\"})\nvfs = VirtualFileSystem()\nvfs.mount_storage(\"/data\", adapter)\n\nprint(vfs.read_file(\"/data/a.txt\"))\nvfs.write_file(\"/data/a.txt\", \"update\")\nvfs.sync_storage(\"/data\")  # refresh from adapter if it changed externally\n```\n\n`mount_storage` keeps the virtual tree synchronized with the adapter, while `sync_storage` refreshes the VFS from the latest adapter contents.\n\n### Snapshots\n\n```python\nsnap = vfs.snapshot()\n# run risky operations ...\nvfs.restore(snap)\n```\n\nSnapshots capture the entire tree (including storage-backed nodes) so you can checkpoint before an agent action and roll back on failure.\n\n## Repository layout\n\n```\n.\n├── docs/               # design/vision notes\n├── sandfs/             # library sources\n└── tests/              # runtime smoke tests\n```\n\n## Local development\n\n```\npython -m venv .venv\nsource .venv/bin/activate\nuv pip install -e .[dev]\npre-commit install\nuv run pytest\nuv run ruff check .\nuv run mypy sandfs\n```\n\n### Persistence hooks\n\nRegister a write hook to flush files into your own store and use optimistic versions to avoid clobbering concurrent updates:\n\n```python\nfrom sandfs import VirtualFileSystem\nfrom sandfs.hooks import WriteEvent\n\nvfs = VirtualFileSystem()\n\ndef flush(event: WriteEvent) -\u003e None:\n    save_to_db(event.path, event.content, event.version)\n\nvfs.register_write_hook(\"/blue/work\", flush)\nvfs.write_file(\"/blue/work/note.md\", \"draft\")\nvfs.write_file(\"/blue/work/note.md\", \"final\", expected_version=1)\n```\n\n### Integration hooks\n\n```python\nfrom sandfs.integrations import InboxRecorder\n\nrecorder = InboxRecorder()\nrecorder.attach(vfs, \"/blue/inbox\")\n```\n\n`PathEvent` (used under the hood) carries the path, event type (`create`, `update`, `delete`), and latest content so you can fan writes into staging queues or audit logs.\n\n## License\n\nMIT\n\n## Further reading\n\n- [Using sandfs with Blue](docs/blue.md)\n- [Publishing sandfs](docs/publishing.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flydakis%2Fsandfs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flydakis%2Fsandfs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flydakis%2Fsandfs/lists"}