# Public files

Public downloads have been available since version 0.6.0. They are served only while the rental is `active` and unexpired. Until enable, and after an unlocked rental passes its lease, public GETs return 404. A locked rental stays downloadable past `expires_at`.

The public URL is `{public_url}/files/{directory}/{percent-encoded relative path}`. `directory` is the first 12 characters of the rental UUID. Each path segment is percent-encoded, including spaces (`%20`) and U+202F (`%E2%80%AF`). The stored filename stays exact, so an ASCII space does not match U+202F. That encoding has been the URL form since version 0.10.0. Use the `file_url` the upload returns.

`..` and absolute paths are rejected on upload. A nested upload such as `docs/readme.md` is stored at that relative path.

The rental root is a page:

- If `index.html` exists at the root, that file is served. A static site wins.
- Otherwise Geoffrey returns an HTML listing of the rental root. `_layout.html` is listed as a disabled name, and `GET …/_layout.html` is 404.

A `.md` file at the rental root is rendered as GitHub-flavored HTML. Raw HTML in the Markdown is passed through. Nested paths such as `docs/readme.md` stay the stored bytes.

Optional `_layout.html` at the rental root wraps the listing and root Markdown when the file contains `{{content}}`. Placeholders:

| Placeholder | Value |
|---|---|
| `{{content}}` | The rendered listing or Markdown body. |
| `{{created_at}}` | Creation time as an RFC3339 UTC string. Since version 0.8.0. |
| `{{expires_at}}` | Expiration time as an RFC3339 UTC string. Since version 0.8.0. |
| `{{age}}` | How long the rental has been active, in one unit, for example `3 hours`. Since version 0.8.0. |
| `{{root_link}}` | Public base URL. Since version 0.8.0. |

Values other than `{{content}}` are HTML-escaped. A missing value, or a timestamp that is not RFC3339 or `YYYY-MM-DD HH:MM:SS` UTC, becomes an empty string. Link any stylesheet from the layout file. There is no well-known CSS name. If the layout is missing or has no `{{content}}`, Geoffrey uses an unstyled HTML document. `index.html` is never wrapped, and placeholders inside the Markdown body stay unchanged.

The download counter (`http_requests_served`) increments only on public file GETs.

