pageshelf · v1.0.0 · 2026-05-10 · sha256 d845bdbf99c4cc65

pageshelf v1.0.0B

Immutable. This exact content is served forever at /api/v1/blob/d845bdbf99c4cc65.

---
name: pageshelf
description: Use when publishing agent-generated HTML artifacts, long plans, research reports, PR explainers, or interactive review pages with the pageshelf CLI. Covers secure local/Tailscale serving, session handling, URL generation, and common pitfalls.
version: 1.0.0
author: Hermes Agent
license: MIT
metadata:
  hermes:
    tags: [html-artifacts, tailscale, cli, reports, agent-output, security]
    related_skills: [writing-plans, requesting-code-review]
---

# Pageshelf

## Overview

Pageshelf is a small Go CLI and secure static artifact server for agent-generated HTML. Use it when a response would be too long or visually dense for chat, Markdown, Telegram, or a PR comment. Instead of dumping a giant plan into the conversation, create an HTML artifact, put it into a pageshelf session, and return the generated URL.

The default workflow is intentionally low-friction for agents:

```bash
pageshelf put report.html
```

If no session is supplied, pageshelf creates one automatically and prints a tokenized `/a/<session>/index.html` URL. For sharing across the user's private network, run the server with Tailscale integration:

```bash
pageshelf serve --tailscale
```

Pageshelf is not a general public hosting platform. Treat it as a private artifact shelf for local or tailnet use.

## When to Use

Use pageshelf when:

- A plan, report, diff explanation, architecture review, or research synthesis is too long for chat.
- The user needs tables, SVG diagrams, color-coded sections, tabs, or interactive controls.
- Telegram or another messaging surface cannot render the information well.
- A coding/review agent needs to attach a browsable HTML explainer to a task or PR.
- You want the user to skim a visual artifact and keep the chat response short.

Do not use pageshelf when:

- The output is short enough to read directly in chat.
- The artifact contains secrets, private tokens, credentials, or raw sensitive data.
- The user needs a long-lived public website.
- You need server-side application behavior. Pageshelf serves static files only.

## Quick Start

### 1. Start the server locally

```bash
pageshelf serve
```

Default bind:

```text
127.0.0.1:8787
```

### 2. Publish one HTML file

```bash
pageshelf put index.html
```

Typical output:

```text
session: 20260510-0945-artifact-ifw1wa
url: http://127.0.0.1:8787/a/20260510-0945-artifact-ifw1wa/index.html?t=psr_...
```

### 3. Use Tailscale for private network sharing

Start the server on the detected Tailscale IP:

```bash
pageshelf serve --tailscale
```

Generate Tailscale URLs from `put`:

```bash
pageshelf put --tailscale index.html
```

Or later:

```bash
pageshelf url --tailscale 20260510-0945-artifact-ifw1wa
```

## Core Commands

### `serve`

```bash
pageshelf serve
pageshelf serve --tailscale
pageshelf serve --host 127.0.0.1 --port 8787
pageshelf serve --host 0.0.0.0 --unsafe-public-bind
```

Behavior:

- Defaults to `127.0.0.1:8787`.
- `--tailscale` binds to a detected `100.64.0.0/10` Tailscale address.
- Public wildcard binds such as `0.0.0.0` and `::` require `--unsafe-public-bind`.
- Server has read/write/header timeouts and graceful shutdown.

Prefer `--tailscale` over `0.0.0.0` for private sharing.

### `put`

Publish files into a session. If `--session` is omitted, pageshelf creates a new session.

```bash
pageshelf put index.html
pageshelf put report.md
pageshelf put --raw source.md
pageshelf put index.html assets/
pageshelf put -s pr-review index.html
pageshelf put -s pr-review ./artifact/
```

From stdin:

```bash
cat report.html | pageshelf put --stdin --name index.html
```

Inline content:

```bash
pageshelf put --content '<!doctype html><h1>Report</h1>' --name index.html
```

Machine-readable output:

```bash
pageshelf put --json index.html
```

Generate URL for a non-default server endpoint:

```bash
pageshelf put --host 100.88.12.34 --port 8787 index.html
pageshelf put --tailscale index.html
pageshelf put --base-url http://agent-box:8787 index.html
```

With `--tailscale`, pageshelf prefers the local MagicDNS name when available, e.g. `http://omarchy-1.tailaf73.ts.net:8787/...`, and falls back to the raw `100.x.y.z` Tailscale IP.

URL selection from `put`:

- A single file returns that file's URL, e.g. `pageshelf put foo.html` returns `/foo.html`.
- Markdown files render to HTML by default: `report.md` returns `/report.html`, `index.md` returns `/index.html`.
- Use `--raw` when you need to serve the Markdown file itself, e.g. `pageshelf put --raw report.md` returns `/report.md`.
- `--stdin --name index.html` and `--content ... --name index.html` return `/index.html`.
- `--stdin --name report.md` and `--content ... --name report.md` render Markdown unless `--raw` is supplied.
- Multi-file/directory uploads prefer `index.html` if present; otherwise they return the first added file.

Interactive HTML:

```bash
pageshelf put --interactive index.html
```

Only use `--interactive` when the artifact needs local JavaScript for sliders, tabs, copy buttons, animations, or custom editors.

### `session`

```bash
pageshelf session create rate-limiter
pageshelf session create rate-limiter --tag plan --tag backend --ttl 14d
pageshelf session create rate-limiter --expires-at 2026-06-01 --json
pageshelf session info rate-limiter
pageshelf session info rate-limiter --json
pageshelf session meta rate-limiter --tag pr-review --ttl 7d
pageshelf session meta rate-limiter --clear-tags --ttl 0
pageshelf session rm rate-limiter
pageshelf gc --dry-run
pageshelf gc
```

Most agent workflows do not need `session create`; `put` can auto-create sessions. New sessions get `created_at`, `updated_at`, and `expires_at` metadata automatically; default retention is 14 days. Add tags during creation with `--tag`, replace them later with `session meta --tag ...`, and remove expired sessions with `pageshelf gc`. Use `--ttl 0` only when the artifact should not expire automatically.

### `list`, `files`, and `url`

```bash
pageshelf list
pageshelf list --json

pageshelf files <session>
pageshelf files <session> --json

pageshelf url <session>
pageshelf url <session> diff.html
pageshelf url --tailscale <session>
pageshelf url --base-url http://agent-box:8787 <session>
```

Use `url` when you already added files and need to regenerate a share link.

## Agent Workflow Patterns

### Long planning output

1. Write a concise chat summary.
2. Generate `index.html` with the full plan, diagrams, code snippets, and risk sections.
3. Publish it:

```bash
pageshelf put --tailscale index.html
```

4. Return the URL and a 3-5 bullet summary in chat.

### PR explainer

Create a self-contained HTML file with:

- high-level summary
- annotated diff snippets
- severity-colored findings
- data/control-flow diagrams
- test checklist

Then publish:

```bash
pageshelf put --tailscale pr-review.html
```

If the filename is not `index.html`, either name it explicitly while writing or call:

```bash
pageshelf url --tailscale <session> pr-review.html
```

### Multi-file artifact

If HTML references local assets, put the whole directory:

```bash
pageshelf put --tailscale ./artifact/
```

Inside the HTML, use relative links:

```html
<img src="./assets/flow.svg" alt="Flow diagram">
<link rel="stylesheet" href="./style.css">
```

Pageshelf serves them under the same session route.

### Existing session append

When `put` creates a session, capture the returned session ID. Add follow-up files with `-s`:

```bash
pageshelf put --json index.html
pageshelf put -s 20260510-0945-artifact-ifw1wa data.json diagram.svg
```

Avoid relying on hidden current-session state. Pageshelf intentionally makes session choice explicit after the first auto-create.

## Security Model

Pageshelf is designed for private local/tailnet artifact serving, not public internet hosting.

Key protections:

- tokenized read URLs
- token hash stored in manifest
- local token file stored separately
- default localhost bind
- explicit Tailscale bind
- wildcard bind requires `--unsafe-public-bind`
- path traversal prevention
- symlink rejection on input
- restrictive CSP
- `Cache-Control: no-store`
- `X-Content-Type-Options: nosniff`
- `Referrer-Policy: no-referrer`
- `X-Frame-Options: DENY`

### Safe vs Interactive CSP

Default safe mode disables scripts:

```http
script-src 'none'
connect-src 'none'
```

Interactive mode permits inline JavaScript but still blocks network fetches:

```http
script-src 'self' 'unsafe-inline'
connect-src 'none'
```

Use `--interactive` only for artifacts that need browser-side behavior.

### Sensitive data rule

Do not publish secrets, credentials, raw tokens, private keys, production `.env` files, or confidential dumps. Tokenized Tailscale URLs are convenient, not a replacement for data classification.

## HTML Authoring Guidance

For agent-created pages:

- Make the HTML self-contained where practical.
- Prefer inline CSS for portability.
- Use SVG for architecture and sequence diagrams.
- Use relative asset paths for multi-file artifacts.
- Include a short executive summary at the top.
- Add a sticky table of contents for long reports.
- Include copy buttons only when `--interactive` will be used.
- Avoid external CDNs; CSP and tailnet usage make local assets more reliable.
- Do not embed third-party analytics, trackers, fonts, or remote scripts.

For Telegram handoff, keep the chat reply short:

```text
詳細HTML作った:
<url>

中身:
- architecture diagram
- security checklist
- implementation tasks
```

## Common Pitfalls

1. **Forgetting the server endpoint in generated URLs.**
   If the server runs with `--tailscale`, generate URLs with `pageshelf put --tailscale ...` or `pageshelf url --tailscale ...`.

2. **Expecting `/a/<session>/...` to work without a token.**
   Artifact URLs require `?t=psr_...`.

3. **Putting a directory with absolute asset links.**
   HTML should use relative paths like `./assets/diagram.svg`, not `/tmp/artifact/assets/diagram.svg`.

4. **Using `--interactive` casually.**
   It allows inline JavaScript. Use safe mode unless the page needs client-side behavior.

5. **Publishing secrets because the server is “only Tailscale.”**
   Tailnet access is still access. Redact or summarize sensitive content first.

6. **Binding to `0.0.0.0` out of habit.**
   Use `--tailscale` for private sharing. Only use `--unsafe-public-bind` when you understand the exposure.

7. **Losing the session ID from auto-create output.**
   Use `--json` when an agent needs to parse and reuse the session ID.

8. **Expecting pageshelf to host an app backend.**
   It serves static files. Interactive artifacts must run fully in the browser.

## Verification Checklist

After publishing an artifact:

- [ ] `pageshelf serve` or `pageshelf serve --tailscale` is running.
- [ ] The URL path uses `/a/<session>/<file>` and includes `?t=psr_...`.
- [ ] The artifact opens successfully in a browser.
- [ ] Wrong or missing token returns `401`.
- [ ] Safe artifacts do not require JavaScript.
- [ ] Interactive artifacts were published with `--interactive` intentionally.
- [ ] No secrets or raw credentials are present in the HTML or asset files.
- [ ] Multi-file assets load via relative paths.
- [ ] Chat response includes only the short summary plus URL.

## One-Shot Recipes

### Publish a long report to Tailscale

```bash
pageshelf serve --tailscale
pageshelf put --tailscale report.html
```

### Publish a generated HTML string from an agent

```bash
cat <<'HTML' | pageshelf put --stdin --name index.html --tailscale
<!doctype html>
<html>
<head><meta charset="utf-8"><title>Artifact</title></head>
<body><h1>Artifact</h1></body>
</html>
HTML
```

### Publish an interactive tuning UI

```bash
pageshelf put --interactive --tailscale index.html assets/
```

### Regenerate a link for a different server address

```bash
pageshelf url --base-url http://100.88.12.34:8787 <session> index.html
```

### Inspect and clean up

```bash
pageshelf list
pageshelf files <session>
pageshelf session rm <session>
```