MoonshotAI / MoonshotAI/kimi-code
web UI renders ms:// video URLs into <video src>: placeholder, unplayable, CSP-blocked under --host
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
Summary
In --host mode, videos shown in the kimi web UI render as a placeholder and cannot be played. The browser console reports:
Loading media from 'ms://fc833bfx9fx111cppzs1' violates the following Content Security Policy directive: "default-src 'self'". Note that 'media-src' was not explicitly set, so 'default-src' is used as a fallback. The action has been blocked.
User scenario: kimi-code started with --host; the agent runs a script that generates a video on a remote server, copies it back to a local path (e.g. /tmp/h3_fl2va_t2v.mp4), and loads it into the session. The video renders as a placeholder in the web UI and is unplayable.
Verified against kimi-code main @ dceb3fd (0.38.0) and kimi-code-app main @ 97006d3.
Root cause
In the v2 architecture, ms:// (Moonshot platform file IDs) is supposed to exist only in the in-memory copy of an LLM request built by mediaResolverService — it is never written back to history for user messages. But the ReadMediaFile tool persists provider-side ms:// references into history as tool output:
agent-core-v2/src/agent/tools/read-media-file/readMediaFileTool.ts:196-213—videoContentPartcalls the video uploader and embeds the resultingms://part in the tool output ([<video path="/tmp/…">, video_url ms://…, </video>], see:458-474). This tool message is persisted into context / the wire journal.kap-server/src/services/messages/messageProjection.ts:32-37— by design, any non-kimi-file://video_urlis passed through verbatim as{kind:'url', url}(locked in bymessageProjection.test.ts:111-121); tool messages with media parts are output as-is at:46-51. This reaches the web client viaGET /api/v1/sessions/{id}/messagesand the transcript WS frames (services/transcript/coreEventMap.ts:838-851).- Client-side last line of defense:
kimi-code-app/packages/app-core/src/client/messagesToTurns.ts:315-318tries to recover afileIdfrom the<video path>when the URL isms://, butfileIdFromCachePath(:80-92) only accepts daemon cache path shapes (f_<ULID|UUID>basename). An arbitrary script-generated path like/tmp/h3_fl2va_t2v.mp4does not match →fileIdis undefined. kimi-code-app/apps/web/src/components/chat/AuthMedia.vue:34,56-61,111-120— with nofileId,resolvedUrl = props.urlis emitted verbatim, so the DOM gets<video src="ms://…">. The browser cannot load thems://scheme; under--host, the CSP (kap-server/src/middleware/securityHeaders.ts:9-10, applied only for non-loopback binds perstart.ts:341-343) has nomedia-src, sodefault-src 'self'blocks it first. The desktop renderer (apps/desktop/.../AuthMedia.vue, byte-identical) has the same flaw, failing with an unknown-scheme error instead of CSP.
Note: the user-attachment path (attach a video to a prompt) is not affected in v2 — it stores kimi-file://<f_id> and projects to {kind:'session_media', file_id}, which streams correctly via GET /api/v1/sessions/{id}/media/{file_id} (kap-server/src/routes/sessionMedia.ts:48-110). The ms://-into-history behavior described here matches the v1 engine (agent-core/src/agent/turn/index.ts:593-599), but the leaking path on v2/--host is specifically ReadMediaFile tool results.
Aggravating finding: CSP may block even healthy video playback under --host
The CSP sets no media-src, so it falls back to default-src 'self'. The healthy path (AuthMedia authenticated fetch → blob: URL → <video>) uses a blob: src, which is not covered by 'self' — so even correctly attached videos may be CSP-blocked under --host. (Inferred from the CSP spec; not browser-verified.)
Suggested fix shape
A. Fix at the source (primary): make ReadMediaFile video results reference a servable file. In readMediaFileTool.videoContentPart, materialize the bytes into the session media store and return a kimi-file://<f_id> part instead of the provider ms:// part. Request building still resolves/uploads to ms:// via mediaResolver as today, but history only ever contains daemon-servable references. Projection and web client need zero changes (the kimi-file:// → session_media route already exists). Also add media-src 'self' blob: to securityHeaders.ts (one line; verify the blob-video CSP question above in a browser).
B. Server-side re-sourcing route for ms:// (proxy Moonshot platform files through kap-server): needs provider credentials, platform file TTL handling, auth/caching/failure semantics — high effort, fragile payoff. Not recommended as the primary fix.
C. Client-side graceful degradation (required regardless): in kimi-code-app (web + desktop), recognize ms:// with no recoverable fileId and render a "video unavailable (provider reference cannot be served)" placeholder instead of a bare <video src="ms://…">. Needed for existing sessions whose history already contains ms:// references that can never be re-sourced.
Recommended combination: A + C, plus the one-line CSP media-src addition.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with agent-core-v2/src/agent/tools/read-media-file/readMediaFileTool.ts, then inspect AuthMedia.vue in the web and desktop apps and kap-server/src/middleware/securityHeaders.ts. Run the referenced message projection tests and add coverage for the affected media paths. Done means persisted tool results use servable references, unresolvable ms:// media degrades gracefully, and blob video playback is allowed under --host.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- full-stack, security, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100