# feat\(files\): open markdown, HTML, and PDF files outside the workspace · PR \#9140 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/9140)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: t3code/open-outside-workspace-files

Target branch: main

Head commit: c0fa311c308958fa1d6249e57d233e77254d6724

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/9140)

[Commits](https://git.cafe/pingdotgg/t3code/pull/9140/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/9140/diffs?format=markdown)

## Description

---

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and `projects.readFile` rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

## How

- **Server**: `WorkspaceFileSystem.readFile` accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and `writeFile` still rejects absolute paths, so host files are read-only. The `media-file` asset resource now also accepts `.html`, `.htm`, and `.pdf`, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped `workspace-file` resource so sibling assets load.
- **Web**: file chips carry a `panelPath` that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. `openFileInPreview` picks `workspace-file` or `media-file` by workspace membership, so the panel's browser button and PDF chips work for host files.
- **Mobile**: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a `media-file` resource), and the asset URL hook picks `media-file` for host paths.
- **Docs**: user note in `composer.md`; `environment-auth.md` updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: `media-file` and `readFile` keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated `orchestration:read` client can now read any text file the server account can read, not only workspace files. That matches what `filesystem.browse` and `media-file` already allow and is documented as such.

## Demo

Clicking a `/tmp/...cleanup-report.md` chip opens it in the files panel, toggling to rendered Markdown, then opening the `.html` chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

## Verification

- `apps/server`: `WorkspaceFileSystem.test.ts` (absolute read, absolute write rejected), `AssetAccess.test.ts` (HTML/PDF minting), `http.test.ts`
- `apps/web`: `filePath.test.ts` (host breadcrumbs), `markdown-links.test.ts`, `ChatMarkdown.*.test.tsx`, `FilePreviewPanel.test.ts`
- `apps/mobile`: `filePath.test.ts` (route segments round-trip)
- Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Expands what authenticated `orchestration:read` clients can read and preview on the host (any text file via absolute `readFile`, HTML/PDF via `media-file`), though writes stay workspace-bound and HTML gets a sandbox CSP.
> 
> **Overview**
> Agents can link files outside the project (e.g. `/tmp/.../report.md`); this PR wires those links through end-to-end instead of stopping at workspace-only paths.
> 
> **Server:** `projects.readFile` / `WorkspaceFileSystem.readFile` now accept an **absolute host path** and read that file in place (still **read-only**—`writeFile` rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use **non-blocking** mode so FIFOs fail instead of hanging. The **`media-file`** asset resource now allows **HTML and PDF** (via `hostPreviewMimeTypeFromExtension`), and inline HTML responses add a **sandbox CSP**.
> 
> **Web & mobile:** File chips carry a **`panelPath`** that can be the absolute host path; the files panel shows host files **read-only** (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. **`openFileInPreview`** and asset URL minting choose **`workspace-file` vs `media-file`** based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on **`imageBaseDir`** when rendering a file outside the repo.
> 
> **Docs/contracts:** User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit c0fa311c308958fa1d6249e57d233e77254d6724. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- Macroscope's pull request summary starts here -->
<!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. -->
<!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. -->
> [!NOTE]
> ### Support opening markdown, HTML, and PDF files outside the workspace
> - Server `WorkspaceFileSystem.readFile` now accepts absolute host paths via `resolveReadTarget`, resolving them through `realpath` without applying the workspace-root boundary check; writes remain workspace-relative only.
> - Asset URL issuance and resolution in `AssetAccess` accept HTML and PDF (in addition to images and videos) using the new shared `hostPreviewMimeTypeFromExtension` classifier; unsupported extensions still return no asset.
> - Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional `baseDir` so relative links anchor to the rendered file's directory rather than only the workspace cwd.
> - HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
> - File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
> - Risk: `WorkspaceFileSystem.resolveReadTarget` bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.
>
> <!-- Macroscope's review summary starts here -->
>
> <sup><a href="https://app.macroscope.com">Macroscope</a> summarized c0fa311.</sup>
> <!-- Macroscope's review summary ends here -->
>
<!-- Macroscope's pull request summary ends here -->
