Install
openclaw skills install @askegor/space-duck-kimi-relayOptional Lane A / BYOB add-on for Space Duck — runs a local RFC 8628 device-code "Sign in with Kimi" flow (no password; browser-approved) so a self-hosted duck can use the owner's flat-rate Kimi membership for inference. Credentials (access + rotating refresh token) are stored locally at ~/.kimi-code/credentials/kimi.json (0600) and are NEVER sent to Spaceduckling. Contacts only auth.kimi.com and api.kimi.com; inference is processed by Moonshot AI in China (no Western data residency). Optional pay-per-token fallback to openrouter.ai when OPENROUTER_API_KEY is set (daily-capped). Runs a localhost-only proxy (127.0.0.1, default 8471) protected by an auto-generated 0600 bearer secret. Hosted (Lane B) ducks use the Mission Control card instead. Triggers on "sign in with kimi", "kimi membership login", "clawhub space-duck kimi", "kimi relay login".
openclaw skills install @askegor/space-duck-kimi-relayLets a duck that runs on your own infrastructure use your Kimi membership (flat-rate subscription quota) for inference instead of pay-per-token API keys.
~/.kimi-code/credentials/kimi.json (0600), on
your machine only.auth.kimi.com and
api.kimi.com). Spaceduckling never sees or holds the token.kimi_login.py login # interactive device sign-in
kimi_login.py token # print fresh access token (auto-refresh, file-locked)
kimi_login.py probe # inference smoke on membership quota
kimi_login.py serve [port] # local proxy for your runtime (default 8471)
kimi_login.py install-service [port] # run proxy as a service (systemd user / launchd)
kimi_login.py uninstall-service # remove the service
kimi_login.py status [port] # creds + proxy + fallback-meter health check
kimi_login.py logout # delete local credentials
login shows a kimi.com URL + user code; approve it in your browser and
the script stores the tokens. token transparently refreshes — Kimi
access tokens live ~15 minutes and refresh tokens rotate on every
grant, so always let this script (not ad-hoc curl) do the refreshing;
a stale refresh token is dead after one rotation.
Kimi access tokens live ~15 minutes, so a static key in your runtime's config will not survive. Run the local proxy instead:
kimi_login.py serve # http://127.0.0.1:8471/v1/chat/completions
Point your runtime's OpenAI-compatible provider at
http://127.0.0.1:8471/v1. As the api key, use the proxy secret
that serve / status prints (auto-generated at
~/.kimi-code/credentials/proxy_secret, 0600) — without it the proxy
answers 401, so other local processes/users can't spend your quota.
KIMI_RELAY_NO_AUTH=1 disables the check (single-user boxes only).
The proxy injects a fresh membership token per request (refreshes under
a file lock — safe when several ducks on one box share the login; note
the fallback cap below is per-box, so ducks sharing a box share the
budget).
k3 (full), kimi-for-coding (budget)reasoning_content and spends completion budget on it —
give it roomy max_tokens (512+) or replies can arrive empty.Streaming ("stream": true) is passed through as SSE, so chat UIs get
token-by-token output on the membership lane.
Automatic fallback: export OPENROUTER_API_KEY before serve and
any failed membership call (quota/auth/outage) is retried once on
OpenRouter's kimi lane (moonshotai/*, pay-per-token) — same
degradation the hosted lane performs. Fallback is metered: capped at
KIMI_RELAY_FALLBACK_DAILY_CAP calls/day (default 200) so a broken
membership can't silently run up a pay-per-token bill; past the cap the
proxy returns 429. If the client asked for stream: true, the fallback
reply is wrapped as a single SSE chunk + [DONE] so streaming clients
still get valid SSE. Without the env var, failures return the error so
your runtime's own ladder takes over.
For a proxy that survives reboots, install-service writes a systemd
user unit (Linux — enable lingering with
loginctl enable-linger $USER so it runs while logged out) or a
launchd agent (macOS). Any OPENROUTER_API_KEY / KIMI_* env vars set
when you run install-service are written to a separate 0600
EnvironmentFile (~/.kimi-code/credentials/relay.env) that the unit
references — secrets are never inlined into the world-readable systemd
unit itself — so the fallback survives reboots without leaking the key.
status shows creds, proxy health, and the fallback meter.
Env overrides: KIMI_CLIENT_ID (if Moonshot rotates the public
client), KIMI_AUTH_HOST, KIMI_CODING_BASE,
KIMI_RELAY_FALLBACK_DAILY_CAP.
Refresh tokens rotate on every grant. That means:
~/.kimi-code/credentials/kimi.json
to a second box and let that box run (token, probe, or serve), it
will refresh and rotate the shared refresh token — the box you copied
from will silently strand on the next refresh.kimi_login.py login. Never copy kimi.json
back to the original box.kimi_login.py login) — that keeps
the token in local custody where the trust model expects it. Only if
you cannot log in on the remote box, a short-lived access token from
kimi_login.py token can be used for a brief test: it is valid ~15 min,
has no refresh capability, and cannot strand any lineage. Treat it like
any secret — do not paste it into logs, chat, or shared terminals, and
let it expire rather than storing it.kimi.json across two
long-running services; give each its own login.Kimi inference is processed by Moonshot AI on infrastructure in China. Don't route conversations through this lane if you require Western data residency.
| Script | Purpose |
|---|---|
scripts/kimi_login.py | Device sign-in, token refresh, probe, logout |
space-duck skill works without it.