Agents · Qwen3.8-Flash-Next IQ3_S · Basalt · GeForce RTX 5090 · 30 GB VRAM
published by @dh7 on
m64 m64_fd6b2982ea67opencode agent on Qwen3.8-Flash-Next (Basalt, RTX 5090). opencode 1.18.23 coding agent driving Qwen3.8-Flash-Next (IQ3_S pack, 384k context) on the Basalt engine, both in one container on one RTX 5090 with 64 GB of RAM. Each session gets an empty directory; the agent can write and run code there. opencode itself (its terminal interface, in the browser, opened on the folder home) is on a second port. Needs no network (the model runs in the same container); serve it with --allow-external-network to let the agent install packages.
Lineage
- parent
- none declared
- derived from
- no published capsule shares half its files
- children
- none
Files
8 files- human/
- index.html11.4 KB
- agent_gateway.py15.0 KB
- AGENT.md6.9 KB
- capsule.toml1.7 KB
- Dockerfile3.0 KB
- opencode.json899 B
- start.sh4.0 KB
- warm_opencode.sh1.8 KB
AGENT.md
opencode coding agent (Qwen3.8-Flash-Next on Basalt, RTX 5090)
This service is a coding agent you drive over HTTP. You open a session, send it a task in plain English, and it does the work: it writes files, runs shell commands and reads the results until it is done. Then it replies. The harness is opencode 1.18.23. The model is Qwen3.8-Flash-Next (IQ3_S pack, 384k context) on the Basalt engine, running on the RTX 5090 in this same container.
Each session works in its own empty directory, and you can list, download and upload its
files. The agent runs in a sandbox. It is not root, and it has no network access unless
the user served it with --allow-external-network (the public internet) or
--allow-internal-network (the LAN and tailnet); network_access in GET /status and
sandbox.network in GET /agent?format=json say what this run has. Without a grant,
pip install, curl https://example.com or git clone are blocked and logged, so give it
work that needs no downloads: Python 3.12's standard library, node 22, git, ripgrep, jq and
a C toolchain are installed.
You cannot choose the model, tools or system prompt. You send only message text and files.
Security note
- Anyone who can reach this service can run code in it (inside the sandbox, on the GPU machine). Keep its token private.
- Sessions are not isolated from each other. Every session runs as the same user, in the same container, so an agent in one session can read and change other sessions' files. Use one running service per person or project, not per session.
Base URL: {{BASE_URL}}
opencode in the browser
This service also exposes opencode itself on a second port, the app port: its terminal
interface in a browser tab (ttyd), opened on the folder home, one TUI per tab. Its URL is
app_url in GET /status and GET /agent?format=json; it is usually this service's port
-
/human/links to it.
Files, shell commands and model calls all run in this service's container: nothing leaves the GPU machine.
- The app port has no per-session isolation: it sees every session and file, and its
project is the folder
home(/scratch/home). - It has no params filtering: the TUI runs the agent's tools, shell included, which is what the agent can already do inside its container.
Quick start
SID=$(curl -s -X POST {{BASE_URL}}/v1/sessions {{AUTH}} \
-H "Content-Type: application/json" -d '{"title": "demo"}' | jq -r .id)
curl -s -X POST {{BASE_URL}}/v1/sessions/$SID/messages {{AUTH}} \
-H "Content-Type: application/json" \
-d '{"text": "write hello.py that prints hi and run it"}' | jq '{text, steps, elapsed_s, tools: [.tools[] | {tool, status}]}'
curl -s {{BASE_URL}}/v1/sessions/$SID/files {{AUTH}}
curl -s "{{BASE_URL}}/v1/sessions/$SID/files?path=hello.py" {{AUTH}}
Routes
POST /v1/sessions
Creates a session with a fresh empty directory. The body is optional: {"title": "..."}.
Returns 201 with {"id": "ses_...", "title": ..., "created": <unix time>}.
POST /v1/sessions/{id}/messages
Sends one task or follow-up to the session. The agent remembers earlier messages in the same session.
text(string, required): what to do.wait(bool, defaulttrue): withtrue, the request blocks until the agent's turn is over. That can take a few seconds or several minutes, so set your client timeout to at least 10 minutes. Withfalse, it returns202at once; follow the turn on the events stream.
Reply (with wait: true):
{"id": "msg_...", "role": "assistant", "text": "Created hello.py ... prints: hi",
"tools": [{"tool": "write", "status": "completed", "input": {...}, "output": "..."},
{"tool": "bash", "status": "completed", "input": {"command": "python3 hello.py"}, "output": "hi\n"}],
"steps": 3, "tokens": {...}, "finish": "stop", "elapsed_s": 6.2}
tools lists every tool call in the turn, and each output is cut to 4000 characters.
When the model call itself fails (for example, the prompt is longer than the context), the reply
carries an error object.
GET /v1/sessions/{id}/events
Server-sent events for this session: opencode's own event objects (message parts as they
stream, tool calls, session.idle when a turn ends), one data: line each. The stream
stays open; close it when you are done.
curl -sN {{BASE_URL}}/v1/sessions/$SID/events {{AUTH}}
GET /v1/sessions/{id}/files[?path=...]
With no path, or a directory path, it lists entries:
{"path": ".", "entries": [{"name": "hello.py", "type": "file", "size": 12}]}.
type is file, dir or other (a FIFO, socket or device).
A file path returns the file's bytes as a download (Content-Disposition: attachment).
The agent wrote these bytes, so they are never served as a web page: the type is
text/plain, image/png, image/jpeg, image/gif or image/webp when the extension
says so, and application/octet-stream for everything else (.py, .html, .svg...).
A path that leaves the session directory, contains a NUL byte, or names something other
than a regular file or directory returns 400. A missing path returns 404.
PUT /v1/sessions/{id}/files?path=...
Uploads the raw request body to path in the session directory, creating parent
directories as needed. 400 if path exists and is not a regular file. Returns 201 with {"written": "data/input.csv", "bytes": 1234}.
curl -s -X PUT "{{BASE_URL}}/v1/sessions/$SID/files?path=data/input.csv" {{AUTH}} \
--data-binary @input.csv
POST /v1/sessions/{id}/abort
Stops the session's running turn. A messages request waiting on that turn then
returns early.
GET /v1/agent
What this agent is: harness, model and sandbox policy.
GET /status
Start-up progress: phase is building, starting, loading, ready or error.
network summarises the sandbox's outbound connections, and lists blocked attempts.
Timings and concurrency
- First start builds the container image, which takes a few minutes. After that, the service is ready once Basalt has loaded the 87 GB pack: about a minute when it is in the page cache, a few minutes cold (budgeted at 300 s).
- A small task such as the quick start takes about 3–10 s, and bigger tasks take minutes. Generation runs at about 300 tok/s; time depends on how many steps the agent takes.
- Sessions are independent, but the model answers one request at a time: parallel sessions take turns. Send one message at a time per session, and wait for the turn to end before you send the next.
- Sessions and their files live only as long as this run of the service. Download what you want to keep.
Errors
401: missing or wrong token. SendAuthorization: Bearer <token>.503withRetry-Afterwhile the service is still starting: wait and retry.404: unknown session id, file not found, or a route this service does not offer.400: the body is not a JSON object,textis missing, or a path escapes the session.5xxotherwise: retry once, then checkGET /status.
capsule.toml
schema = "m64/capsule@1"description = "opencode agent on Qwen3.8-Flash-Next (Basalt, RTX 5090). opencode 1.18.23 coding agent driving Qwen3.8-Flash-Next (IQ3_S pack, 384k context) on the Basalt engine, both in one container on one RTX 5090 with 64 GB of RAM. Each session gets an empty directory; the agent can write and run code there. opencode itself (its terminal interface, in the browser, opened on the folder home) is on a second port. Needs no network (the model runs in the same container); serve it with --allow-external-network to let the agent install packages."kinds = ["agent.session"]authors = ["dh7"]license = "mit"[hardware]platform = "linux-x86_64"gpu = "NVIDIA GeForce RTX 5090"gpu_count = 1vram_gb = 30# The pack's experts (46.8 GiB) sit in system RAM next to what VRAM caches: a 64 GB# machine (which reports ~60.5 GB).ram_gb = 60driver = ">=580"disk_gb = 90[runtime]sandbox = "container"# No image: the Dockerfile builds Basalt at a pinned commit on nvidia/cuda 13.0.0 (pinned by# digest) and adds node 22 + opencode.gpu = trueshm_gb = 8internal_port = 9500health = "/health"# Basalt loads first (~30 s with the pack in the page cache, minutes cold), then opencode.expected_load_seconds = 300user = "1000:1000"# opencode's terminal interface in the browser (ttyd), opened on the folder home, on a second# front port. Raw: no per-session isolation and no params filtering there.[runtime.app]internal_port = 7681# The Basalt pack is the whole model (weights, tokenizer, chat template, vision, drafter).[[artifacts]]type = "hf"repo = "jesdga/Qwen3.8-Flash-Next-Basalt"revision = "2a6a758dc12f1d027d2d35b8964110df7e80f70b"include = ["LICENSE", "Qwen3.8-FN-GSQ-RCO-IQ3_S.basalt"]mount = "/weights/basalt"