Skip to content

feat(server): add bearer-token auth and safe host exposure#1006

Open
sailist wants to merge 29 commits into
MoonshotAI:mainfrom
sailist:feat/web-auth
Open

feat(server): add bearer-token auth and safe host exposure#1006
sailist wants to merge 29 commits into
MoonshotAI:mainfrom
sailist:feat/web-auth

Conversation

@sailist

@sailist sailist commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR makes the local Kimi Code server safe to authenticate and expose. The server now requires a per-start bearer token on every REST and WebSocket call (the CLI and web UI pick it up automatically), validates Host/Origin headers, and gains a --host flag with a dedicated hardening tier for non-loopback (LAN / public) binds — mandatory password + TLS, auth-failure rate limiting, disabled remote shutdown/terminals, and security response headers. See packages/server/SECURITY.md for the threat model.


1. Bearer-token authentication for the server

Problem: The local server exposed its full REST + WebSocket API with no authentication — anything that could reach the port could drive the agent, read the filesystem, and run terminals.

What was done:

  • Added a per-start bearer token (tokenStore) and a global onRequest auth hook enforcing it on all API routes, with bypass and redaction support.
  • Added an IAuthTokenService DI seam and an env-based bcrypt password-hash utility for password-derived auth.
  • Enforced bearer auth on WebSocket upgrade via a WS bearer subprotocol and parser.
  • The CLI now reads the server token and sends Authorization on its own calls; the /web URL carries the token in its fragment so the browser UI can authenticate.

2. kimi-web authentication flow

Problem: The browser UI had no way to present credentials, so it could not talk to an authenticated server.

What was done:

  • Added a ServerAuthDialog and a serverAuth API client module to prompt for and store the token / password.
  • Wired token injection into the web HTTP and WebSocket daemon clients.

3. Host / Origin request validation

Problem: Without Host/Origin checks, a DNS-rebinding or cross-origin browser request could target the local server even when it is authenticated.

What was done:

  • Added a Host-header allowlist middleware and an Origin/CORS middleware.
  • Wired both into HTTP routes and the WebSocket gateway, and stopped reflecting the Host header in /asyncapi.json.

4. Safe non-loopback (--host) exposure

Problem: Binding the server beyond loopback dramatically increases the attack surface and was previously unguarded.

What was done:

  • Added bindClassify to classify binds as loopback / LAN / public, and registered a --host flag passed through the daemon.
  • On non-loopback binds: require KIMI_CODE_PASSWORD and TLS (or explicit --insecure-no-tls), rate-limit repeated auth failures, disable remote shutdown and terminals by default, add security response headers, and suppress debug routes.
  • The lock file is now created with 0600 permissions.

5. Tests, docs, and changeset

What was done:

  • Added an e2e server harness with token support, an API-surface snapshot guardrail, and extensive e2e/unit coverage (auth middleware + wiring, WS auth, host exposure, host/origin, rate limit, security headers, bind classification, password, lock, debug-route suppression).
  • Added packages/server/SECURITY.md documenting the deployment security model and threat model.
  • Added a minor changeset for @moonshot-ai/kimi-code.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above. (No linked issue — problem and motivation are described in each section above.)
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset. (Changeset: .changeset/server-auth-and-host-exposure.md)
  • Ran gen-docs skill, or this PR needs no doc update. (Adds packages/server/SECURITY.md; user-facing CLI docs update to be confirmed.)

sailist added 28 commits June 23, 2026 11:57
Boot startServer on port 0 and snapshot the documented v1 route table derived from /openapi.json paths, plus the reachability of doc/meta endpoints (/healthz, /openapi.json, /asyncapi.json, /). Gives later auth/--host phases an intentional diff when routes change. M0 makes no production behavior change.
Add test/helpers/serverHarness.ts: boot() wraps startServer with an isolated lock + home dir and returns a handle (server, address, baseUrl, wsUrl, token, close) plus authedFetch/authedWs that carry Authorization: Bearer <token> (and the kimi-code.bearer.<token> WS subprotocol). serviceOverrides is the generic DI seam later phases use to inject a fixed-token auth service; IAuthTokenService is not referenced yet. closeAll() tears down every booted server and socket. M0 makes no production behavior change; typecheck-only gate.
@changeset-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f9fff11

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 23, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@f9fff11
npx https://pkg.pr.new/@moonshot-ai/kimi-code@f9fff11

commit: f9fff11

- Replace native @node-rs/bcrypt with pure-JS bcryptjs so the ESM CLI
  bundle and the SEA native bundle both build without native-addon
  require issues (node-rs/bcrypt broke the ESM smoke and the SEA
  check-bundle allowlist).
- Remove dead cleanup references (stopSpinner, authLogoBlinkTimer) in
  apps/kimi-web App.vue that failed vue-tsc.
- Fix lint: drop empty spread fallbacks in the e2e auth-header merge,
  void the intentionally-async WS upgrade listener, add missing
  assertions to satisfy jest/expect-expect, and convert a ternary
  statement to if/else.
- Send the bearer token in the snapshot perf/smoke tests so they pass
  under the new global auth hook.
- Refresh the pnpmDeps hash in flake.nix for the updated lockfile.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant