Description
Written-by: Chief
A room line is not local: appendRoom fans every line to the room's remote seats (core/src/host/room-membership.js:2270), so a friend's Bot reads it on the far side. Two places build a room line out of this host's own detail:
core/src/host/images.js:210—imagesForGrouppassesattachmentFilePath()intoimagePlaceholder(images.js:77), the room text for a human image post (groups.js:820logPost→:1120→appendRoom:84). The line reads[Image "holiday.png" (image/png, 9 bytes): it could not be read right now. The file is at "/tmp/…/attachments/v1/objects/ab/…"; your own file tools can open it.— an absolute path of this host, addressed to a Bot that is not on this host.core/src/host/room-participant-external.js:207—reportExitbuilds the crash detail asexit 1: <stderr tail>(the lastSTDERR_TAIL = 200bytes) andreport(:88) posts it viart.externalRoomLine(groups.js:245→appendRoom→fanoutRoomLine). A child that dies at startup names the file it could not open and any token-shaped string it printed:crashed (exit 1: Error: ENOENT: no such file or directory, open '/data/dsh/bot/home/state.json' token=ghp_…).
Reproduction (main 1913ebc7a6af808ec9039cb2c7fdd14a21608e4f)
/data/dsh/bot/workspace/audit44/leak.test.mjs — real modules, scratch home, a room with a remote seat; node --test → 2/2 pass, 0.4 s. What the far side receives:
Alice: <untrusted-message from="Alice">
[Image "holiday.png" (image/png, 9 bytes): it could not be read right now. The file is at "/tmp/audit44-…/attachments/v1/objects/ab/…"; your own file tools can open it. …]
</untrusted-message>
Chief A: <untrusted-message from="Chief A">
Chief A's external process crashed (exit 1: Error: ENOENT: no such file or directory, open '/data/dsh/bot/home/state.json' token=ghp_FAKE…). Its turns in this room have no answer until it starts again.
</untrusted-message>
Reverse controls in the same file: imagePlaceholder(attachment, { group: true }) carries no path, and a crash line of only exit 1 carries neither path nor token.
Why it matters
The room is the one surface that crosses machines. A path tells the far side the layout and user name of this host (/tmp/…, /data/dsh/bot/home/…), and the stderr tail can carry anything the child printed before dying — including a token, as the reproduction does with a deliberately fake one. Nothing redacts a room line before sendShareTo; the exposure is silent, and the friend's Bot may repeat what it read into its own room or model context.
Blocked, for the record: the same placeholder in a Bot's own chat (images.js:197) is read only by that Bot's model on this host; groups.js:722's Session failure text returns through groups.js:907 to the same home's tool result; capabilities.js:290 runs rt.redactSecrets first.
Expected
A room line carries what the room needs and nothing about this host: the image placeholder names the attachment by its id and size in a group and only offers a path where the reader is local (images.js:197's own-chat case may keep it); a crash line states exit <code> / signal <name> and keeps the stderr tail in rt.warn/the local log, where the owner can read it and a remote seat cannot. If a redaction helper already exists (rt.redactSecrets, oneLine), the room-line path uses it rather than each builder remembering.
Change scope
core/src/host/images.js (imagesForGroup call, imagePlaceholder's group form), core/src/host/room-participant-external.js (reportExit's detail and the report path), and a test that asserts the bytes a remote seat receives carry neither a path nor the tail. docs/subsystems/core-host.md / social.md if a room-line rule is stated there.
Activity
HugoXK commentedon Nov 8, 2026
Claim
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 617f9722adda49289ff581a30da565af
Branch: dev/2699-a-room-line-tells-the-far-side-this-host
Base: 1913ebc
At: 2026-11-08T17:01:54Z
Keep this host's paths and keys out of room lines (ThinkFlowLab#2700)
Release v0.2.27 (ThinkFlowLab#2701)