ADR 0181: Main-owned picker capabilities
- Status: Accepted
- Date: 2026-09-08
- Deciders: PI-Desktop core
- Related: ADR 0059, ADR 0172, D197, D334, D344,
03-runtime/01-ipc-protocol.md§13c, E2E-102h
Context
Composer attachments can be selected through a native Electron file picker and then copied into the owning session's scratch directory. The renderer needs to request the copy after it materializes a home draft, but a renderer IPC payload must not be treated as proof that a user selected the paths it contains.
The earlier picker flow returned native absolute paths to the renderer and then accepted those paths again through composer/importFiles. That allowed any renderer code with access to the bridge to request a copy of an arbitrary regular file, including outside the workspace and attachment roots. The same flow also advertised directory selection even though the importer accepted regular files only.
Decision
composer/pickFilesandcomposer/pickPhotosexecute the native dialog in Electron main. Main stores the selected paths against a random token bound to the invokingWebContents.- The picker token expires after 60 seconds and is consumed before the import starts.
composer/importFilesaccepts the token and durablesessionId, never renderer-supplied source paths. A token cannot be replayed or used by another renderer WebContents. pickFilesoffers regular files only in the MVP. Folder import remains a separate future feature that must define bounded traversal and ownership rules before it is exposed in the UI.- Main still realpaths and stats every recorded source, enforces the existing per-file and total-size limits, and copies only into the session scratch directory.
Consequences
- A compromised or stale renderer cannot turn the picker import channel into an arbitrary absolute-path copy primitive.
- Canceling a picker does not create a draft session or write scratch files.
- The renderer retains only the returned session-owned scratch references after import, while the source paths remain in Electron main.
- Directory selection is no longer offered with a misleading unsupported label.
Alternatives rejected
- Returning native paths from the picker and validating them again in the import IPC: rejected because validation does not prove user selection.
- Importing directly before returning from the picker IPC: rejected because the renderer must materialize a home draft only after a non-canceled selection.
- Supporting folders by recursive copy in this change: rejected because it needs explicit traversal, size, symlink, and UI semantics beyond the current MVP.