Chat · DeepSeek V4.1 Flash · OpenRouter · CPU/API
published by @dh7 on
m64 m64_04b2a07e4eacDeepSeek V4.1 Flash chat endpoint (OpenRouter). An OpenAI-compatible chat endpoint for DeepSeek V4.1 Flash on OpenRouter, for opencode (or any client) running on your own machine: opencode and its file access stay there, only the model calls come here. Streams. No GPU; the only egress is openrouter.ai:443, and the m64 gate adds the OpenRouter key, so the capsule never holds it.
Lineage
- parent
- none declared
- derived from
- no published capsule shares half its files
- children
- none
Files
5 files- human/
- index.html9.0 KB
- AGENT.md2.9 KB
- capsule.toml1.6 KB
- server.py4.8 KB
- start.sh391 B
AGENT.md
deepseek-flash-openrouter-gb10
An OpenAI-compatible chat endpoint for DeepSeek V4.1 Flash (OpenRouter model
deepseek/deepseek-v4.1-flash). It only provides the model: run opencode (or any
OpenAI-compatible client) on your own machine and point it here. Your files and tools
stay on your machine; only the model calls come to this capsule.
No GPU. The container's only egress is openrouter.ai:443, through the m64 gate,
which adds the OpenRouter key: the capsule never holds it.
Routes
All paths are relative to the capsule's URL (e.g. http://<tailnet-ip>:<port>).
| Method | Path | What |
|---|---|---|
| POST | /v1/chat/completions |
OpenAI chat completions, streaming or not |
| GET | /v1/models |
one model: deepseek-v4.1-flash |
| GET | /status |
phase (ready when usable) |
| GET | /agent |
this document plus live status |
| GET | /human/ |
landing page: opencode setup and a quick-test chat |
POST /v1/chat/completions
Body: application/json only (415 otherwise). Accepted fields:
messages(required),tools,tool_choice,response_format,max_tokens,stream,capsule;model,user,stream_optionsare accepted and ignored or passed on;nmust be 1. The model is always DeepSeek V4.1 Flash, whatevermodelsays.
Any other field (e.g. temperature, top_p) is refused with 400 naming the field.
stream: true returns Server-Sent Events (data: {...} chunks, ending with
data: [DONE]), relayed as OpenRouter sends them. OpenRouter may interleave SSE comment
lines (: OPENROUTER PROCESSING) while the model thinks; SSE clients ignore them.
Non-streaming responses are OpenRouter's JSON, plus params_used.
Errors from OpenRouter (rate limits, upstream failures) keep their status and body.
curl -s $URL/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"Say hi"}],"max_tokens":50}'
curl -N $URL/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"Count to 5"}],"stream":true}'
opencode on your machine
~/.config/opencode/opencode.json (global) or opencode.json in the project root:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"dsflash": {
"npm": "@ai-sdk/openai-compatible",
"name": "DeepSeek V4.1 Flash (capsule)",
"options": { "baseURL": "<capsule URL>/v1" },
"models": { "deepseek-v4.1-flash": { "name": "DeepSeek V4.1 Flash", "tool_call": true } }
}
},
"model": "dsflash/deepseek-v4.1-flash"
}
Or, on a machine with m64: m64 deepseek-flash-openrouter-gb10 --ui opencode
serves the capsule and starts opencode with a generated config pointing at it.
Access
On the tailnet (the default bind) there is no token: anyone on the tailnet can use the
endpoint and so spend the OpenRouter key. With a token (--host 0.0.0.0 etc.), send
Authorization: Bearer <token> (opencode: "apiKey": "<token>" in the provider options).
capsule.toml
schema = "m64/capsule@1"description = "DeepSeek V4.1 Flash chat endpoint (OpenRouter). An OpenAI-compatible chat endpoint for DeepSeek V4.1 Flash on OpenRouter, for opencode (or any client) running on your own machine: opencode and its file access stay there, only the model calls come here. Streams. No GPU; the only egress is openrouter.ai:443, and the m64 gate adds the OpenRouter key, so the capsule never holds it."kinds = ["chat"]authors = ["dh7"]license = "apache-2.0"[hardware]platform = "linux-aarch64"[runtime]sandbox = "container"# python:3.12-slim (multi-arch index digest). server.py uses the standard library only.image = "python@sha256:2f17fc044b579bab302c2e8054d3a686e2cb9a83de48e70534b94cd8ebbe06a9"gpu = falseinternal_port = 9600health = "/health"expected_load_seconds = 20# The front rewrites every request's "model" to this; server.py maps it to# deepseek/deepseek-v4.1-flash on OpenRouter. Also the name `m64 <capsule> --ui opencode` uses.model_name = "deepseek-v4.1-flash"user = "65534:65534"[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: server.py 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 (and server.py does not even read it).[[secrets.inject]]name = "OPENROUTER_API_KEY"host = "openrouter.ai"header = "Authorization"value = "Bearer {secret}"