# Trusted folders

> Trust a folder to give every HTML Clay file inside it a stable address, editable pages, and a write grant, and learn the security boundary that comes with it.

Source: https://htmlclay.com/docs/guide/trusted-folders/

Double-click a file and it opens in an ordinary tab in your browser. Trust a folder when the files inside belong together: a trusted folder is HTML Clay's durable write grant, and every HTML Clay file in it, now or later, can become editable without another prompt and can follow links to other HTML Clay files in the folder.

## The folder keeps a stable address

A trusted folder gets one stable hostname on the app's one port. Every `.htmlclay` file inside opens editable without another prompt, including files added later, and relative links work like they do on any static site.

- **Stable after a restart.** HTML Clay remembers the folder's hostname and the port.
- **Browser storage stays put.** Cookies and browser storage keep the same origin.
- **Trust ends immediately.** Untrusting the folder closes its origin and invalidates its save tokens.

Opening one file explicitly gives its page read access to its own folder, so relative CSS, scripts, images and links work, and gives that one document a save token. A trusted folder is broader and persistent: it gets one stable hostname on the app's one port, every `.htmlclay` file inside it can auto-register as editable including files added later, relative links work across the project, browser storage stays on that hostname, and bookmarks survive restarts while the port is still the one they were made on.

## Two rules carry the whole design

1. **Only files you choose can be saved.** That choice takes two forms: a file you opened yourself, and any HTML Clay file inside a folder you have trusted. Everything else, a file reached by a link, an iframe, or a typed URL, is served read-only and never gets a save token.
2. **A page can only read inside the folder you opened it from.** A request for anything outside that folder pauses and asks you, in a dialog HTML Clay draws itself, to widen reads to one named folder.

One sentence: a page can only change itself. A trusted folder is the one exception, and inside it every HTML Clay file can change every other, forever, including files that arrive later. Trust only a folder you control completely.

## Images live in one uploads folder

An editable page can upload a file into `~/htmlclay/uploads/`, in a folder named after the document, such as `assets-notes/`. The page receives a link that keeps working when you rename or move the document. An upload can be up to 25 MB, and HTML Clay stores it once by content hash rather than repeating it as base64 inside every save and version.

- **Files with different bytes get different names.**
- **HTML and JavaScript uploads are refused.**
- **Each document gets its own asset folder.**

## What is always refused

Always refused, trusted or not: anything outside your home folder, dotfiles and dot-directories such as `.env`, `.git`, and `.ssh`, HTML Clay's own settings and version history, and directory listings. A refusal is a single fixed response that names no path and does not depend on whether the file exists, so a page cannot use refusals to work out which files you have. Symlinks are resolved and the real path of the opened file descriptor is checked before any bytes are served. Save tokens are per-file, 256 bits, and never written to the log. Save and restore both replace the file atomically rather than truncating in place.

## Three ways to trust a folder

There are three ways to trust a folder, and the two page routes have different rules. Getting this backwards is the usual mistake, so it is worth stating exactly.

The personal folders in question are the top-level names `Desktop`, `Documents`, `Downloads`, `Library`, `Movies`, `Music`, `Pictures`, `Public`, plus the sync roots `Dropbox`, `Google Drive`, `Nextcloud`, `Sync`, `Box`, and `OneDrive`. `OneDrive` matches by the start of the name, so a work account's `OneDrive - Contoso` is covered. When Windows folder backup has moved `Desktop`, `Documents`, or `Pictures` inside a sync root, those are covered there too. Matching is case-folded and follows symlinks, so a `Documents` that points at an external drive is still recognized.

1. **The tray's "Trust a Folder…" picker** consults none of the rules below, because choosing a folder from a picker is already a deliberate act. It warns once before letting you pick a personal folder, then does it.
2. **The read-permission dialog's "Trust this folder" button**, on a page that asked to read outside its folder. The page steers which folder gets named here, by choosing which files it asks for, so the rule is the strict one: a personal folder, **anything inside one**, and any ancestor of one are all refused, and the button is simply not shown. The practical cost is real: if your projects live under `~/Documents` or `~/Desktop`, this route will never offer to trust them.
3. **A page asking to trust its own folder**, either from the read-only banner or from a script. The folder is pinned to the requesting file's own directory, so a page cannot inflate it, and the rule is the looser one: a personal folder itself and any ancestor that would swallow one are refused, but a folder **inside** a personal folder is allowed. `~/Documents/GitHub/myproject` is trustable this way; `~/Documents/GitHub` itself is refused, because it would take the whole checkout tree.

Both page routes raise a native dialog naming the requesting file and the folder, which is always and only the folder that file actually sits in. The banner's button presents a single-use, server-minted code that resolves to the exact file that page was served from; the code expires in ten minutes, dies on first use, and a Deny quiets that folder for the rest of the session. The dialog also has an "Allow Once" choice, which widens reading only, never writing, and is forgotten when you quit.

## Untrusting a folder

Untrusting genuinely ends the capability: the folder's origin closes, files it auto-registered lose their save tokens immediately, their live-sync streams close, and a save from a page that was already open is re-checked at write time. Files you had opened yourself survive on a new address of their own, because opening a file was a separate decision, and stored helper decisions for those files remain separate from folder trust.

## The folder's identity

Each trusted folder is pinned to the folder's identity on disk: volume UUID and inode on macOS, device and inode on Linux, volume serial and file id on Windows. A folder deleted and replaced stops granting anything, its address stops being served, and it shows in the tray as "missing" rather than silently covering the newcomer. Approving the dialog again re-pins it to the folder now on disk, which is the one way back. A folder that is changed into a link to another folder is treated the same way, and if the filesystem cannot supply a stable identity, HTML Clay falls back to treating the stored path as the identity.

Windows gained that fingerprint later than the other two platforms. An entry recorded before it did carries an empty pin, which means the path is the whole of that entry's identity; it keeps working and is pinned to the directory currently there on the next launch. `SECURITY.md` and the features page still describe Windows folder trust as path-only, but the code has moved past them: `internal/platform/dirid_windows.go` implements `DirIdentity`, `internal/config/config.go` backfills a pin for older entries, and `internal/trust/policy.go` compares it on every platform.

## The limits of the bridge

HTML Clay is a local bridge, not a sandbox for untrusted HTML. The browser still sandboxes the page as a web page, and the page can still run JavaScript and make network requests like any other page. The known limitations are enumerated in `SECURITY.md` rather than left to be discovered.

Plainly: **one hostile HTML file inside a trusted folder can rewrite every other HTML Clay file in that folder.** It can open each sibling in a visible tab, read that page's save token through the shared origin, and overwrite it. A silent background fetch still receives no token, so this path is visible, but it is possible.

- **A trusted folder trusts everything in it.** A hostile HTML file placed inside one can read that whole tree and send it somewhere, and it can open its siblings in visible tabs, lift their save tokens, and overwrite those files too. A worm is possible; because tokens ride only on real navigations, it is not invisible. Only trust folders where you control the HTML and JavaScript.
- **The Linux and Windows permission prompts are beta.** They are implemented and reviewed but not yet exercised on real machines. They fail closed: if a dialog cannot run, access is denied. On Linux the prompts are drawn by `zenity`, falling back to `kdialog`; with neither installed every prompt fails closed, and the app says so at startup, in the log, in a notification, and in a permanent tray row. The folder picker also falls back to the XDG desktop portal (`org.freedesktop.portal.FileChooser`), which needs no helper binary.
- **Version history is not protection from a helper.** It records saves made through HTML Clay and normally keeps at least the last 20 versions plus everything from the last 60 days. That helps recover from page-based overwrites, but a helper runs with your file permissions, so it can change or delete the version store.

Also true, and load-bearing when you reason about the bridge: the server listens on loopback, so any process running as your user can reach the app's port on loopback, under any of its hostnames. Saving requires the per-file token, but the top-level serve route returns a currently-open file by path with no token, so a malicious local process, or a page you already have open, can read other open documents. This is inherent to the localhost-bridge model. HTML Clay serves only files you have explicitly opened, and nothing is exposed off the machine.
