Agents · DeepSeek V4.1 Flash · OpenRouter · CPU/API
published by @dh7 on
m64 m64_7796c3cb05e5opencode terminal on DeepSeek V4.1 Flash (OpenRouter). opencode 1.18.23's own TUI, running in tmux inside the sandbox and served to the browser by ttyd: open the app port and use opencode as in a terminal. The session survives closing the tab. Drives DeepSeek V4.1 Flash through OpenRouter. No GPU; the only egress is openrouter.ai:443, and the m64 gate adds the OpenRouter key, so the agent never holds it.
Lineage
- parent
- none declared
- derived from
- no published capsule shares half its files
- children
- none
Files
9 files- human/
- index.html3.7 KB
- AGENT.md2.8 KB
- capsule.toml1.7 KB
- Dockerfile2.0 KB
- health.js1.0 KB
- opencode.json994 B
- start.sh2.4 KB
- tmux.conf955 B
- warm_opencode.sh1.8 KB
AGENT.md
opencode-tmux-dsflash-openrouter-gb10
opencode 1.18.23's own terminal UI, running in a tmux session inside the sandbox and
served to the browser by ttyd 1.7.7 (xterm.js over a WebSocket). Model: DeepSeek V4.1
Flash on OpenRouter. No GPU. The container's only egress is openrouter.ai:443, through
the m64 gate, which adds the OpenRouter key: the container never holds it.
This capsule is for a human at a browser. It has no API kind: there is nothing for an agent
to call besides status. An agent that wants to drive opencode programmatically should use
opencode-dsflash-openrouter-gb10 (sessions API + opencode attach); one that wants only
the model should use deepseek-flash-openrouter-gb10 (OpenAI chat completions).
Ports and routes
Main port (the capsule's URL):
| Method | Path | What |
|---|---|---|
| GET | /status |
phase, and app_url: the terminal |
| GET | /agent |
this document plus live status (?format=json: with app_url) |
| GET | /human/ |
landing page linking to the terminal |
| GET | /health |
200 once ttyd answers |
App port (app_url, the front's port + 1 unless --app-port): ttyd, raw.
GET /is ttyd's page;GET /tokenits (empty) auth token;GET /wsthe WebSocket, subprotocoltty. ttyd's protocol: the client first sends a JSON text frame{"AuthToken":"","columns":C,"rows":R}; then binary frames, first byte the command: client0+bytes = input,1+JSON{"columns":C,"rows":R}= resize; server0+bytes = output,1= window title,2= preferences.- Every client attaches to the tmux session
opencode(tmux new-session -A), so all tabs share one opencode, and it survives disconnects. If opencode is quit, the next client starts a new one. The workspace is/scratch/work(the run's scratch).
Security
- ttyd runs without its own auth and without
--check-origin. The app front gives this port exactly the main front's protections: tailnet peers only, a Host allowlist (DNS rebinding), same-site/CSRF refusal, the token rule, and a 403 for any WebSocket upgrade whoseOriginis present and not the app's own origin (tested: a foreign Origin, or the main port's origin, gets 403; a fake Host gets 400 on HTTP and 403 on the upgrade). - ttyd's own
--check-originis left off on purpose. The front forwards Host and Origin verbatim, so behind it ttyd's check would pass for a browser on the right origin, but it adds nothing the front does not already enforce, and it drops every upgrade that carries no Origin at all (non-browser clients such as scripts and tests). - On the tailnet (the default) there is no token: anyone on your tailnet can open the
terminal, i.e. run any command opencode or a shell can run in this container, and spend
the OpenRouter key. With a token, open
app_url?token=<token>once (it becomes a cookie).
capsule.toml
schema = "m64/capsule@1"description = "opencode terminal on DeepSeek V4.1 Flash (OpenRouter). opencode 1.18.23's own TUI, running in tmux inside the sandbox and served to the browser by ttyd: open the app port and use opencode as in a terminal. The session survives closing the tab. Drives DeepSeek V4.1 Flash through OpenRouter. No GPU; the only egress is openrouter.ai:443, and the m64 gate adds the OpenRouter key, so the agent never holds it."# No API kind: the main port only carries /status, /agent, /human and /health; the# terminal is the app ([runtime.app]) on the second port.kinds = []authors = ["dh7"]license = "apache-2.0"[hardware]platform = "linux-aarch64"[runtime]sandbox = "container"# No image: the Dockerfile builds on node:22-bookworm-slim pinned by digest.gpu = false# A tiny health server (health.js); the front needs an internal port and a health route.internal_port = 9500health = "/health"expected_load_seconds = 60user = "1000:1000"# ttyd: the opencode TUI (tmux session "opencode") over HTTP + WebSocket (subprotocol# "tty"). Raw on the second front port, which carries the front's peer, Host, same-site,# WebSocket-Origin and token checks; ttyd itself has no auth.[runtime.app]internal_port = 7681[network]needs = ["openrouter.ai:443"]why = "the model runs on OpenRouter: your prompts and its replies go through openrouter.ai"[secrets]required = ["OPENROUTER_API_KEY"]# The key never enters the container: opencode calls http://openrouter.ai/api/v1 through# the gate, which adds the header and goes on over TLS. The capsule's env var holds only the# placeholder m64-injected:OPENROUTER_API_KEY.[[secrets.inject]]name = "OPENROUTER_API_KEY"host = "openrouter.ai"header = "Authorization"value = "Bearer {secret}"