motif64

Chat · DeepSeek V4.1 Flash · OpenRouter · CPU/API

published by @dh7 on

m64 m64_174cfd55170a
Download .tar.gz

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.

Lineage

parent
none declared
derived from
no published capsule shares half its files
children
none

Files

6 files

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}"