# Publications module · v0

> The Publications module lets anyone publish a text on Each. Every publication is versioned: an edit creates a new immutable version, older ones stay readable. Every version carries a signed proof of its content (SHA-256 hash).

Status: draft · updated 2026-10-30 · JSON: https://test.each.app/en/surface/publications/v0.json

## A publication
- **id**: Stable publication id (pub_...).
- **path**: Reading address: /p/{author}/{slug}.
- **author**: Author name, as the author gives it.
- **title**: Title.
- **version**: Version number (1, 2, 3...). Never rewritten.
- **format**: "markdown" or "html" (sanitized HTML: p, br, em, strong, h2, h3, blockquote, ul, ol, li, a).
- **content**: Exact content, as published. Each never corrects it.
- **themes**: Themes (list of words).
- **license**: License given by the author. Without one: "not specified — all rights reserved by the author".
- **content_sha256**: SHA-256 hash (hex) of the UTF-8 bytes of the content.
- **proof**: Signed each.publication proof for this version.

## The core (non-modifiable)
The Each core cannot be modified by a module. Every write (create a publication, add a version) requires either a passkey confirmation or a capability token carrying the publications:write scope. The core checks one or the other before writing, and signs the proof. Versions are append-only: no edit, no delete.

## Scopes
- `publications:read`: Read publications and their versions (public, no token).
- `publications:write`: Create a publication or add a version, on behalf of the person who created the token.

### Capability tokens
A capability token is created with your passkey (POST /v1/capabilities). It is shown once, Each keeps only its hash. It expires after 30 days. Send it in the Authorization: Bearer each_cap_... header.

## Signed proof for every version
Every version produces a signed proof (ES256, public key: /.well-known/each-keys), verifiable at /verifier/{id}. The proof says who signed it: the author's passkey, a capability token, or the Each team for a test publication.

Type: `each.publication` · fields: `v`, `type`, `id`, `issuer`, `environment`, `issued_at`, `publication.id`, `publication.path`, `publication.version`, `publication.title`, `publication.author`, `publication.format`, `publication.license`, `publication.content_sha256`, `publication.previous_sha256`, `signed_by`, `signed_by_author`, `note`, `statement`

## API
- `GET /v1/publications`
- `GET /v1/publications/{id}`
- `GET /v1/publications/{id}/versions`
- `GET /v1/publications/{id}/versions/{n}`
- `POST /v1/publications/options`
- `POST /v1/publications`
- `POST /v1/publications/{id}/versions`
- `POST /v1/capabilities/options`
- `POST /v1/capabilities`

- CORS: GETs on /v1/publications are open to every origin (Access-Control-Allow-Origin: *), so an external reader can display the texts.

## Example reader
A copyable example reader: examples/publication-reader/ (one file, one config, one README).

## Versions
Contract version: v0 = draft. A breaking change creates v1 at a new address; v0 stays readable. To propose a change: copy src/modules/publications/, edit contract.json, open a review.

## Open questions
- Who may publish on an author's behalf (relative, publisher), and how does the author take over a test publication?
- Default licenses offered (all rights reserved, open licenses) and how to show them.
- Withdrawal: a version stays readable forever; should there be a public withdrawal (hide without erasing the proof)?
- Translations: is a translation a version or a linked publication?
