Skip to main content
The Discord VFS exposes guild, channel, and member data as a virtual filesystem mounted at some prefix such as /discord/. For token setup, see Discord Setup.

Config

Fields marked secret are redacted in snapshots.

VFS.md

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

Virtual Filesystem

VFS Layout

/<path>
  • A guild, channel, member or attachment name is <display-name>__<id>. The display name keeps Discord’s spelling (spaces, apostrophes, emoji); only / becomes ∕ (U+2215). ls the parent first to find exact names, and quote a name that has spaces.
  • Messages are JSONL; use jq for fields such as .content, .author.username and .attachments. A day’s files/ may be empty.
  • Without start_time/end_time, a channel lists the latest 30 days up to its newest message; a date glob finds earlier days.
  • With start_time/end_time, listings cover that scope; messages and attachments outside it are absent, direct paths included. Users and channel metadata stay available as context.
  • grep and rg read the files they reach, so grep needs -r for a directory. Under a channel that reads every listed day, attachments included: they download from the CDN, and binary ones such as JPG or PDF produce noise.
Time scope: start_time=unbounded (inclusive), end_time=unbounded (exclusive). Explicit paths and globs cannot escape this scope.

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 <cmd> or <cmd> --help for one command’s usage.
Display names keep their original spelling from Discord (spaces, apostrophes, emoji are all preserved). Only / is replaced with ∕ (U+2215) so it cannot collide with a directory boundary. The Discord snowflake ID is appended after __ (double underscore) on guild, channel, member, and attachment names so VFS specific commands can extract it without an extra lookup, and so two same named entities never collide. Quote names containing spaces in shell commands. stat also exposes the ID in the extra dict (see Finding IDs).

Guilds

The root lists one directory per guild the bot has access to.

Channels

/discord/<guild>/channels/ lists text channels (types 0, 5, 15). Each channel directory contains day-partitioned directories for the 30 days leading up to the channel’s last message. Each day directory holds:
  • chat.jsonl, the day’s messages (one JSON object per line).
  • files/, attachments posted on that day. Each blob is named <stem>__<attachment-id>.<ext>, where the stem keeps the original filename’s spelling (only / is replaced). The ID suffix keeps the filename collision-free. cat’ing a blob downloads it from the Discord CDN.
The date range is derived from last_message_id on the channel object, so inactive channels show dates around their last activity, not today. Soft errors (403 missing permissions, 404 unknown channel, 429 rate limit) on a single day are swallowed so listings, find, and grep keep working across the rest of the tree.

Members

/discord/<guild>/members/ lists one .json file per member. Reading a member file returns the full member payload from the Discord API.

Smart Commands

grep / rg

grep -r and rg read every day’s chat.jsonl under the directory they walk, so a channel or guild costs one history fetch per day. Name a day directory or a date glob to search a large channel. Discord’s own search is not used: a reply carries the message it answers (referenced_message), which that search does not tie to the reply’s day, so it cannot say which days hold a word.

head / tail

head and tail on file-level paths use the Discord messages API directly (GET /channels/{id}/messages) instead of downloading the full day’s history.

Cache

The Discord VFS uses IndexCacheStore (same as S3, disk and GitHub). Index entries store guild IDs, channel IDs, and last_message_id for date range computation. File bytes go through the workspace file cache.

Integration

What Discord answers through its own API, so fewer files are read. Integration explains each command.

Example

See examples/python/vfs/discord/discord.py for the full working example.

Finding IDs

VFS-specific commands require Discord snowflake IDs (channel_id, guild_id, message_id). These can be extracted from the filesystem:

Working with Large Channels

Tips for efficient access on busy channels:

Shell Commands

Standard commands available on the mounted Discord tree: Acting on Discord (sending, editing, reactions, threads, polls, member and guild info, search) goes through the discord CLI when installed; the mounted tree stays read-oriented. The <name>__<id> path segments supply the snowflake IDs the CLI flags take.

Time scope

Set start_time and/or end_time in the mount’s YAML config (or pass start_time/end_time to Python; startTime/endTime to the TypeScript constructor). Bounds are RFC3339 timestamps with an explicit timezone and at most millisecond precision. Start is inclusive; end is exclusive. An omitted or null bound is open. Start must precede end.
With a bound configured, channel listings cover the selected period rather than the default recent window. Day directories use UTC. Messages are selected by creation time, and attachments follow their parent messages. Direct paths, stat, date globs, and grep/rg cannot escape the scope. Scoped searches scan the filtered files. User/member and channel/server metadata remain visible as context. These settings apply to the VFS, not to separately installed account CLIs. mode: read prevents mount writes; it does not freeze upstream edits or deletions. Rebuild/remount the VFS to change its scope so cached listings and bytes are cleared.