Skip to content
Docs

Box Mount

Box is a cloud content platform companies use to store and govern documents. Box Mount maps a Box folder to a directory inside a Vercel Sandbox and keeps the two in sync while the sandbox runs. An agent works at that path with standard filesystem calls: reading a file, creating one, listing a directory, and editing one all behave normally. It signs in to Box the way a person would, so it gets the same view that account gets: the same limits on what it can open, and the same record of what it changed. Because the content lives in Box, the output the agent writes remains after the sandbox is deleted.

Box Mount is in private preview with Box. It is not publicly available, it is not covered by a production SLA, and its features can change before general availability. Box provides the binary and setup guidance to preview participants. To request access, complete Box's private preview form.

Box Mount runs a background sync process inside the sandbox. When it starts, it fills the mounted directory with the Box folder's contents. From then on, files the agent writes go up to Box, and changes people make in Box are available in the directory.

This is not the same as the S3 mounts on Mount remote storage. Those use a FUSE driver, which passes each read straight through to the remote store, so nothing is kept in the sandbox. Box Mount keeps real files in the sandbox and syncs them, so reads are local and the mounted content takes up sandbox disk.

The vercel-labs/vercel-box-mount example runs this setup end to end. It reviews a synthetic contract in a sandbox and checks that the review is still in Box after the sandbox is deleted.

Work through these steps in order.

Before you start, you need:

  • Access to the Box Mount private preview, including the binary. Box Mount runs on Linux for x86_64 and ARM64, which covers the managed Sandbox images.
  • A Box app in the Developer Console configured for OAuth 2.0 or JWT, and its credentials saved as a box-config.json file. See Configure credentials.
  • The ID of the Box folder to mount, which is the number at the end of the folder's URL in Box.
  • A Vercel project with Sandbox access, and the JS SDK. The user isolation below is not available in the Python SDK.

Upload the preview archive and put the binary on PATH:

index.ts
import { Sandbox } from '@vercel/sandbox';
import { readFile } from 'node:fs/promises';
 
const sandbox = await Sandbox.create();
const root = sandbox.asUser('root');
 
await sandbox.writeFiles([
  {
    path: '/vercel/sandbox/box-mount.tar.gz',
    content: await readFile(process.env.BOX_MOUNT_ARCHIVE),
    mode: 0o600,
  },
]);
 
await root.runCommand({
  cmd: 'bash',
  args: [
    '-c',
    `tar -xzf /vercel/sandbox/box-mount.tar.gz -C /usr/local/bin &&
     chmod 755 /usr/local/bin/box-mount`,
  ],
});

The Box credentials are a Box API identity, and that identity is usually broader than the folder you mount, so anything that can read them can act as that app or user against all of Box. Box recommends splitting the work across two Unix users, and Sandbox multi-user support provides them. The boxmount user holds the credentials and runs the sync. The boxagent user gets the mounted folder and nothing else.

Set the share permissions while the directory is still empty, before the first mount fills it. setfacl comes from the acl package, which the managed images do not preinstall:

index.ts
const boxmount = await sandbox.createUser('boxmount');
const agent = await sandbox.createUser('boxagent');
const workspace = await sandbox.createGroup('boxworkspace');
await sandbox.addUserToGroup('boxmount', 'boxworkspace');
await sandbox.addUserToGroup('boxagent', 'boxworkspace');
 
const dataPath = '/home/boxmount/.box-mount';
 
await sandbox.writeFiles([
  {
    path: '/vercel/sandbox/box-config.json',
    content: await readFile(process.env.BOX_CONFIG_PATH),
    mode: 0o600,
  },
]);
 
await root.runCommand({
  cmd: 'bash',
  args: [
    '-c',
    `mkdir -p ${dataPath} && chown boxmount:boxmount ${dataPath} && chmod 700 ${dataPath} &&
     install -o boxmount -g boxmount -m 600 /vercel/sandbox/box-config.json ${dataPath}/box-config.json &&
     rm /vercel/sandbox/box-config.json &&
     (command -v setfacl >/dev/null || (apt-get update -qq && apt-get install -y -qq acl)) &&
     chown boxmount:boxworkspace ${workspace.sharedDir} &&
     chmod 2770 ${workspace.sharedDir} &&
     setfacl -m g:boxworkspace:rwx -m d:g:boxworkspace:rwx ${workspace.sharedDir}`,
  ],
});

Check the split before you mount. Running test ! -r /home/boxmount/.box-mount/box-config.json as boxagent should exit 0, and never start the agent with the BOX_* variables in its environment.

Run the mount as the boxmount user:

index.ts
await boxmount.runCommand({
  cmd: 'box-mount',
  args: [
    '--data-path',
    dataPath,
    'mount',
    workspace.sharedDir,
    process.env.BOX_FOLDER_ID,
  ],
});
 
const status = await boxmount.runCommand({
  cmd: 'box-mount',
  args: ['--data-path', dataPath, 'status'],
});
console.log(await status.stdout());

The agent now works against workspace.sharedDir with normal file operations, and Box Mount syncs its writes back to the folder.

Stopping a sandbox kills the sync process, and deleting it discards the filesystem along with anything that never reached Box. Unmount first, and check the status before you do: box-mount status reporting No mount found. means the sync is already gone and local edits have not reached Box.

index.ts
await boxmount.runCommand({
  cmd: 'box-mount',
  args: ['--data-path', dataPath, 'unmount', workspace.sharedDir],
});
 
await sandbox.stop();

To check that the sync landed, read the file back through the Box API and compare its hash after the sandbox is gone.

  • Box asks you to keep preview mounts off production workloads, and sets a size and file count per mount. See Box's workload limits for the current values.
  • Box Mount does not support atomic save, where an application writes a temporary file and renames it over the original. Files saved that way sync as a new file instead of a new version, and lose their Box version history.
  • Developer tokens expire after 60 minutes and cannot be refreshed. Use OAuth 2.0 or JWT for a mount that outlives one token.
  • A session ends at its timeout, which defaults to 5 minutes, whether or not a mount is still running. Size the timeout to cover the work and the unmount.
Last updated September 23, 2026

Was this helpful?

supported.