ADR 0172: Contained in-chat image display
- Status: Accepted
- Date: 2026-09-07
- Related: ADR 0019, ADR 0163, ADR 0169, decisions D320/D334, E2E-187
- Amends: the
fs/readworkspace-only clause of the desktop IPC spec
Context
Pasted and uploaded images are stored as extension-less attachments/<sha256> blobs under the data directory. Local Markdown images are workspace-relative. The renderer origin cannot load those files, so history turns showed a chip instead of the picture. Opening an attachments/<sha256> chip through fs/open also failed because the path was treated as workspace-relative.
A generic "any absolute regular file" read channel would let the renderer exfiltrate arbitrary disk contents.
Decision
- Keep
fs/listworkspace-only. Extendfs/read,fs/reveal, andfs/openso they shareresolveOpenablePath: workspace-relative paths, absolute paths already inside the workspace /<data_dir>/scratch//<data_dir>/attachments/, and content-addressedattachments/<sha256>refs. Reads additionallyrealpaththe target and re-check containment. - Add renderer-only
fs/readImageDataUrl({ref, mimeType?}). It returns a bounded image data URL ormissing/notImage/tooLargeand never returns non-image bytes. It is not a plugin host API; plugins keepfs.readPreview. - A known image extension always wins over a client-supplied
mimeType. Extension-less attachment blobs accept only the existingIMAGE_MIMEallowlist. Arbitraryimage/*values are ignored. - Chat thumbnails and local Markdown images load through that channel. Clicking a resolved image opens the host files viewer on the same ref.
Consequences
- History attachments and local Markdown images render inline when the file is still present and under the cap.
/etc/passwdand other outside-root paths stay unreadable even with a spoofedmimeType.- Plugin Files views remain workspace-scoped; transcript artifacts still use the host
file:tab.
Alternatives rejected
New unbounded absolute-path read
Rejected because the renderer IPC is not a user-click gate.
Reuse fs.readPreview from the chat renderer
Rejected because that API is plugin-scoped to the workspace root and cannot see attachments/<sha256> blobs.
References
apps/desktop/electron/main/fs-panel.tsapps/desktop/src/lib/use-referenced-image-data-url.tsdocs/spec/03-runtime/01-ipc-protocol.md