Skip to main content
MIRAGE ships SlackVFS in two runtimes:
  • @struktoai/mirage-node, talks to https://slack.com/api/* directly using a bot token (and optionally a user token for search.messages).
  • @struktoai/mirage-browser, stays secret-free: a small proxy server on your backend holds the token and forwards /api/slack/* to https://slack.com/api/*. The browser only ever sees the proxy URL.
Both runtimes expose the same filesystem shape (/slack/channels/, /slack/dms/, /slack/users/) and the same shell commands, and both pair with the slack CLI for acting on the workspace.

Get a bot token

  1. Visit the Slack API basics page and create an app for your workspace.
  2. Under OAuth & Permissions, add the bot scopes you need. The minimum for read access is:
    • channels:history, channels:read
    • groups:history, groups:read
    • im:history, im:read
    • users:read
  3. For posting messages, also add chat:write.
  4. For search.messages, Slack requires a user token (xoxp-…) with search:read. Bot tokens (xoxb-…) get not_allowed_token_type. Provide it via the optional searchToken field.
  5. Install the app to your workspace and copy the Bot User OAuth Token (xoxb-…).

Node (server-side)

Browser

The browser package never sees the bot token. Instead, point it at a relative URL that your backend proxies to https://slack.com/api/*, attaching the Authorization: Bearer … header server-side.

1. Server: minimal proxy

2. Browser: wire it up

Calling https://slack.com/api/* directly from a browser fails CORS. The proxy is mandatory, Slack does not set permissive CORS headers.

Filesystem layout

Each channel or DM directory contains day-partitioned directories for the last 90 days (or since channel creation). Each date directory contains chat.jsonl plus a files/ directory for attachments shared that day. Each user file is the full profile JSON returned by users.profile.get. The Slack ID is embedded after __ in directory and file names so you can extract it for the VFS-specific commands without an extra lookup.

Shell commands

Every standard MIRAGE shell command works on the mounted Slack tree: Acting on Slack (sending, reacting, pins, member info, search) goes through the slack CLI when installed; the mounted tree stays read-oriented.

Troubleshooting

search.messages requires a user token (xoxp-…) with search:read scope. Set searchToken on the SlackConfig:
Without it, workspace-scope rg and slack search fail; channel-scope rg (e.g. rg foo /slack/channels/general__C…/) still works since it streams the JSONL files directly.
The browser cannot call https://slack.com/api/* directly, Slack does not set permissive CORS headers. You must run the proxy server shown above (or your own equivalent) and point proxyUrl at it.
SlackVFS uses an IndexCacheStore (default TTL 600s) to deduplicate channel / user / date listings, but high-volume reads of .jsonl files can still hit Slack’s per-method rate limits. Cache hits avoid the API entirely; tune indexTtl if your workspace changes slowly. Per Slack docs, Tier 3 methods like conversations.history allow ~50 requests / minute / workspace.

Examples

See Python Slack VFS for the equivalent Python wiring.

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 instead of using unrestricted provider search. 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.