We are excited to announce the release of Mirage 0.0.6 πππ
Mirage 0.0.6 is a permissions release. 0.0.5 gave an agent typed programs to act with; 0.0.6 decides what it may do with them. A permissions document replaces the old guard list: allow, ask and deny rules, one role per session, mounts that narrow rather than gate, hidden subtrees that stay hidden through a mutation, an approval door where an asked line reaches a person, and a decision ledger you can query and explain. A policy can now be a program, written in Python or JavaScript and judged at the command, op and session doors. Credentials move behind a secrets plane instead of sitting in a config block. Underneath, a long deduplication pass collapses the backends onto shared HTTP, object-store, hierarchy and render kits, the integration fakes become one TypeScript and Prisma kit that runs off a laptop, bash grows most of tiers 2 and 3, guests see one filesystem through a single stat struct, and Hugging Face Hub arrives as both a mount and a CLI. Python and TypeScript stay mirrored throughout.
pip install mirage-ai==0.0.6 Β· npm install @struktoai/mirage-core@0.0.6 (and -node, -browser, -server, -cli, -agents)
@struktoai/mirage-dsh joins the shared version line at 0.0.6. It was published once as 0.0.1; the versions between were never released.
Highlights
- Permissions are a document, not a guard list. A permissions document with profiles replaces the guards (#857), carrying allow, ask and deny rules with an approval door where an asked line reaches a person (#865). A session holds one role, and a mount narrows what that role may reach instead of gating it (#876). The path axis gained show entries, per-subtree modes and hide reasons (#896), and a mutation below a hidden path resolves without revealing it (#901). Every decision lands in a ledger with
explainand whole-line judging (#880), reachable through an asks door and workspace-level list-asks, allow and deny (#899). Documented, with(approval <id>)renamed(ask <id>)(#900). - A profile's policy can be a program. A profile takes a script rather than only coded rules (#884), and the policy block's program defines which gate hooks exist:
pre_command,pre_opsandpre_session, probed once per program text, with a hook left out meaning silence and none at all failing closed (#997). The op-door boundary is stated and pinned (#908), codedpre_opsbinds on the command tier (#909), and dsh routes allow, ask and deny through its own approval channel (#890). - Credentials live on a secrets plane. Env-plane managed credentials arrive first (#960), then a config plane and a 1Password source (#973), and finally a mount or CLI credential that reads from that plane rather than holding the value (#978). Alongside it, every TypeScript mount config now validates through one door, with the two languages' field sets gated against each other (#994).
- The backends collapse onto shared kits. One HTTP client kit with notion and discord as exemplars (#835), then every hand-rolled client onto it (#841). One JSON render kit for
.jsonand.jsonlbodies (#823). An object-store kit and a hierarchy kit (#848), taking hf and opfs (#858), postgres, mongodb and the google trio (#868), trello, linear, notion and gcal (#870), discord, slack, gmail and email (#874), and lancedb and qdrant with per-mount routes (#875). The 42 S3-alias resources collapse onto a shared base (#866), and a TypeScript backend is assembled from oneCommandIOtable (#883). - The integration fakes are one TypeScript and Prisma kit. The mock servers unify (#916) and the services follow: dropbox, box and onedrive (#919), slack, notion and mem0 (#925), the hf buckets (#927), databricks, dify and http (#928), and github (#935). They can run somewhere other than a developer's laptop (#905), a run axis rides the URL so two hosts stop sharing one world (#945, #946, #947), and one launcher hosts them all behind pooled sessions (#965).
- Hugging Face Hub is a mount and a CLI. A Hub repo mounts, with the
hfCLI beside it (#931), exercised against a fake in CI (#934), read-only by default (#937), and serving Hub metadata and search (#939). The fake answershf_fserrors the way the live server does (#974), advertises the discovery scopes it claims (#982), serveshf://README.mdbecause the tool document tells the model to read it (#976) and/raw/(#979). - bash tiers 2 and 3. The export attribute and identifier validation (#816); associative arrays, declare attributes,
readonly -fand the jobs flags (#824); thenlet,umask,shopt,alias,mapfile, thereadflags,declare -g/-n,disown/waitandexec(#833). - Guests see one filesystem. Every path-taking
osverb routes through the workspace (#877),symlink,readlinkandsetattrare served (#879), a readdir row carries the link mark (#881), and one stat struct sits at the runtime door with each surface projecting it (#882). Engines and sandbox SDKs are upgraded with the monty Python and TypeScript guest-behavior gaps closed (#904), and the history view and root anchor are withheld from runtimes (#932). Two new providers: a smolvm microVM and a confined sandlock Python runtime (#834), and ssh (#852). - Errors say what the kernel would say. A prefix holding no keys is ENOENT rather than an empty directory (#809, #815), a path under a file is ENOTDIR rather than ENOENT (#813), a directory read carries GNU's errno and exit code (#895),
rmdirrefuses a non-empty directory at the op rather than only in the builder (#854), and every refusal now speaks bash'sPermission deniedinstead of each command's own wording (#993). - Redis reaches the browser through Upstash's REST API, so a browser workspace can use the same cache and store as node (#992).
Breaking Changes
- Guards are gone. The permissions document and its profiles replace them (#857); a session holds one role and a mount narrows rather than gates (#876).
build_resourceis synchronous again (#820), reversing the 0.0.5 change; github hydrates lazily instead. Code written against 0.0.5'sawait build_resource(...)drops the await.FileTypeis the node kind, and content type is its own axis (#878).- core is imported by module path.
index.tsdrops from 1589 lines to 102, so a deep import replaces the barrel (#805), and the dead exports around it are swept (#814). - Builtin commands are per-command packages behind a
BUILTINStable with tiers (#853). ls -lreports the workspace user as owner and the session profile as group (#985).- A Hub mount is read-only (#937).
/dev/nulland/dev/zeroare character devices, not regular files (#910).getState/loadStatesit on the TypeScriptResourcecontract, mirroring Python's base (#800).- Routing is renamed and execution passes through an
ExecContextseam (#944). @struktoai/mirage-dshmoves from its own version line onto the shared one, from0.0.1to0.0.6(#998).
Features
- permissions: the document and profiles (#857); allow, ask and deny with an approval door (#865); one role per session (#876); the path axis, per-subtree modes and hide reasons (#896); the asks door and workspace list-asks/allow/deny (#899).
- policy: decision ledger,
explainand whole-line judging (#880); profiles written by a script (#884); a policy block whose program defines the gate's hooks (#997). - secrets: env-plane managed credentials (#960); config plane and 1Password source (#973); mount and CLI credentials read from the plane (#978).
- cli: virtual
ghworkflows (#915); builtin command catalogs (#929); custom authoring made first-class (#950) with an example (#940) and specs validated at construction (#933); thehfCLI (#931). - shell: bash tier 2 stage 1 (#816); associative arrays, declare attributes,
readonly -f, jobs flags (#824);let,umask,shopt,alias,mapfile,readflags,declare -g/-n,disown/wait,exec(#833). - runtime: smolvm microVM and confined sandlock Python (#834); the ssh sandbox provider (#852); every engine and sandbox SDK upgraded (#904).
- resource: a Hugging Face Hub mount (#931, #937, #939); Redis in the browser over Upstash (#992); mount parents, kit backend ops and a resource class reference (#887).
- core: optional byte-range reads across backends (#810).
- watch:
delta_hookfor ten more backends (#811); push event mapping and subtree invalidation (#817). - integ: a restricted-token GitHub fake,
gh api --jqand notion retrieve-block (#819); branch history recorded when a ref moves (#969); the kit migration (#916, #919, #925, #927, #928, #935) and the run axis (#945, #946, #947, #965).
Bugfixes
- cache: a rename evicts the destination's own listing, across thirteen backends, with disk Windows separators and databricks copy invalidation (#802); a recursive delete or directory rename evicts its subtree (#864, #863); the cache manager walks the ancestor chain (#838); the Redis file cache
addis atomic (#830). - errors: a prefix with no keys is ENOENT (#809, #815); a path under a file is ENOTDIR (#813); a directory read uses GNU's errno and exit code (#895);
rmdirrefuses at the op (#854). - ls: a cross-mount listing is relayed so GNU headers and the global sort survive (#844); a nested mount's root is listed in its parent's
-Rlisting (#847). - rename: directories move whole, on every backend that fakes them (#808).
- search: the grep and rg push-downs are gated on operand count (#851), and the remaining find push-downs with them (#856).
- shell: an orphaned
$varexpands in a redirect target, andrecordis forwarded through the daemon (#926); the tree-sitter engine boots once (#912). - naming: every
<label>__<id>name is budgeted in bytes, not characters (#885); the gmail excerpt and daemon description budgets are measured in code points (#871). - backends: box rename and copy refuse a destination they were destroying (#898); the dropbox emptiness probe bounds its walk (#860) and stat and du stop laundering a listing 5xx into a false ENOENT (#963); postgres refuses a readdir on a schema that does not exist (#894); sharepoint scopes the site and drive id cache to the accessor (#988); the browser presigned S3 client refuses a version-pinned read (#840); discord fixes dirname-parsed scope, tombstoned attachments and date-less grep paths (#797); a glob narrows a windowed listing instead of filtering it (#902).
- fuse: a driverless machine is told which driver to install (#862), and an OSError is caught when mfusepy cannot find libfuse (#829).
- runtime:
/dev/stderrstays alive so a python timeout cannot kill the host (#922). - namespace: every namespace-table write is gated on its session's grant (#943).
- parity: github ref default, NAME_MAX byte budget, the browser registry on node's config shape (#873); trello write guards, fenced revisions, browser ambient fallback (#938); the two hosts' extension surfaces aligned (#986).
- corpus: ten agent-corpus defects across sed, awk, date, find and the fakes (#822); tar and unzip member selectors, relay extraction, grep file filters and direct path execution (#821).
- security: fast-uri, fastify and qs advisories (#991), deepmerge-ts raised to 8 (#845), and the deepagents floor raised to what the API backend needs (#826).
Changes
- layout: module homes and workspace/config placement (#837); constants in constants and shapes in types (#936); helpers split by concern with the Python and TypeScript symbols converged (#942); find strict numerics, tar and git constants, and the
parse_flagsconvention (#948). - cleanup: the 37-item plan closed with the printf engine split (#843), then
execute_nodesplit,candidates()deleted and the integ facets landed (#849); the nested-function rule, layout parity, ts-audit and ls facets (#831); dead code removed behind an optionaldeadcodedependency (#980); survivor renames across github, gcal, msgraph, sharepoint and mem0 (#827); duplicated config, sanitizer, render and drive-op bodies collapsed (#869); the browser resource layer inheritsBaseResource(#872). - ci: TypeScript is bootstrapped once and its dist reused (#889); pytest runs under xdist (#892) without coverage on every invocation (#913); mypy is cached (#917); example checks run concurrently (#918); Ruff replaces flake8 and autoflake (#924); pre-commit hook environments are cached (#954); three fixed costs leave the integ workflow (#941); the two integ CLI hosts run concurrently (#842);
examples/typescriptis typechecked (#812); a script refuses to run against a stalenode_modules(#832). - integ: the truth txt files are retired for JSON (#839).
- observe: op timing is owned in the record path (#968).
- docs: the navigation is restructured (#903); the architecture figure is redrawn and the READMEs rewritten (#975, #964, #977); sandbox runtime documentation is organized (#850); upstream CLI projects and licenses are credited (#966); a Haystack integration page (#828); how a view reaches a handler, with the opt-in helper deleted (#930); the permissions system (#900); CLI page icons, frontmatter and the removed filetype renderers (#801, #803, #804, #806, #807).
Contributors
@zechengz, @bytecii, @sonhmai, @Beverly621, @Jackpkn
New Contributors
- @Beverly621 made their first contribution in #830
- @Jackpkn made their first contribution in #829
Full Changelog: v0.0.5...v0.0.6