Skip to main content
Mirage ships DropboxVFS in two runtimes:
  • @struktoai/mirage-node, uses client_id + client_secret + long-lived refresh token to fetch short-lived access tokens server-side.
  • @struktoai/mirage-browser, supports the same refresh token via PKCE (no client secret in the bundle).
Both runtimes hit the same Dropbox v2 HTTP API: /2/files/list_folder for directories, /2/files/download for content, and /2/files/upload / create_folder_v2 / delete_v2 / move_v2 / copy_v2 for writes (single-call uploads cap at ~150 MB). Credentials are obtained the same way in both runtimes, see Dropbox Credentials. The Python package ships the same backend, see Dropbox (Python).

Node (server-side)

The DropboxTokenManager caches the access token in memory and refreshes it ~5 minutes before expiry, so cold-start API calls don’t pay the refresh round-trip.

Config

Fields marked secret are redacted in snapshots.

Mount a subfolder

Pass rootPath to expose a single Dropbox folder as the mount root instead of the whole account. Every command and FUSE/VFS op is scoped to that folder; paths outside it are unreachable.
rootPath accepts Team/data, /Team/data, or /Team/data/ (all normalized the same way); .. segments are rejected. Config dictionaries (e.g. via the VFS registry) may spell it root_path. Omitting it (or passing /) mounts the account root as before.

VFS.md

The VFS.md an agent reads with Dropbox mounted at /<path>.

Virtual Filesystem

Supported Unix Commands for VFS

Unix commands work on each path as on a local filesystem, as far as the path supports them:

Limitations of Unix Commands for VFS

Where a path differs from a local filesystem:Run man to list every command, and man <cmd> or <cmd> --help for one command’s usage.

Integration

What Dropbox answers through its own API, so fewer files are read. Integration explains each command. The Python Dropbox docs list when each search answers and what it can miss.

Names missing from a listing

A name missing from a cached listing that the running command did not fetch is looked up on Dropbox by path before mirage answers that it does not exist: one get_metadata for a stat, or the download itself for a read. A file another client created after the listing is therefore found, and >>, touch and sed -i act on it instead of replacing it. A listing the running command fetched itself is trusted as is, for the whole command. Each command of a script is its own command, but one long-running command, such as a program a runtime runs, does not see a file another client creates after it listed the folder. Some lookups run outside any shell command: FUSE, programmatic calls, [ ] and [[ ]] tests, and a redirect on anything but a simple command (a brace group, subshell, loop, if or case). No listing is the caller’s own there, so each lookup of a name missing from a cached listing costs one request.

Reads under fresh

Under read: fresh, a cached file is checked against its content_hash before it is served: one get_metadata per warm read, never a folder listing. A download names the hash in its Dropbox-API-Result header, full or ranged, so a read stamps it with no extra request. server_modified is not used: Dropbox repeats it across same-size rewrites.

Writes

A write records the content_hash its upload reply names, so a fresh read right after a tee or sed -i downloads nothing.

Conditional writes

A Dropbox mount accepts write: conditional. A write that holds a version looks the file up once (get_metadata), refuses a file whose content_hash changed since it was read, and uploads in update mode with the file’s rev, so Dropbox refuses (409 path/conflict) a change that lands in between; a delete sends the rev as parent_rev. rm -r walks the folder file by file, and a mv within the mount is Dropbox’s own move, which is not refused when its source changed (a mv to another mount is a copy and a conditioned delete, so there a changed source stays and mv reports the failed removal).

Browser (PKCE, no client secret)

Set the redirect URI on your Dropbox app’s Settings tab to your dev/prod origin (e.g. http://localhost:5173/vfs/dropbox/dropbox_pkce.html). The bundled examples/browser/vfs/dropbox/dropbox_pkce.ts runs the full PKCE dance end-to-end and persists the refresh token to localStorage.

VFS mode (patchNodeFs)

Mirage exposes a patchNodeFs(workspace) shim that routes fs calls under a mount through the workspace, so any library that uses Node’s fs API can read directly from Dropbox without code changes.
The async forms (fs.promises.* and the callbacks) are routed. Sync forms like fs.statSync and fs.readFileSync refuse a mounted path because remote reads can’t block the event loop.

FUSE mode

For tools that need a real filesystem path (CLI tools, editors, system utilities), mount the workspace under FUSE:
Requires macFUSE on macOS or libfuse on Linux. See FUSE setup.

Available commands

DropboxVFS ships the same shell command set as the GDrive VFS:
  • Filesystem: ls, cat, head, tail, nl, wc, stat, find, tree, du, file, realpath, basename, dirname
  • Write (on a WRITE mount): tee, touch, mkdir, rm, rmdir, mv, cp, truncate, shell redirection (> / >>)
  • Search/text: grep, rg, awk, sed, sort, uniq, cut, diff, cmp, jq
  • Encoding: base64

Examples

End-to-end runnable scripts are in examples/typescript/vfs/dropbox/: grep classifies the bytes it reads, including downloaded attachments. Use -I to skip binary inputs or -a (--binary-files=text) to search them as text. Text mode bypasses the content-search index and scans files directly.