Based on a real day; names and selected personal details have been changed or omitted.

Hosting Datasets with Runloop

May 11 · 10:45 AM · User Source JSON

tools like runloop ? Specifically for " i have datasets i want to host on a filesystem somewhere and i want to spin up arbitrary computers running claude code / codex cli with read only access to those files" how would you do this ?

May 11 · 10:45 AM · Assistant Source JSON

I’ll treat this as an architecture question plus a “what hosted sandbox tools exist right now” question. I’ll verify current Runloop-style products, then give the practical build path.

May 11 · 10:45 AM · Assistant Source JSON

Yes. I’d build this as object storage + ephemeral agent sandboxes + a writable scratch disk, not as “one giant shared Linux filesystem.”

The shape

R2/S3/GCS dataset bucket
  └── mounted/read-only or synced into sandboxes

Orchestrator API
  ├── creates sandbox
  ├── injects task + model keys
  ├── mounts dataset read-only
  ├── runs claude/codex CLI
  ├── streams logs
  └── saves result artifacts back to bucket/db

Agent sandbox
  ├── /data        read-only dataset
  ├── /workspace   writable scratch / repo clone
  ├── /tmp         disposable
  └── network      restricted

The important trick: do not make the agent’s working directory read-only. Claude Code / Codex-style tools want to write plans, temp files, caches, patches, etc. Give them /workspace as writable, and mount your corpus at /data read-only.

Use Runloop if you want the cleanest “coding agent box”

Runloop’s thing is basically this: secure cloud “Devboxes” for AI agents, with VM isolation and a full execution environment. Their docs describe Devboxes as sandboxed environments for agents, and the product is explicitly positioned around launching agents in secure code sandboxes. citeturn370428search1turn370428search4

For your use case:

  1. Build a custom devbox image with:

    • claude
    • codex
    • ripgrep
    • python
    • uv
    • node
    • your search/index tooling
    • maybe duckdb, sqlite, jq, fd
  2. On job start:

    • create devbox
    • mount or download dataset shard to /data
    • mark /data read-only
    • run claude --dangerously-skip-permissions only inside the sandbox, or safer equivalent with sandbox/network limits
    • stream stdout/stderr
    • persist /workspace/results
  3. Destroy or snapshot the box.

Runloop is probably best if your unit of work is: “spin up a remote coding computer, let an agent inspect files, write scripts, iterate, and produce an output.”

Alternatives

E2B: good if you want fast API-created sandboxes. E2B says it provides isolated sandboxes where agents can execute code, run commands, and interact with the filesystem; it also has docs specifically for running Claude Code, Codex, and Amp in secure sandboxes. citeturn673054search0turn673054search20

Daytona: similar category, maybe more “full composable computer.” Daytona describes sandboxes with dedicated kernel, filesystem, network stack, vCPU, RAM, and disk, and exposes lifecycle/filesystem/process APIs. citeturn673054search5turn673054search1

Modal: better if you think of this as scalable batch compute rather than “agent devboxes.” Modal has Sandbox for running untrusted/agent code and Volumes optimized for write-once/read-many workloads. It also supports cloud bucket mounts for S3/R2/GCS-style storage. citeturn673054search18turn673054search10turn673054search6

Morph Cloud: interesting if you care about snapshot/fork/resume devboxes. Morph positions Devboxes as programmable cloud development environments for CLI agents like Claude Code, Codex, and Gemini CLI. citeturn673054search7turn673054search15

What I’d actually do for you

Given your “huge corpus / Alpha Research / agentic knowledge workers” thing:

V0: cheap and good

Use Cloudflare R2 for data, Runloop or Modal for compute, and Postgres/D1 for jobs.

Dataset layout:

r2://alpha-corpora/
  gutenberg/
    raw/
    normalized/
    metadata.parquet
    shards/
      shard-00000.jsonl.zst
      shard-00001.jsonl.zst
  gov-archives/
  newspapers/

Each job gets:

/data
  /manifest.json
  /shards/shard-00042.jsonl.zst
/workspace
  /task.md
  /scratch.py
  /notes.md
  /answer.md

Your orchestrator creates jobs like:

type AgentJob = {
  id: string
  model: "claude-code" | "codex"
  prompt: string
  datasetRefs: string[]
  allowedCommands?: string[]
  networkMode: "off" | "allowlist" | "open"
  maxRuntimeSeconds: number
}

For most research tasks, do not mount the whole corpus. Give the agent:

  • a manifest
  • metadata DB/parquet
  • a retrieval CLI
  • selected shards
  • read-only raw data

The agent should call tools like:

search-corpus "sierra club 2024 lawsuit energy campaign"
open-doc gutenberg/12345
sample-shard newspapers/nyt-1982 --n 100
duckdb metadata.duckdb -c "select ..."

This is much better than letting Claude Code wander a 20 TB filesystem.

Read-only dataset mechanics

Best options, ranked:

  1. Object store + signed read-only credentials

    • The sandbox can only read s3:GetObject, ListBucket for a prefix.
    • No write/delete permissions.
    • Best security boundary.
  2. Mount object store read-only

    • S3/R2 via Mountpoint, FUSE, provider-native mount.
    • Convenient, but watch perf and weird filesystem semantics.
  3. Pre-copy selected shards into the sandbox

    • Slow for huge jobs, but simplest.
    • Good for V0.
  4. Network file system

    • EFS/NFS/CephFS.
    • Useful if you need POSIX semantics.
    • More ops burden, more security risk.

For agent jobs, I’d prefer object storage + local scratch. Filesystems are nice until 200 agents start recursively grepping the same shared mount.

Security rules that matter

You want three separate boundaries:

Filesystem

  • /data read-only
  • /workspace writable
  • no host mounts
  • no home directory secrets
  • no Docker socket
  • no cloud metadata endpoint

Network

  • ideally off by default
  • allow only model API + object store + your orchestrator
  • block arbitrary outbound internet if working over private datasets

Credentials

  • per-job short-lived creds
  • read-only dataset creds
  • write-only result upload path
  • no global R2/S3 key in env

Anthropic’s Claude Code sandboxing docs/blog emphasize exactly this kind of split: filesystem isolation plus network isolation, with configurable allowed paths and domains. citeturn370428search3turn370428search24

The “agent can’t work on read-only folders” gotcha

Do not run Claude Code with its current directory inside /data. Keep it in /workspace and tell it:

The dataset is mounted at /data and is read-only.
Do not attempt to modify files in /data.
Write scripts, notes, indexes, and outputs under /workspace.

There are current reports/issues around read-only sandbox modes behaving unexpectedly or not matching what users expect, so I would enforce read-only at the mount/credential/container layer, not rely only on the agent CLI’s own sandbox flag. citeturn370428search18turn370428search6

My preferred stack

For fastest build:

Runloop + R2 + small orchestrator

  • Runloop: agent computer
  • R2: dataset storage
  • D1/Postgres: job table
  • Worker/Hono: job API
  • Durable Object: stream logs / job status
  • rg, duckdb, python, custom search-corpus CLI inside image

For cheaper/high-scale batch:

Modal + R2/S3 + custom CLI agent runner

  • Modal sandboxes/functions for elastic compute
  • Volumes or CloudBucketMounts for data
  • Better for “run 10,000 corpus jobs”
  • Less ideal if you need interactive Claude Code-style devbox behavior

For fully self-hosted:

Kubernetes + Firecracker/Kata/gVisor + S3

  • Harder, but gives control.
  • Each job is a pod/microVM.
  • Mount /data read-only from S3/FUSE or prefetch.
  • Run agent as non-root.
  • Use NetworkPolicy to restrict egress.

Minimal V0 implementation

  1. Put corpus in R2.
  2. Create a Docker image:
FROM node:22-bookworm

RUN apt-get update && apt-get install -y \
  ripgrep fd-find jq python3 python3-pip pipx curl unzip git duckdb \
  && rm -rf /var/lib/apt/lists/*

RUN npm install -g @anthropic-ai/claude-code
# install codex cli however you're using it

WORKDIR /workspace
  1. On each job:
    • create sandbox from image
    • inject ANTHROPIC_API_KEY or OpenAI key
    • inject short-lived read-only R2 credentials
    • copy task.md into /workspace
    • either mount or sync selected dataset prefix into /data
    • run:
cd /workspace
claude -p "$(cat task.md)" \
  --allowedTools "Bash,Read,Write,Edit,Grep,Glob"

Or for Codex:

cd /workspace
codex exec --sandbox workspace-write "$(cat task.md)"
  1. Upload:
    • /workspace/answer.md
    • /workspace/artifacts/*
    • logs
    • tool call transcript if available

The non-obvious improvement

Give agents a corpus tool, not just files.

Instead of:

“Here is 5 TB mounted at /data, figure it out.”

Give:

corpus search "query" --dataset sierra-club --top-k 100
corpus open doc_id
corpus grep "phrase" --dataset ngo-filings
corpus sql "select ..."
corpus sample --where "year=2024 and source='irs_990'"

Then Claude/Codex can write code when needed, but most work becomes high-signal corpus interrogation. This also lets you log every read and prevent accidental exfiltration.

Bottom line

Use Runloop if you want “remote Claude Code/Codex computers.” Use Modal if you want scalable batch compute over datasets. Use R2/S3 as the source of truth, mounted or copied read-only into each sandbox. Always give the agent a writable /workspace; enforce read-only with storage credentials/mount flags, not just the agent’s own settings.

May 11 · 10:46 AM · User Source JSON

does runloop have permanent storage that can be quickly mounted to arbitrary devboxes ?

May 11 · 10:46 AM · Assistant Source JSON

Probably not in the exact “persistent shared volume instantly attachable to arbitrary devboxes” sense.

What Runloop clearly has:

  1. Devbox disk snapshots
    You can save the current disk state of a Devbox and create new Devboxes from that snapshot. Their docs say snapshots are for saving disk state, improving build times, rolling back, and fan-out attempts. They also say “currently only disk snapshots are supported.” citeturn273130search2

  2. Files/build-context mounts for building blueprints
    Their files/mounts docs mention build contexts stored as temporary Storage Objects with TTLs. That sounds more like “upload files into the build context / image build process,” not a reusable permanent dataset volume mounted into arbitrary running devboxes. citeturn273130search1

So: Runloop gives you reusable snapshots, not obviously a first-class EFS-style persistent volume service.

For your dataset use case, I would not rely on Runloop-native storage as the corpus layer. I’d do:

R2/S3/GCS = permanent dataset store
Runloop snapshot = prebuilt agent environment
Runloop devbox = ephemeral worker
/data = read-only mounted/synced dataset shard
/workspace = writable agent scratch

The best pattern:

Base snapshot:
  claude/codex/ripgrep/duckdb/python/node installed
  your corpus CLI installed
  no giant dataset inside

Per job:
  launch devbox from snapshot
  attach short-lived read-only R2/S3 creds
  mount or sync needed dataset prefix to /data
  run agent
  upload outputs
  destroy devbox

If you want true quickly mountable persistent volumes, I’d look harder at Modal Volumes / CloudBucketMounts, Daytona, Deno Sandbox Volumes, or self-hosted EFS/NFS/CephFS + Firecracker/K8s. Runloop seems strongest for “agent devbox lifecycle + snapshots,” less clearly for “giant shared read-only corpus volume.”

May 11 · 10:47 AM · User Source JSON

can you mount an s3 bucket / r2 like a filesystem ??

May 11 · 10:47 AM · Assistant Source JSON

Yes. You can mount S3/R2 “like a filesystem,” but it’s a FUSE/object-store filesystem, not a real POSIX filesystem.

Best options:

1. rclone mount — best for Cloudflare R2

Cloudflare officially documents using rclone with R2, and rclone supports S3-compatible providers including R2. R2 exposes an S3-compatible API endpoint like https://worker.example.invalid/example-job-0001. citeturn700635search1turn700635search4turn700635search9

Example:

rclone config
# choose: s3
# provider: Cloudflare
# endpoint: https://worker.example.invalid/example-job-0001
# access_key_id / secret_access_key

Then:

mkdir -p /data/r2

rclone mount r2:my-bucket /data/r2 \
  --read-only \
  --vfs-cache-mode full \
  --dir-cache-time 1h \
  --poll-interval 0

For your agent boxes, this is probably the easiest:

rclone mount r2:alpha-corpora /data \
  --read-only \
  --vfs-cache-mode full \
  --cache-dir /workspace/.rclone-cache

Then Claude/Codex sees:

/data/gutenberg/...
/data/irs-990/...
/data/newspapers/...

2. AWS Mountpoint for S3 — best for actual AWS S3

AWS has Mountpoint for Amazon S3, a high-throughput open-source client that mounts an S3 bucket locally and translates file operations like open and read into S3 API calls. It intentionally does not implement full POSIX filesystem semantics. citeturn700635search0turn700635search3

Example:

mkdir ~/mnt
mount-s3 my-bucket ~/mnt

With cache:

mount-s3 --cache /tmp/s3-cache my-bucket /data

This is probably better than rclone for high-throughput AWS S3 reads, especially ML/dataset-style workloads. AWS notes caching is useful when repeatedly reading unchanged training-style datasets. citeturn700635search0

For R2, I’d expect rclone to be less annoying.

Important limitations

Object storage mounted as a filesystem is good for:

cat file
ls directory
read large files
stream parquet/jsonl/csv
grep selected paths
copy files out

It is bad for:

millions of tiny random reads
sqlite directly over mount
lots of metadata-heavy directory walking
atomic renames
file locking
append-heavy writes
POSIX correctness
agent writing into the mounted corpus

So don’t do:

cd /data
claude

Do:

cd /workspace
claude
# read from /data, write to /workspace

Best pattern for your use case

/data        rclone-mounted R2 bucket, read-only
/workspace   local writable disk
/cache       local cache for repeated reads
/results     uploaded back to R2/S3 when done

And mount with per-job read-only credentials. The bucket policy / API token should literally lack write/delete permissions. The --read-only flag is nice, but credentials are the real boundary.

My recommendation

For Runloop/devboxes:

apt-get install -y fuse3
curl https://rclone.org/install.sh | bash

mkdir -p /data /workspace/.rclone-cache

rclone mount r2:alpha-corpora /data \
  --read-only \
  --vfs-cache-mode full \
  --cache-dir /workspace/.rclone-cache \
  --dir-cache-time 6h \
  --daemon

Then prompt the agent:

Dataset is mounted read-only at /data.
Write all scripts, notes, indexes, and outputs under /workspace.
Never modify /data.

For huge corpora, I’d still add a corpus search CLI on top. Let the agent search first, then open specific files from /data, rather than recursively crawling the whole mounted bucket.

May 11 · 10:48 AM · User Source JSON

but will rclone clone the data ? I need the mount to be instant. and i want to treat it as files on a unix system.

May 11 · 10:48 AM · Assistant Source JSON

No, rclone mount does not clone the whole bucket. The mount can appear basically instantly. It lazily fetches metadata/data as files are listed/read. With VFS caching, it caches chunks or files locally as they are accessed, but it does not pre-download everything unless you explicitly script that. citeturn130405search0turn130405search3

But the catch: it is not a real Unix filesystem. It is “S3/R2 object storage presented through a filesystem interface.”

What works well

ls /data
cat /data/foo.txt
python script.py /data/shard-001.jsonl
duckdb -c "select * from read_parquet('/data/foo.parquet')"
rg "phrase" /data/some-prefix
cp /data/file /workspace/file

What gets weird / bad

sqlite /data/db.sqlite          # bad
git repo living on /data        # bad
mv/rename lots of files         # bad
chmod/chown expectations        # bad
file locking                    # bad
millions of tiny file stats     # slow/expensive
recursive crawl of huge bucket  # slow/expensive

AWS Mountpoint explicitly says it supports basic file operations over S3 but does not provide full POSIX semantics: no symlinks, no file locking, limited modification/delete behavior, etc. citeturn130405search2turn130405search5

For your exact use case

You can absolutely do:

/data      # instant-ish mounted R2/S3 read-only corpus
/workspace # real local Unix disk for Claude/Codex to write into

Then run:

cd /workspace
claude
# or codex

And tell the agent: read from /data, write to /workspace.

R2 mount command

Something like:

mkdir -p /data /workspace/.rclone-cache

rclone mount r2:my-corpus-bucket /data \
  --read-only \
  --vfs-cache-mode full \
  --cache-dir /workspace/.rclone-cache \
  --dir-cache-time 6h \
  --poll-interval 0

Cloudflare documents using rclone with R2 via S3-compatible access keys. citeturn130405search1turn130405search7

But for “treat it as files on Unix”

There are two levels:

Good enough file interface: rclone mount, mount-s3, s3fs, goofys.

Actually Unix/POSIX-like shared filesystem: EFS, NFS, CephFS, JuiceFS, SeaweedFS, Alluxio, Lustre, FSx, etc.

For agent research over mostly immutable datasets, I’d use:

R2/S3 object store
  ↓
rclone/mountpoint read-only at /data
  ↓
local NVMe /workspace
  ↓
results uploaded back to R2

For workloads that need real filesystem semantics, use JuiceFS or EFS/NFS backed by object storage. JuiceFS is probably the most interesting middle layer: object storage as backend, POSIX-ish mount interface, metadata in Redis/Postgres/MySQL, and mountable across many machines.

The real answer

For “instant mount, read-only corpus, arbitrary devboxes,” yes: rclone mount is fine.

For “I want agents/programs to behave as if /data is a normal Unix filesystem in every way,” no: use JuiceFS / EFS / NFS / CephFS.

I’d start with rclone mount until it breaks. It probably only breaks once agents start doing dumb recursive crawls or tools expect locking/mutation.

May 11 · 10:51 AM · User Source JSON

ok if i used juiceFS two questions:

  1. Can i have simultaneous workers on it? Scale it up forever (read only , there would be write locks)
  2. What would you use for the claude code workers ? Modal ? Runloop ? Something different ?
May 11 · 10:51 AM · Assistant Source JSON

I’m going to separate the storage layer from the worker layer; the right answer is mostly “JuiceFS can do this, but metadata and cache topology decide whether it feels infinite.”

May 11 · 10:51 AM · Assistant Source JSON

1. Can you have simultaneous workers on JuiceFS?

Yes. That is basically the point of JuiceFS.

JuiceFS is a distributed POSIX-ish filesystem where file data lives in object storage and metadata lives in Redis / Postgres / MySQL / TiKV / etc. Multiple machines can mount the same filesystem at once. JuiceFS says it can be mounted and accessed across different hosts, and its architecture separates object data from the metadata engine. citeturn394564search9turn394564search12

For your case — read-only corpus, many workers — it should work well.

The scaling bottlenecks are:

1. metadata engine
   ls, stat, open, path traversal, file locks

2. object store throughput
   actual file/chunk reads from S3/R2/GCS

3. per-worker local cache
   whether each worker repeatedly refetches same blocks

4. directory/file layout
   millions of tiny files are worse than larger shards/parquet/jsonl

JuiceFS clients read file blocks concurrently and use object storage as the backing data layer. citeturn394564search8 It also has metadata and data caching; for read-heavy workloads, their docs specifically mention --opencache and client-side file attribute/chunk mapping caches. citeturn394564search4

So the answer is:

Can I have simultaneous workers? Yes.
Can I scale it "forever"? No, but very far if read-only and well laid out.

For “scale it up forever,” the practical constraints are:

  • Redis metadata is fine for moderate scale, but eventually you want TiKV / MySQL / Postgres / JuiceFS Cloud metadata, depending on ops tolerance.
  • R2/S3 request/throughput limits and egress/network matter.
  • If every worker recursively runs find /data or rg over the entire corpus, you will melt metadata/object-store performance.
  • If workers mostly read selected files/shards after search/indexing, you can scale very wide.

For read-only agent workers, mount it read-only:

juicefs mount \
  --read-only \
  --cache-dir /workspace/.juicefs-cache \
  --cache-size 102400 \
  --opencache \
  redis://... \
  /data

I would also design the corpus as:

/data/
  manifests/
  metadata.duckdb
  indexes/
  shards/
    dataset=a/year=2024/part-000001.parquet
    dataset=a/year=2024/part-000002.parquet

Not:

/data/
  900 million tiny text files

Tiny files kill you because every open/stat/listing hits metadata.

Write locks?

JuiceFS metadata includes file locks and common filesystem metadata. citeturn394564search12 But for your workload I would avoid shared writes entirely.

Do:

/data        JuiceFS read-only corpus
/workspace   local writable disk per worker
/results     object-store prefix or database upload

Do not let 1,000 Claude Code instances write into the same JuiceFS tree unless you really need it. Shared mutable filesystems plus agents is asking for stupid race conditions.

If you need writes, use this pattern:

/results/job-123/...
/results/job-124/...
/results/job-125/...

Append-only, per-job output directories. No shared mutable “current result” file.


2. What would I use for Claude Code workers?

My pick depends on whether you want agent devboxes or massively parallel jobs.

Best default: Modal

For your “many arbitrary computers running Claude Code / Codex CLI over a mounted corpus” thing, I’d lean Modal.

Why:

  • Modal Sandboxes are explicitly for running untrusted/user/agent code in isolated containers. citeturn394564search10
  • Modal supports Volumes and CloudBucketMounts with Sandboxes; their docs say Volumes and CloudBucketMounts let you upload data once and access it efficiently from many sandboxes. citeturn394564search17
  • Modal Volumes are distributed and provide a filesystem interface. citeturn394564search14
  • CloudBucketMount supports R2/S3/GCS-style buckets, but inherits Mountpoint/object-store limitations. citeturn394564search2turn394564search6

For pure compute orchestration, Modal is closer to what you want than Runloop:

submit 1,000 jobs
each gets container
mount /data
run claude/codex
stream logs
upload artifacts
scale down to zero

I’d use Modal if the UX is:

“Run this research/code task over this dataset shard and return artifacts.”

Best coding-agent product: Runloop

Runloop is better if the UX is:

“Give Claude Code a real remote devbox and let it hack interactively.”

Runloop Devboxes are secure VM-based execution environments for AI agents. citeturn394564search7 They also support snapshots to save disk state and create new devboxes from that state. citeturn394564search3

So Runloop is good for:

- coding tasks
- repo edits
- browser-ish/devbox workflows
- persistent-ish agent attempts
- fan-out from snapshots
- software engineering evals

But I’m less convinced it is ideal for your “mount the same giant read-only JuiceFS corpus into arbitrary workers” thing unless Runloop gives you clean FUSE privileges / custom mounting / long-running sidecars. You’d have to verify whether their devboxes allow the exact JuiceFS mount flow you need.

If using JuiceFS specifically: Kubernetes may be cleanest

If JuiceFS is central, the “boring correct” answer is:

Kubernetes
  + JuiceFS CSI driver
  + worker pods
  + per-job PVC/mount
  + local ephemeral disk
  + queue

JuiceFS has a CSI driver for Kubernetes, exposing JuiceFS as PersistentVolumes to pods. citeturn394564search20

Architecture:

R2/S3/GCS object storage
        ↑
     JuiceFS
        ↑
K8s PersistentVolume mounted read-only at /data
        ↑
worker pods running claude/codex

This is the most “real infra” version. More annoying than Modal, but you get:

  • exact mount control
  • read-only mounts
  • node-local caches
  • sidecars
  • network policies
  • cheaper sustained scale
  • no dependency on a sandbox vendor supporting FUSE weirdness

For a serious Alpha Research infra layer, I’d eventually end up here or on self-managed Firecracker.


My recommendation

Start with Modal + object-store mount or Modal Volume unless you’re absolutely set on JuiceFS.

If you need true POSIX-ish shared FS, use:

Modal worker containers
  + install juicefs in image
  + mount JuiceFS at /data read-only
  + run Claude/Codex in /workspace

But confirm Modal permits the needed FUSE mount behavior. If FUSE is a pain, use Modal’s own Volume/CloudBucketMount first.

If you want the cleanest long-term architecture:

Storage:
  R2/S3 for raw objects
  JuiceFS for POSIX-ish read-only corpus mount
  Postgres/TiKV/managed JuiceFS metadata, not random tiny Redis forever

Workers:
  V0: Modal
  V1: Kubernetes + JuiceFS CSI
  Special interactive coding boxes: Runloop

Decision table

Need Use
Fastest prototype Modal Sandboxes
Claude Code interactive devbox Runloop
Massive batch agent workers Modal
Full control + JuiceFS done right Kubernetes + JuiceFS CSI
Cheapest sustained high volume Hetzner/DO/K8s/Fly Machines + JuiceFS
Huge read-only files, not POSIX R2/S3 mount directly
True shared POSIX-ish corpus JuiceFS

For you, I’d do:

V0:
  Modal workers
  R2 bucket or Modal Volume
  local /workspace
  Claude/Codex CLI
  results to R2

V1:
  Add JuiceFS only once direct object-store mounts become too limiting

V2:
  K8s + JuiceFS CSI if this becomes core infra

Runloop is attractive, but I’d reserve it for software engineering devboxes, not corpus-scale worker fleets.

May 11 · 10:53 AM · User Source JSON

modal with modal volumes or juiceFS ? can modal mount juiceFS ?

May 11 · 10:53 AM · Assistant Source JSON

I’ll check the current Modal storage/mount constraints before giving a recommendation, because the answer depends on whether Modal’s sandbox/container runtime allows FUSE-style mounts cleanly.

May 11 · 10:53 AM · Assistant Source JSON

Modal Volumes vs JuiceFS

Use Modal Volumes first.

Modal Volumes give you persistent shared filesystem storage that can be mounted into Modal Functions and Sandboxes. Modal says Volumes are distributed, cached/chunked for throughput, and expose a full filesystem interface. They also work directly with Sandboxes. citeturn382854search14turn382854search4

For your agent-worker use case:

/data       Modal Volume, read-only corpus or prepared dataset
/workspace  local sandbox disk, writable
/results    Modal Volume or R2 output prefix

This is much simpler than bringing JuiceFS into Modal.

Can Modal mount JuiceFS?

Maybe, but I would assume not cleanly until proven otherwise.

JuiceFS’s normal mount path is FUSE. JuiceFS’s Docker docs say mounting directly inside a container requires --privileged because FUSE is used. citeturn382854search2 JuiceFS’s Kubernetes CSI setup also uses a privileged mount pod for FUSE. citeturn382854search8

Modal Sandboxes are secure containers for agent/user code, not arbitrary privileged Docker hosts. citeturn382854search9 So unless Modal explicitly supports FUSE/privileged mounts for your account/runtime, I would not design around juicefs mount inside Modal.

You might be able to use JuiceFS in Modal if:

1. Modal allows FUSE/device access in your container, or
2. JuiceFS is mounted outside the container and passed in, or
3. you use JuiceFS through an NFS/Samba gateway instead of FUSE

But those are more annoying than just using Modal Volumes.

What about Modal CloudBucketMount?

Also viable, especially for R2/S3.

Modal has CloudBucketMount, which supports AWS S3, Cloudflare R2, and Google Cloud Storage buckets. It is built on AWS Mountpoint tech and inherits object-store mount limitations. citeturn382854search1turn382854search6

So the hierarchy is:

Modal Volume
  Best for: persistent shared filesystem inside Modal

Modal CloudBucketMount
  Best for: cheap huge R2/S3 corpus, mostly sequential reads

JuiceFS
  Best for: you need POSIX-ish shared filesystem across many non-Modal machines too

My recommendation

For V0:

Modal Sandboxes
Modal Volume for prepared corpus/indexes
CloudBucketMount or R2 for raw giant objects
local /workspace for Claude/Codex writes

Do not start with JuiceFS inside Modal.

A good layout:

/data/indexes       Modal Volume
/data/manifests     Modal Volume
/data/raw           CloudBucketMount to R2, read-mostly
/workspace          local writable disk
/results            Modal Volume or R2 upload

Then run agents from /workspace, not /data.

When I’d choose JuiceFS instead

Use JuiceFS if you want the same filesystem mounted across:

Modal workers
Runloop devboxes
your own Hetzner/DO/Fly machines
Kubernetes pods
local dev machines

But in that world I’d probably move the workers to Kubernetes + JuiceFS CSI, not Modal. JuiceFS has a Kubernetes CSI driver specifically for mounting JuiceFS into pods. citeturn382854search5

Bottom line

For Modal: Modal Volumes + CloudBucketMounts.

For JuiceFS: Kubernetes/self-hosted workers.

Trying to force JuiceFS FUSE mounts inside Modal is likely the wrong first move.

May 11 · 10:54 AM · User Source JSON

can modal volumes work across multiple workers simultaneously ? Does this provide any advantage over my current digital ocean setup ? Modal

May 11 · 10:55 AM · Assistant Source JSON

Yes, Modal Volumes can be mounted across multiple workers simultaneously.

Modal describes Volumes as a writable filesystem that can be shared between one or more Modal functions, and used to persist durable state across several instances of the same function. They also work with Sandboxes, so many Claude/Codex sandboxes can access the same mounted Volume. citeturn953528search1turn953528search7

But there’s a big semantic caveat:

Modal Volumes are not quite “live NFS”

For regular Modal Volumes, changes from one container may not instantly appear in another container. In Sandbox docs, Modal says Volume files are synced back when the Sandbox terminates; CloudBucketMounts sync automatically. citeturn953528search7

So for your use case, Modal Volumes are great for:

/data       read-mostly corpus/index/model files
/workspace  local writable scratch
/results    per-job output, synced/uploaded at end

They are not ideal for:

100 agents collaboratively editing the same shared tree live
agents depending on immediate cross-worker file visibility
shared SQLite DB
append-heavy logs from many workers into same files

For read-only datasets, this is fine. The workers all see the same mounted corpus. Just do not use the Volume as a coordination database.

Advantage over your DigitalOcean setup

Probably yes, if your bottleneck is bursting arbitrary agent workers.

Modal advantages:

- You don’t manage machines
- Scales to many containers quickly
- Built-in sandbox/function abstraction
- Persistent Volumes
- Easy Python orchestration
- Good for bursty batch jobs
- Per-job CPU/GPU/memory selection
- No idle server cost

Modal is explicitly optimized for large-scale batch processing and can scale jobs across many containers without you hand-managing workers. citeturn953528search3turn953528search10

DigitalOcean advantages:

- Cheaper if machines are busy 24/7
- Simpler mental model: real VM, real disk
- More control over Docker/FUSE/systemd/networking
- Easier to mount JuiceFS/NFS however you want
- Better for long-lived services
- Less vendor-specific

The key distinction

If you currently have a DO box with:

1 big disk
1 queue
N worker processes
datasets already local

Modal is not magically better. It is mostly better when you want:

10 workers now
0 workers in 5 minutes
500 workers for one big corpus job
different job sizes
ephemeral isolated agent sandboxes
easy fanout

What I’d use

For your exact thing:

Modal Volume:
  prepared indexes, manifests, smaller hot datasets

Cloudflare R2 / S3:
  raw giant corpus

Modal CloudBucketMount:
  mount raw R2/S3 data when needed

Local sandbox disk:
  /workspace for Claude/Codex writes

DB/queue:
  job status, claims, locks, outputs metadata

I would not use Modal Volume as the main source of truth for huge raw corpora unless the data is already in Modal and mostly static. For giant datasets, keep R2/S3 as source of truth and use Modal Volume for hot/cache/index layers.

My call

Use Modal if your goal is: “spin up arbitrary Claude/Codex workers on demand over a mostly read-only dataset.”

Stay on DigitalOcean if your workload is: “a few always-on workers, one local corpus, predictable load.”

Best hybrid:

DigitalOcean:
  orchestrator, DB, dashboards, maybe corpus indexing

R2/S3:
  canonical dataset

Modal:
  bursty Claude/Codex workers

Modal Volume:
  hot indexes / manifests / reusable environment data

That gives you the benefit of Modal without moving your entire infra religion into it.

May 11 · 10:56 AM · User Source JSON

Right now my data lives in a digital ocean volume. What's wrong with a modal volume ? it's not that big (1 TB)

May 11 · 10:56 AM · Assistant Source JSON

Nothing is inherently wrong with a Modal Volume for 1 TB. For your use case it may be the cleanest move.

The main caveat is semantic, not size.

Modal Volume is good if your data is mostly read-only

For:

1 TB corpus
many Claude/Codex workers
read shared files
write per-job outputs elsewhere

Modal Volume is probably fine.

Modal describes Volumes as writable persistent filesystems shareable between functions/instances, exposed as a filesystem. citeturn354233search4 They are also designed for distributed, on-demand compute, where Modal can scale jobs across many containers. citeturn354233search6

So your architecture could simply be:

Modal Volume: /data       # your 1 TB dataset
Local disk:    /workspace  # Claude/Codex scratch
R2/S3/Volume:  /results    # job outputs

Run agents from /workspace, read from /data.

What’s worse than your DigitalOcean Volume?

A DO volume is basically a normal block volume attached to a VM. It has simpler, more predictable filesystem semantics.

Modal Volume is more “distributed persistent filesystem for Modal containers.” The tradeoffs:

Thing DigitalOcean Volume Modal Volume
Normal Linux filesystem feel Better Good, but platform-managed
Attach to many workers Not really Yes
Burst to hundreds of workers You manage it Modal handles it
Long-running always-on cheapness Usually better Maybe worse
Zero-idle burst jobs Worse Better
Works outside Modal Yes No
Weird sync/concurrency edge cases Fewer More likely
Infra control Higher Lower

The biggest issue: don’t treat Modal Volume like a live multi-writer NFS coordination layer. Modal’s docs say Volumes can share/persist state, but for Sandboxes, Volume files are synced back when the Sandbox terminates; CloudBucketMounts sync automatically. citeturn354233search0 So if many Claude workers write to the same shared paths and expect immediate visibility, that is risky.

But for read-only data, this mostly does not matter.

What’s better than your DO setup?

Modal gives you:

- Many isolated workers without managing VMs
- Scale-to-zero
- Easy fan-out
- Per-job CPU/RAM/GPU choices
- Cleaner sandbox model for arbitrary Claude/Codex tasks
- Persistent shared Volume mounted into every worker

If your current DO setup is one machine with a mounted 1 TB volume, then the annoying part is: scaling means provisioning more droplets, copying/mounting data, managing queues, cleaning workers, etc.

Modal is better when you want:

run 200 Claude Code jobs against the same corpus
then scale back to 0

What I’d do

For 1 TB, I’d try Modal Volume directly before JuiceFS.

V0:
  upload/sync DO volume -> Modal Volume
  run Modal Sandbox workers
  mount volume at /data
  /workspace is local writable scratch
  output each job to /results/job-id

Use conventions:

/data        read-only by policy
/workspace   writable local scratch
/results     per-job output only

Do not let agents write into /data.

When I would not use Modal Volume

I’d stay with DO / use JuiceFS / use R2 if:

- the 1 TB changes constantly
- you need access from non-Modal machines
- you need true immediate multi-writer visibility
- you want cheapest 24/7 always-on workers
- you need custom FUSE/kernel/network behavior
- your workload is just a few steady workers

My call

For 1 TB, read-heavy, agent workers: use Modal Volume.

It is probably simpler than JuiceFS and much more scalable than a single DigitalOcean volume. The only thing I’d be careful about is not using it as a shared mutable live filesystem. Keep shared data read-only, write outputs per job, and use a DB/queue for coordination.

May 11 · 10:58 AM · User Source JSON

How powerful are modal workers ? can they run codex cli / claude code ?

May 11 · 10:58 AM · Assistant Source JSON

Yes. Modal workers are more than powerful enough to run Codex CLI / Claude Code. Those tools are mostly network-bound agent CLIs, not heavy local inference workloads.

How powerful are Modal workers?

By default, a Modal Function/Sandbox starts tiny: 0.125 CPU cores and 128 MiB RAM, but you can request more CPU, memory, disk, and GPUs. Modal’s resource docs say Functions/Sandboxes can request larger CPU/memory similarly to Kubernetes. citeturn913557search0

For Claude/Codex workers, I’d use something like:

cpu=2-8
memory=4096-32768
timeout=3600+

For large repo/corpus work:

cpu=8
memory=32768
ephemeral_disk=50_000  # MB-ish depending Modal API version

No GPU needed unless you are running local models.

Modal also explicitly markets Sandboxes for executing untrusted user/agent code and says you can spin up thousands of isolated secure Sandboxes. citeturn913557search4turn913557search7

Can they run Claude Code?

Yes. Claude Code supports Linux, including Ubuntu/Debian/Alpine, with 4 GB+ RAM and an internet connection. citeturn913557search2

Modal even has a JS example that builds a Sandbox image with bash, curl, git, ripgrep, installs Claude Code via:

curl -fsSL https://claude.ai/install.sh | bash

and then creates a Modal sandbox from that image. citeturn913557search19

So yes, this is a first-class-ish use case.

Can they run Codex CLI?

Almost certainly yes. Treat it like any Node/Rust/native CLI: bake it into the Modal image, inject OPENAI_API_KEY, mount /data, run in /workspace.

Modal Sandboxes can run arbitrary commands via Sandbox.exec, with stdout/stderr/stdin available through the returned process object. citeturn913557search1

Practical architecture

Modal Sandbox
  /data        Modal Volume, read-only corpus
  /workspace   local writable disk
  /results     output path

Env:
  ANTHROPIC_API_KEY
  OPENAI_API_KEY

The worker command is basically:

cd /workspace
claude -p "$(cat task.md)"

or:

cd /workspace
codex exec "$(cat task.md)"

Important gotchas

Do not run agents from /data.

Use:

/data       shared read-only corpus
/workspace  writable scratch

Also avoid having many workers write to the same Modal Volume path. Use per-job directories:

/results/job_abc123/
/results/job_def456/

My call

For 1 TB read-heavy data and many agent workers:

Modal Volume for /data
Modal Sandboxes for Claude/Codex
local /workspace for writes
DB/queue for coordination

This is strong enough. Start there before inventing JuiceFS/K8s.

May 11 · 11:15 AM · User Source JSON

how do secrets work for claude code / codex cli ?

May 11 · 11:15 AM · Assistant Source JSON

Use Modal Secrets to inject API keys as environment variables into each worker/sandbox. Then Claude Code/Codex pick them up from env.

Claude Code

Claude Code can authenticate from:

ANTHROPIC_API_KEY=...

Anthropic’s docs say ANTHROPIC_API_KEY is sent as the X-Api-Key header, and in non-interactive mode claude -p, the key is used automatically when present. citeturn225146search12

So in a worker:

export ANTHROPIC_API_KEY=sk-ant-...
claude -p "do the task"

Codex CLI

Codex can use env-var auth too. OpenAI’s Codex auth docs say you can set an env_key for provider-specific API keys, and for normal OpenAI usage the key is typically:

OPENAI_API_KEY=...

citeturn225146search10

So:

export OPENAI_API_KEY=sk-proj-...
codex exec "do the task"

In Modal

Create a secret:

modal secret create agent-keys \
  ANTHROPIC_API_KEY=sk-ant-... \
  OPENAI_API_KEY=sk-proj-...

Modal Secrets are dictionaries of environment variables injected into containers, and Modal says they can be created in the dashboard, from code, or via CLI. citeturn868683search1turn868683search3

Then attach it to the function/sandbox.

For a Modal Function:

import modal

app = modal.App("agent-workers")

image = (
    modal.Image.debian_slim()
    .apt_install("curl", "git", "ripgrep", "nodejs", "npm")
    .run_commands(
        "npm install -g @anthropic-ai/claude-code",
        "npm install -g @openai/codex"
    )
)

@app.function(
    image=image,
    secrets=[modal.Secret.from_name("agent-keys")],
    cpu=4,
    memory=8192,
    timeout=3600,
)
def run_agent(prompt: str):
    import os, subprocess, pathlib

    pathlib.Path("/workspace").mkdir(exist_ok=True)

    result = subprocess.run(
        ["claude", "-p", prompt],
        cwd="/workspace",
        text=True,
        capture_output=True,
        env=os.environ.copy(),
    )

    return {
        "stdout": result.stdout,
        "stderr": result.stderr,
        "code": result.returncode,
    }

For Codex:

result = subprocess.run(
    ["codex", "exec", prompt],
    cwd="/workspace",
    text=True,
    capture_output=True,
    env=os.environ.copy(),
)

Important security pattern

Do not put API keys in:

/data
/workspace
.env files inside agent-visible repos
task prompts
logs

Use env injection only.

For your setup:

Modal Secret:
  ANTHROPIC_API_KEY
  OPENAI_API_KEY
  R2_READ_ONLY_ACCESS_KEY_ID
  R2_READ_ONLY_SECRET_ACCESS_KEY

/data:
  mounted read-only corpus

/workspace:
  writable scratch, no secrets written

/results:
  per-job output

One subtle issue: agents can run shell commands, so they can technically do env and see keys. That is unavoidable if the CLI needs the key directly. If you don’t want the agent process to be able to inspect provider keys, put a tiny LLM proxy in front of Claude/OpenAI and give the worker only a short-lived per-job token with budget/rate limits.

May 11 · 11:16 AM · User Source JSON

Great can you write up a prompt for my coding agent to make the change from digital ocean to modal ? Codex CLI not claude code ,but otherwise mimick laude that coding agent example you said.

May 11 · 11:16 AM · Assistant Source JSON

Use this as the Codex prompt:

We need to migrate our current “agent worker over a 1TB dataset” setup from a DigitalOcean volume to Modal.

Current setup:
- Data currently lives on a DigitalOcean volume.
- We want to run many ephemeral Codex CLI workers against that shared dataset.
- The dataset should be treated as read-only by agent workers.
- Each worker should have its own writable scratch directory.
- Outputs should be written to per-job result directories, not back into the shared dataset.
- We are using Codex CLI, not Claude Code.

Goal:
Build a Modal-based worker system that can:
1. Store/mount the dataset using a Modal Volume.
2. Spin up Modal workers/sandboxes/functions that run Codex CLI.
3. Mount the shared dataset at `/data`.
4. Use `/workspace` as local writable scratch.
5. Write final outputs to `/results/<job_id>/`.
6. Inject secrets via Modal Secrets, especially `OPENAI_API_KEY`.
7. Avoid putting secrets in files, prompts, logs, or mounted volumes.
8. Make it easy to submit jobs programmatically.

Please inspect the current repo first and adapt to the existing structure. Do not rewrite everything unnecessarily. Prefer a small, clean integration.

Implementation requirements:

- Add Modal as a dependency if not already present.
- Create a Modal app, probably in something like `modal_workers/agent_worker.py` or an equivalent location that fits this repo.
- Define a Modal image that installs:
  - Node.js / npm if needed
  - Codex CLI
  - git
  - ripgrep
  - fd/findutils if useful
  - jq
  - Python utilities already needed by the repo
- Define or reference a Modal Volume for the dataset, e.g. `agent-dataset`.
- Define or reference a Modal Volume for results if useful, e.g. `agent-results`.
- Mount dataset Volume at `/data`.
- Mount results Volume at `/results`.
- Ensure workers run from `/workspace`, not `/data`.
- Ensure the prompt tells Codex that `/data` is read-only and `/workspace` is writable.
- Use `OPENAI_API_KEY` from a Modal Secret, e.g. `agent-keys`.

The core worker should look conceptually like:

```python
@app.function(
    image=image,
    volumes={
        "/data": dataset_volume,
        "/results": results_volume,
    },
    secrets=[modal.Secret.from_name("agent-keys")],
    cpu=4,
    memory=8192,
    timeout=3600,
)
def run_codex_job(job_id: str, prompt: str, extra_context: dict | None = None):
    ...

Inside the worker:

  • Create /workspace/<job_id> or similar.
  • Write the task prompt to /workspace/<job_id>/task.md.
  • Run Codex CLI from the workspace directory.
  • Use non-interactive Codex mode, likely codex exec.
  • Include clear instructions in the Codex prompt:
The dataset is mounted at /data and must be treated as read-only.
Do not modify files in /data.
Use /workspace for scratch files, scripts, intermediate notes, and temporary indexes.
Write final outputs to /results/<job_id>/.
Do not print environment variables or secrets.
  • Capture stdout, stderr, return code, and runtime.
  • Save stdout/stderr to /results/<job_id>/logs/.
  • Save a final response or summary to /results/<job_id>/answer.md if Codex produces one.
  • Return a structured Python dict with:
    • job_id
    • status
    • return_code
    • stdout_tail
    • stderr_tail
    • result_path

Also add a local submit/test script, for example:

python -m modal_workers.submit_job --job-id test-001 --prompt "Inspect /data and summarize what datasets are available."

or a simple Python function that calls the Modal function remotely.

Add a data migration script or documented command path for copying data from the current DigitalOcean volume into the Modal Volume. Use Modal’s documented volume upload approach if available. If direct upload of 1TB is awkward, write a clear staged migration plan:

  1. Package/sync dataset from DO.
  2. Upload into Modal Volume.
  3. Verify file count / byte size / sample reads.
  4. Run a smoke-test Codex job.
  5. Freeze DO volume as backup until Modal results are verified.

Important constraints:

  • Do not use the Modal Volume as a live multi-writer coordination layer.
  • Do not let workers write into /data.
  • Per-job results must go under unique paths like /results/<job_id>/.
  • Use an external DB/queue if the repo already has one; otherwise keep this PR focused on Modal worker execution, not a full queue system.
  • Keep current DigitalOcean path working until the Modal path is proven. Ideally add a config flag like WORKER_BACKEND=modal|digitalocean or similar.
  • Make the migration incremental.

Please produce:

  1. The Modal worker implementation.
  2. The submit/test script.
  3. Any required dependency/config changes.
  4. A README section explaining:
    • how to create the Modal secrets
    • how to create/upload the Modal Volume
    • how to run a smoke test
    • how to submit a real job
    • known limitations around Volume concurrency and write visibility
  5. A minimal smoke test that verifies:
    • /data is mounted
    • /workspace is writable
    • OPENAI_API_KEY is present but never printed
    • Codex CLI can run
    • results are written to /results/<job_id>/

Before coding, inspect the repo and tell me the smallest clean set of files you plan to touch. Then implement it.


Optional stricter add-on:

```text
Style preferences:
- Keep code minimal and boring.
- Prefer typed Python.
- Avoid clever abstractions.
- Do not introduce a huge framework.
- Do not log secrets.
- Do not let Codex run from the mounted dataset directory.
- Use per-job directories everywhere.
May 11 · 11:30 AM · User Source JSON

could i use modal to create a worker that creates a jupyter notebook and publishes it ? although i guess you wouldnt see the research as it happens.

May 11 · 11:30 AM · Assistant Source JSON

I’ll separate “generate a notebook artifact” from “watch the research live,” because Modal can do the first cleanly and the second needs streaming/log/UI plumbing.

May 11 · 11:30 AM · Assistant Source JSON

Yes. Modal is a good fit for this.

You have two different products/patterns:

1. Batch-generated notebook report

This is what I’d do first.

Modal worker
  reads /data
  runs Codex / scripts
  creates report.ipynb
  executes notebook
  exports report.html
  uploads/publishes HTML

Modal supports Sandboxes for executing agent/user code, and Volumes/CloudBucketMounts can be attached so data is available inside the sandbox. citeturn501121search7turn501121search4

The worker can use:

jupyter nbconvert --execute --to notebook report.ipynb
jupyter nbconvert --to html report.ipynb

nbconvert supports executing notebooks and saving outputs back into .ipynb, and also converting notebooks to formats like HTML from the command line. citeturn501121search6turn501121search1

Then publish to one of:

R2 public bucket
Cloudflare Pages
S3 static hosting
your own app route
Modal web endpoint

This gives you a polished artifact:

/reports/job-123/report.ipynb
/reports/job-123/report.html
/reports/job-123/artifacts/*.png
/reports/job-123/logs.txt

2. Live Jupyter notebook while research happens

Also possible, but different.

Modal has hosted notebooks and also an example of running a Jupyter notebook server inside a Modal Sandbox, then exposing it via a URL. citeturn501121search0turn501121search2 Modal also supports tunnels to expose live TCP ports from a container/Sandbox. citeturn501121search12

That would let you open a running Jupyter server and watch/edit interactively.

But for agent research, live Jupyter is often the wrong primitive. Better:

Live:
  stream logs/events/tool calls to your UI

Final:
  publish notebook + HTML report

The agent can still write the notebook incrementally, but you probably don’t want to watch cells appear live unless you’re debugging.

The architecture I’d use

submit_research_job(prompt, dataset_refs)
  ↓
Modal Sandbox / Function
  ↓
/data      mounted read-only corpus
/workspace writable scratch
/results   report output
  ↓
Codex CLI does research
  ↓
Python script builds report.ipynb
  ↓
nbconvert executes + exports HTML
  ↓
Upload report.html to R2/Pages
  ↓
Return public URL

The notebook is the audit artifact, not necessarily the live UI.

Codex flow

Prompt Codex like:

You are running inside a Modal worker.

Dataset:
- /data is read-only.
- Use it only for reading corpus files.

Workspace:
- /workspace is writable.
- Write scripts, notes, charts, and intermediate files here.

Final report:
- Create /results/{job_id}/report.ipynb.
- The notebook should include:
  1. Research question
  2. Dataset/files inspected
  3. Methods
  4. Code cells for reproducibility
  5. Findings
  6. Charts/tables if useful
  7. Limitations
- Also create /results/{job_id}/summary.md.
- Do not print secrets or environment variables.

Then after Codex exits, your wrapper runs:

jupyter nbconvert \
  --execute \
  --to notebook \
  --inplace /results/$JOB_ID/report.ipynb

jupyter nbconvert \
  --to html \
  --output /results/$JOB_ID/report.html \
  /results/$JOB_ID/report.ipynb

The live-research problem

You’re right: you would not automatically “see the research as it happens” if it’s just a background Modal job.

Fix that with one of these:

Simple V0

stream stdout/stderr from Codex
save logs to /results/job-id/logs.txt
poll job status from your app

Better

Codex wrapper emits events:
- started
- command_run
- file_read
- note_written
- chart_created
- notebook_cell_added
- final_report_ready

Best

Use your own web UI:
left: live event stream
right: generated notebook/report preview
bottom: artifacts/files

I would not start by trying to make Jupyter itself the live UI. Use logs/events for live visibility, notebook for final proof.

Bottom line

Yes:

Modal worker creates notebook: good
Modal worker publishes HTML: good
Modal hosted/running Jupyter: possible
Live research visibility: build log/event streaming, not just notebook publishing

For your product, the killer output is probably “research report with reproducible notebook attached”: HTML for normal users, .ipynb for technical users, logs/artifacts for auditability.