Archil.Disk reference
Branch
Section titled “Branch”Source:
packages/archil/src/Branch.ts
An independent, writable fork of a disk, started from a committed checkpoint in about 35 ms whatever the disk’s size. Writes on the branch never reach its source or its siblings. Branches share the disk’s credentials, so a disk user can mount any branch of its disk.
Archil has no API to delete a branch yet, so destroying the resource
only removes it from Alchemy’s state. Branches made per session at run
time come from BranchDisk.
Branch: Creating a Branch
Section titled “Branch: Creating a Branch”export const Staging = Effect.gen(function* () { const seed = yield* Seed; const base = yield* Base; return yield* Archil.Branch("Staging", { disk: seed.diskId, fromCheckpoint: base.name, });});BranchDisk
Section titled “BranchDisk”Source:
packages/archil/src/BranchDisk.ts
Fork one disk per session or task at run time: each branch is an
independent, writable copy started from a committed checkpoint in about
35 ms, whatever the disk’s size. Seed the disk once at deploy time with an
Checkpoint, then branch from it per request.
Mount a branch on your own machines with MountDisk and branch.
Branches share the disk’s credentials. Sandboxes can’t mount a branch.
BranchDisk: Branching per Session
Section titled “BranchDisk: Branching per Session”export default class Api extends Cloudflare.Worker<Api>()( "Api", { main: import.meta.url }, Effect.gen(function* () { const seeds = yield* Archil.BranchDisk(Seed); return { fetch: Effect.gen(function* () { const branch = yield* seeds.branch({ name: "session-42", fromCheckpoint: "base" }); return yield* HttpServerResponse.json(branch); }), }; }).pipe(Effect.provide(Archil.BranchDiskHttp)),) {}Checkpoint
Section titled “Checkpoint”Source:
packages/archil/src/Checkpoint.ts
A named, point-in-time snapshot of a disk, the seeded base that branches
fork from. Alchemy runs the run commands against the disk, then takes
the checkpoint in the same microVM, so the checkpoint holds exactly what
they produced. A checkpoint takes about 15 ms whatever the disk’s size,
and never changes after it’s taken.
Archil can’t delete checkpoints yet, so destroying the resource only removes it from Alchemy’s state.
Checkpoint: Seeding a Base
Section titled “Checkpoint: Seeding a Base”export const Seed = Archil.Disk("Seed", {});export const Base = Effect.gen(function* () { const seed = yield* Seed; return yield* Archil.Checkpoint("Base", { disk: seed.diskId, name: "base", run: [ "git clone --depth 1 https://github.com/alchemy-run/alchemy repo", "cd repo && corepack enable && pnpm install --frozen-lockfile", ], });});CreateDisk
Section titled “CreateDisk”Source:
packages/archil/src/CreateDisk.ts
Create disks at run time, one per user, session, or task, by name. This is how most apps use Archil: a disk mounts in about 50 ms, so it can belong to a single request. The client returns full disk handles (exec, grep, objects, branches, disk tokens), the same shape the deploy-time bindings return.
Objects created at run time are tagged with the stack, stage and binding
(alchemy::stack, alchemy::stage, alchemy::id) and are not deleted
when the stack is destroyed. List them by those tags to clean them up.
Archil has no tags, labels or descriptions, so the tags are a name
prefix, alc-<id>-<hash of stack/stage/id>: every name this client
creates is <prefix>-<name>, and prefix on the client is the value.
CreateDisk: Disks per User
Section titled “CreateDisk: Disks per User”export default class Api extends Cloudflare.Worker<Api>()( "Api", { main: import.meta.url }, Effect.gen(function* () { const disks = yield* Archil.CreateDisk(); return { fetch: Effect.gen(function* () { const request = yield* HttpServerRequest; const user = request.headers["x-user"] ?? "anonymous"; const home = yield* disks.create(`user-${user}`); const result = yield* home.exec("ls -la /mnt/archil"); return HttpServerResponse.text(result.stdout); }), }; }).pipe(Effect.provide(Archil.CreateDiskHttp)),) {}CreateDisk: Cleaning Up
Section titled “CreateDisk: Cleaning Up”const disks = yield* Archil.CreateDisk();for (const disk of yield* disks.list()) { yield* disks.delete(disk.name.slice(disks.prefix.length + 1));}Source:
packages/archil/src/Disk.ts
An Archil disk: a durable filesystem that mounts in about 50 ms on any Linux machine, sandbox, or serverless exec, with hot data on SSD and the rest in object storage. Keep its files in Archil-managed storage, or in your own bucket as ordinary objects.
Declare a disk at deploy time for the few long-lived ones an app shares,
like a dataset or a seeded base you branch from. Disks that belong to one
user, session, or task are created at run time with
CreateDisk, which returns the same disk shape.
Archil has no tags, so Alchemy owns a disk through its generated name.
A disk with a fixed name that already exists is adopted only with
--adopt.
Disk: Creating a Disk
Section titled “Disk: Creating a Disk”Archil-managed storage
export const Dataset = Archil.Disk("Dataset", {});On your own S3 bucket, with no keys
export const Store = AWS.S3.Bucket("Store", {});export const Dataset = ArchilAWS.S3Disk("Dataset", { bucket: Store, prefix: "datasets/" });On an R2 bucket
export const Dataset = Archil.Disk("Dataset", { storage: { type: "r2", bucketName: "datasets", bucketEndpoint: "https://<account>.r2.cloudflarestorage.com", accessKeyId: Alchemy.Secret("R2_ACCESS_KEY_ID"), secretAccessKey: Alchemy.Secret("R2_SECRET_ACCESS_KEY"), },});Disk: Restricting Access
Section titled “Disk: Restricting Access”export const Dataset = Archil.Disk("Dataset", { allowedIps: ["203.0.113.0/24"], allowSandboxMount: false,});Disk: Binding to a Runtime
Section titled “Disk: Binding to a Runtime”Object bindings give each host its own S3 user on this disk (read-only
for ReadDisk), so the host never holds an API key.
Read and write the disk from a program sandbox
export default class Api extends Archil.Sandbox<Api>()( "Api", { main: import.meta.url }, Effect.gen(function* () { const files = yield* Archil.ReadWriteDisk(Dataset); return { fetch: Effect.map(files.getText("README.md"), (text) => HttpServerResponse.text(text)), }; }).pipe(Effect.provide(Archil.ReadWriteDiskHttp)),) {}Mount it into a sandbox
export const Box = Archil.Sandbox("Box", { mounts: [{ disk: Dataset, path: "/data", mode: "read-only" }],});Mount it in a Cloudflare Container
const dataset = yield* Archil.MountDisk(Dataset, { path: "/data", mode: "read-only" });const path = yield* dataset.mount();Disk: Runtime Operations
Section titled “Disk: Runtime Operations”Run commands and read files
const dataset = yield* Archil.ExecDisk(Dataset);const files = yield* Archil.ReadDisk(Dataset);const result = yield* dataset.exec("wc -l /mnt/archil/*.csv");const readme = yield* files.getText("README.md");Upload a large file and share a link
const files = yield* Archil.ReadWriteDisk(Dataset);yield* files.upload("exports/2026-11.parquet", body);const url = yield* files.share("exports/2026-11.parquet", 3600);Disk: Branches and Checkpoints
Section titled “Disk: Branches and Checkpoints”export const Seed = Archil.Disk("Seed", {});export const SeedBase = Effect.gen(function* () { const seed = yield* Seed; return yield* Archil.Checkpoint("SeedBase", { disk: seed.diskId, name: "base", run: ["git clone https://github.com/acme/app ."], });});// runtimeconst seeds = yield* Archil.BranchDisk(Seed);const branch = yield* seeds.branch({ name: `session-${id}`, fromCheckpoint: "base" });DiskUser
Section titled “DiskUser”Source:
packages/archil/src/DiskUser.ts
Access to one disk. A token user gets a disk token for archil mount; an
s3 user gets an access key pair for the disk’s S3-compatible API; an
IAM user lets an AWS principal mount with its own identity, so no secret
exists at all. Archil checks each one itself, so a host holding a disk
user can’t reach any other disk. readOnly, expiresAt and oneUse
narrow it further.
Archil only shows a token or secret key once, when it’s created, so
Alchemy stores it in state as a Redacted value. Destroying the user
revokes it. Archil.MountDisk(disk) and the object bindings
(Archil.ReadDisk, Archil.WriteDisk, Archil.ReadWriteDisk) create
one per host for you.
DiskUser: Creating a Disk User
Section titled “DiskUser: Creating a Disk User”A token for one machine
const user = yield* Archil.DiskUser("Indexer", { disk: dataset.diskId });// ARCHIL_MOUNT_TOKEN=<user.token> archil mount <disk> /mnt/data --region aws-us-east-1An ECS task role that mounts with its own identity
const user = yield* Archil.DiskUser("Workers", { disk: dataset.diskId, principal: taskRole.roleArn,});A read-only token that expires
const user = yield* Archil.DiskUser("Viewer", { disk: dataset.diskId, readOnly: true, expiresAt: "2027-01-01T00:00:00Z",});DiskUser: S3 Keys
Section titled “DiskUser: S3 Keys”const reader = yield* Archil.DiskUser("Reader", { disk: dataset.diskId, type: "s3", readOnly: true,});// reader.accessKeyId, reader.secretAccessKey sign requests to// https://s3.green.us-east-1.aws.prod.archil.com/<disk id>/<key>ExecDisk
Section titled “ExecDisk”Source:
packages/archil/src/ExecDisk.ts
Run serverless commands against one disk, and search it, from a Worker,
Lambda, or any other host. Each exec boots a fresh microVM with the
disk mounted at /mnt/archil, runs the command to completion (up to five
minutes), and shuts down, so you pay only for the call. Files the command
writes stay on the disk.
The host calls Archil with the API key you deploy with: Archil only creates API keys from the console and can’t scope them to one disk.
ExecDisk: Running Commands
Section titled “ExecDisk: Running Commands”export default class Api extends Cloudflare.Worker<Api>()( "Api", { main: import.meta.url }, Effect.gen(function* () { const dataset = yield* Archil.ExecDisk(Dataset); return { fetch: Effect.gen(function* () { const result = yield* dataset.exec("wc -l /mnt/archil/*.csv"); return HttpServerResponse.text(result.stdout); }), }; }).pipe(Effect.provide(Archil.ExecDiskHttp)),) {}ExecDisk: Searching
Section titled “ExecDisk: Searching”const hits = yield* dataset.grep({ pattern: "TODO", directory: "src", recursive: true });ExecDisk: Recovering From a Crashed Mount
Section titled “ExecDisk: Recovering From a Crashed Mount”A mounted client holds write delegations on the paths it writes. When it crashes without unmounting, reclaim them so other clients can write.
for (const d of yield* dataset.delegations()) { if (d.isOrphaned) yield* dataset.revokeDelegation(d);}ExecDisks
Section titled “ExecDisks”Source:
packages/archil/src/ExecDisks.ts
Run one serverless command against several disks at once, e.g. read a shared dataset and write into a user’s own disk in the same call. Disks are named at run time, so it works with disks created per user.
ExecDisks: Running Across Disks
Section titled “ExecDisks: Running Across Disks”const exec = yield* Archil.ExecDisks();const result = yield* exec.exec({ disks: { data: { disk: datasetId, readOnly: true }, home: userDisk.diskId, }, command: "cp -r /mnt/archil/data/samples /mnt/archil/home/",});MountDisk
Section titled “MountDisk”Source:
packages/archil/src/MountDisk.ts
Mount an Archil disk where your program runs. MountDisk(disk, options)
returns a mount reference: the fields of a mounts entry (disk,
path, mode, …) plus mount().
- In an
Archil.Sandboxprogram, the sandbox mounts the disk natively: the binding adds the entry to the sandbox’smounts, andmount()returns the path at once. - On your own machines (a Cloudflare Container, an ECS task, a Fly
Machine, or a VM), the binding installs the Archil client into the
host’s image and checks at plan time that the host provides FUSE.
mount()runs the client for the life of the scope that called it. The host gets its own disk user (read-only formode: "read-only"), so Archil refuses it any other disk and it never holds an API key.
MountDisk() mounts whatever disk the program names, with a disk token
it receives at run time (e.g. from a control Worker’s
DiskHandle.createToken).
The client reaches Archil over the network, so start a Cloudflare
Container with container.start({ enableInternet: true }).
MountDisk: Mounting in a Sandbox Program
Section titled “MountDisk: Mounting in a Sandbox Program”export default class Api extends Archil.Sandbox<Api>()( "Api", { main: import.meta.url }, Effect.gen(function* () { const dataset = yield* Archil.MountDisk(Dataset, { path: "/data", mode: "read-only" }); const fs = yield* FileSystem.FileSystem; return { fetch: Effect.gen(function* () { return yield* HttpServerResponse.json(yield* fs.readDirectory(dataset.path)); }).pipe(Effect.orDie), }; }).pipe(Effect.provide(Archil.MountDiskLive)),) {}MountDisk: Mounting on Your Own Hosts
Section titled “MountDisk: Mounting on Your Own Hosts”Read a dataset in a Cloudflare Container
export default class Indexer extends Cloudflare.Container<Indexer>()( "Indexer", { main: import.meta.url }, Effect.gen(function* () { const dataset = yield* Archil.MountDisk(Dataset, { path: "/data", mode: "read-only" }); const fs = yield* FileSystem.FileSystem; return { fetch: Effect.scoped( Effect.gen(function* () { const path = yield* dataset.mount(); return yield* HttpServerResponse.json(yield* fs.readDirectory(path)); }), ), }; }).pipe(Effect.provide(Archil.MountDiskLive)),) {}Write through a mount in an ECS task on EC2
const scratch = yield* Archil.MountDisk(Scratch, { path: "/scratch" });// runtimeconst path = yield* scratch.mount();yield* fs.writeFileString(`${path}/result.json`, JSON.stringify(result));MountDisk: Passing a Mount to a Sandbox
Section titled “MountDisk: Passing a Mount to a Sandbox”const workspace = yield* Archil.MountDisk(Workspace, { path: "/workspace", mode: "conditional" });const sandboxes = yield* Archil.CreateSandbox();// runtimeconst sandbox = yield* sandboxes.create({ name: `task-${id}`, mounts: [workspace] });MountDisk: Mounting Disks Named at Run Time
Section titled “MountDisk: Mounting Disks Named at Run Time”const archil = yield* Archil.MountDisk();const path = yield* archil.mount({ disk: job.diskId, token: job.token, path: "/work" });ReadDisk
Section titled “ReadDisk”Source:
packages/archil/src/ReadDisk.ts
Read a disk’s files as objects over Archil’s S3-compatible API, with no machine and no mount: get, stream, head, list, and presigned share URLs. Reads see what mounted clients have written.
The host gets its own read-only S3 user on this disk: its keys can only
GET and HEAD this disk’s files, and the host never holds an API key.
Destroying the stack revokes the user.
ReadDisk: Reading Files
Section titled “ReadDisk: Reading Files”Serve files from a disk in a Worker
export default class Api extends Cloudflare.Worker<Api>()( "Api", { main: import.meta.url }, Effect.gen(function* () { const files = yield* Archil.ReadDisk(Dataset); return { fetch: Effect.gen(function* () { const readme = yield* files.getText("README.md"); return HttpServerResponse.text(readme); }), }; }).pipe(Effect.provide(Archil.ReadDiskHttp)),) {}Stream a large file to the response
return HttpServerResponse.stream(files.getStream("exports/data.parquet"));ReadDisk: Sharing Files
Section titled “ReadDisk: Sharing Files”const url = yield* files.share("reports/2026-11.pdf", 3600);ReadWriteDisk
Section titled “ReadWriteDisk”Source:
packages/archil/src/ReadWriteDisk.ts
Read and write a disk’s files as objects: everything ReadDisk
and WriteDisk do, in one client. The host gets its own
read-write S3 user on this disk, and never holds an API key.
ReadWriteDisk: Reading and Writing
Section titled “ReadWriteDisk: Reading and Writing”const files = yield* Archil.ReadWriteDisk(Dataset);yield* files.put("uploads/photo.jpg", bytes, { contentType: "image/jpeg" });const url = yield* files.share("uploads/photo.jpg", 600);WriteDisk
Section titled “WriteDisk”Source:
packages/archil/src/WriteDisk.ts
Write a disk’s files as objects over Archil’s S3-compatible API: put,
append, copy, delete, and multipart uploads of any size. The host gets
its own read-write S3 user on this disk, so it can’t reach any other
disk and never holds an API key. A write that conflicts with a mounted
client’s write delegation fails with ObjectConflict; ExecDisk’s
delegations and revokeDelegation reclaim paths a crashed client
still holds.
WriteDisk: Writing Files
Section titled “WriteDisk: Writing Files”const files = yield* Archil.WriteDisk(Dataset);yield* files.append("events.ndjson", JSON.stringify(event) + "\n");WriteDisk: Uploading Large Files
Section titled “WriteDisk: Uploading Large Files”const files = yield* Archil.WriteDisk(Dataset);const request = yield* HttpServerRequest;yield* files.upload("uploads/video.mp4", request.stream, { contentType: "video/mp4" });