---
{
  "n": 55,
  "title": "Share a document through a tunler link",
  "abstract": "",
  "refs": [],
  "seen": [
    "agent",
    "user"
  ],
  "data": {
    "environment": "main",
    "revisions": 1,
    "open_until": 1790272463.520709,
    "buttons": [
      {
        "label": "Accept this proposal",
        "say": "I accept the sharing proposal"
      },
      {
        "label": "Change it first",
        "say": "I want changes to the sharing proposal first"
      }
    ],
    "pressed": [
      "Accept this proposal",
      "Change it first"
    ]
  },
  "created": 1790270626.1111329,
  "updated": 1790772632.050285,
  "deleted": 0.0,
  "completed": 0.0,
  "outcome": "",
  "type": "doc"
}
---
Proposal: share one document or collection with someone outside the journal through tunler, with a unique link that opens that item and nothing else.

## What it does
You press **Share** on a document or a collection, and the journal gives you one link, such as `https://share.tunler.example.com/s/3f6c1e0a-9b2d-4c47-8a51-2d0e7c9f4b18`. Whoever opens it sees that document, or every item in that collection, read-only and nicely laid out. They see nothing else of the journal: no chat, no to-dos, no other documents, no settings.

Sharing is separate from the shared journal. Nobody signs in, nobody writes, and nothing is synchronised. It is a window onto the items you picked, open until you close it or it expires.

## How a share works
1. You press **Share** on a document or a collection (or ask the agent: *share document 54 with Anna*).
2. A dialog shows exactly what the visitor will be able to open (see *What a visitor can see*), lets you set an expiry and, optionally, a password, and creates the share.
3. The journal makes a share row with a fresh random ID (a UUID v4) and makes sure the share server and its tunnel are running.
4. The dialog shows the link with a **Copy** button. The share also appears on the document or collection itself, with how often it was opened and when it ends.
5. **Stop sharing** ends it at once: the link answers *This link has ended* from that moment. When no share is left open, the journal closes the tunnel too.

## What a visitor can see
A share has a **scope**: the exact set of items the link opens.

- **A document:** that document, its sections, and the files attached to it.
- **A collection:** the collection's page with its cards, and every member in it, each with its sections and attached files. A member added to the collection later becomes visible; a member removed stops being visible.

Before you create the share, the dialog lists the scope in plain words: *Anna will be able to view: Sharing proposal (doc 54) and its 2 attached files.* For a collection, it lists every member.

Everything outside the scope does not exist for the visitor. The share server looks up the share by its ID, works out the scope, and answers only for items inside it. Any other address gets the same *not found* page as a wrong ID, so a visitor cannot even tell whether something exists.

## Links and buttons outside the share
Journal text is full of links: *to-do 1612*, *message 9671*, file paths, and buttons that open inspectors. On a shared page:

- A reference to an item **inside** the scope stays a link and opens that item within the share (for example, one document in a shared collection linking to another).
- A reference to anything **outside** the scope is shown as plain words, with no link, no chip and no hover.
- Buttons that would open an inspector, a panel, a command or any journal action are left out entirely. A shared page has no *Comments*, *Delete*, *Add to collection*, *Accept this proposal* or similar.

This is decided on the server, when the page is rendered, not hidden in the browser. The HTML the visitor gets never contains an address outside the scope, so nothing can be reached by inspecting the page or typing an address.

## The share server
The viewer's own server must never be tunnelled: it answers the whole API, and anyone reaching it could read and change the journal. Sharing therefore runs a **second, small server** of its own:

- It listens only on `127.0.0.1`, on a port of its own.
- It answers `GET /s/<id>`, `GET /s/<id>/<item>` and `GET /s/<id>/files/<name>`, and nothing else: no `/api`, no viewer, no POST.
- It renders read-only HTML pages from the journal's rows through the same formatters as the viewer, with the scope rules above applied.
- It holds no state of its own: every request looks the share up again, so stopping a share or reaching its expiry takes effect immediately.

It runs as a service of the journal, like a plugin's servers: started when the first share opens, stopped when the last one ends.

## Tunler
Tunler gives a local port a public HTTPS address on your own domain. The journal uses **one tunnel per project**, and every share is a path on it:

- **One address, many shares.** The project's tunnel gets a subdomain made from the project name plus a short random part, such as `agent-journal-7k2q.tunler.example.com`, so two projects never collide. It is chosen once and kept, so links stay valid across restarts. Each share is a path on that address: `https://agent-journal-7k2q.tunler.example.com/s/<id>`. Creating a share never starts a new tunnel; it adds a path the share server now answers.
- **Setting up (once):** you log tunler in yourself (`tunler login you@example.com`). The journal checks `tunler status --json` and says plainly if tunler is missing or not logged in.
- **Opening:** when the first share opens and the tunnel is down, the journal runs `tunler <share port> --domain=<project subdomain> -d`, a background tunnel.
- **Closing:** when the last share ends, the journal runs `tunler disconnect <project subdomain>`. The subdomain stays yours, so the next share reuses it.
- **Password (optional):** checked by the share server itself, so each share can have its own password on the same tunnel.

Tunler logs no traffic on its server, keeps its certificates per subdomain, and needs nothing new to make this work.

## Safety
- **The ID is the key.** A UUID v4 has 122 random bits; it cannot be guessed. Nothing lists shares publicly.
- **Read-only.** The share server has no write routes at all.
- **Nothing outside the scope,** enforced by the server, as described above.
- **Ends when you say.** Stopping a share, or its expiry, ends it immediately. Expiry defaults to 7 days, and *Never* is available.
- **Visible.** Every open share shows on its item and on a *Shares* list, with its link, its scope, how often it was opened and when it ends.
- **Agent rules.** The agent can create a share only when you ask for one, and the chat always shows the link it made.

## Commands and viewer
- `journal share create <ref> [--expires 7d] [--password <word>]` creates a share of a document or collection and prints its link and scope.
- `journal share all` lists the open shares; `journal share stop <n>` ends one.
- **Share button:** documents and collections get a **Share** button that opens the share dialog: what the visitor can open, expiry, password, the link and a **Copy** button. Open shares also show on the item itself.
- **Tunnel icon in the top bar:** while the tunnel is up, an icon sits in the top bar next to notifications and search. Clicking it opens a dropdown with the tunnel's address and state, every open share (its item, link, views and end date, each with *Copy* and *Stop sharing*), and the tunnel's settings: expiry default, and **Stop the tunnel**, which ends every share at once.
- **Chat:** creating or stopping a share shows a mark in the chat with the link.

## Decisions for you
1. **Live or frozen.** Should a shared document show its latest text, or stay as it was when shared? I'd pick **live**, with a *Share this revision* option on the revisions row for a frozen copy.
2. **Default expiry.** I'd pick **7 days**, with 1 day, 30 days and never as choices.
3. **Visitor comments.** Later, visitors could leave comments that come back as messages. I'd leave that out of the first version.

Settled by you (message 9683): one tunnel per project on a unique, kept subdomain, with every share a path on it, and a top-bar icon with a dropdown while the tunnel is up.

## Build order
1. The share row, its scope, and `journal share create / all / stop`.
2. The share server: read-only rendering of documents and collections with the scope rules, and the *link ended* page.
3. Driving tunler: status check, open and close the tunnel, the Settings subdomain.
4. The viewer: the Share button and dialog, the open shares on the item, the Shares list.
5. Optional passwords, and frozen revisions if you want them.
