Chat · DeepSeek V4.1 Flash · OpenRouter · CPU/API
published by @dh7 on
m64 m64_174cfd55170aOpenRouter with a secret: a chat endpoint that forwards to a model on OpenRouter. Shows [network], [secrets] and [[secrets.inject]]: your OPENROUTER_API_KEY stays in m64's gate and never enters the container. No GPU.
Lineage
- parent
- none declared
- derived from
- no published capsule shares half its files
- children
- none
Files
6 files- human/
- index.html3.0 KB
- AGENT.md1.5 KB
- capsule.toml2.2 KB
- README.md1.5 KB
- server.py3.9 KB
- start.sh310 B
AGENT.md
openrouter-secrets
An OpenAI-compatible chat endpoint for DeepSeek V4.1 Flash on OpenRouter. The capsule only forwards requests; m64's gate adds the OpenRouter key on the way out, so the capsule never holds it.
Base URL: {{BASE_URL}}
Routes
| Method | Path | What |
|---|---|---|
| POST | /v1/chat/completions |
chat completions, streaming or not |
| GET | /v1/models |
one model: deepseek-v4.1-flash |
| GET | /status |
phase (ready when usable) |
| GET | /human/ |
a page for people |
curl -s {{BASE_URL}}/v1/chat/completions {{AUTH}} \
-H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"Say hi"}],"max_tokens":50}'
curl -N {{BASE_URL}}/v1/chat/completions {{AUTH}} \
-H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"Count to 5"}],"stream":true}'
Accepted fields: messages (required), tools, tool_choice,
response_format, max_tokens, stream, seed. Others, like temperature,
get a 400. The model is always DeepSeek V4.1 Flash, whatever model says.
Timings and errors
- Ready in a few seconds. Answers take as long as OpenRouter takes.
- OpenRouter's errors keep their status and body: 401 means the key is missing or wrong, 429 a rate limit.
- 502 means openrouter.ai could not be reached: the capsule was served without
--allow-external-network. - Requests are handled in parallel. Every request spends credit on the key's OpenRouter account.
capsule.toml
# openrouter-secrets: a capsule that uses an API key without ever holding it.## It adds three things to hello-world:# [network] the one host it may reach, and why (the person reads `why`# before granting it at serve time)# [secrets] the key it needs, by name; never its value# [[secrets.inject]] how m64's gate adds the key to outgoing requests, so the# key stays outside the containerschema = "m64/capsule@1"description = "OpenRouter with a secret: a chat endpoint that forwards to a model on OpenRouter. Shows [network], [secrets] and [[secrets.inject]]: your OPENROUTER_API_KEY stays in m64's gate and never enters the container. No GPU."kinds = ["chat"]authors = ["motif42"]license = "mit"[hardware]# CPU only. On an ARM machine use "linux-aarch64" (the image is multi-arch).platform = "linux-x86_64"[runtime]sandbox = "container"# python:3.12-slim (multi-arch index digest); server.py is standard library only.image = "python@sha256:2f17fc044b579bab302c2e8054d3a686e2cb9a83de48e70534b94cd8ebbe06a9"gpu = falseinternal_port = 9000health = "/health"expected_load_seconds = 10model_name = "deepseek-v4.1-flash"user = "65534:65534"# Without this table the capsule has no network at all. With it, the person# still has to grant it: `m64 serve <folder> --allow-external-network`.# Every host the code names must be listed here, or `m64 check` blocks the push.[network]needs = ["openrouter.ai:443"]why = "the model runs on OpenRouter: your prompts and its replies go through openrouter.ai"# The capsule needs this secret to start. m64 looks for it in# ~/.config/m64/secrets.env (set it with `m64 config secrets set OPENROUTER_API_KEY`)# or in the file given with --secret-file. A missing secret refuses the serve.[secrets]required = ["OPENROUTER_API_KEY"]# The key never enters the container. server.py calls http://openrouter.ai# (plain http) through $HTTP_PROXY, which is m64's gate. The gate adds# `Authorization: Bearer <key>` for that host only, then goes on over TLS.# Inside the container, $OPENROUTER_API_KEY holds only a placeholder.[[secrets.inject]]name = "OPENROUTER_API_KEY"host = "openrouter.ai"header = "Authorization"value = "Bearer {secret}"