The wiki should document the whole API surface: the machine endpoints and the plugin API #170

Closed
opened 2026-09-10 12:22:18 +00:00 by tiagoagueda · 1 comment
Owner

Decision

The wiki documents the whole of Postulo's API surface, core and plugin, and is the only place that does (#169).

Where it stands

I measured it against the code rather than by reading.

The REST API (/api/v1, from the API's own OpenAPI schema). The API names all 39 operations, three of them in shorthand, and points at /api/v1/openapi.json for the rest. Two details are missing:

  • the values reason takes when a listing is discarded (not_for_me, pay, location, closed, other);
  • what GET /me answers (name, owner, scopes, expires_at, last_used_at).

Corrected after filing: the first version of this issue said three operations were never mentioned. /me is there as a full URL in Using it, and discard and restore are in the Writing table as /shortlist · /discard (reason) · /restore. My first check matched paths too literally.

The endpoints for machines outside /api/v1: /healthz, /logs and /metrics. They appear only as rows in Configuration and notes in Troubleshooting. Nothing says what each returns, how it is guarded, or what a collector should expect.

The plugin surface (postulo.plugins.api, the promise from #126). None of it is in the wiki. It lives in the core repository's docs/PLUGINS.md, 1,152 lines. That guide never names 8 of the 37 promised names: the interfaces a plugin of each kind implements (ConnectedPlugin, FeaturePlugin, ImporterPlugin, OutboxPlugin, SourcePlugin, StorePlugin, SyncPlugin, TransportPlugin). It describes each kind in prose without saying what to write class Mine(...) against.

What doing it is

  • The API gains the discard reasons and what /me answers.
  • A page for the machine endpoints: what /healthz, /logs and /metrics answer, with which token, at what rate, and how each is switched on.
  • The plugin guide moves into the wiki as Writing a plugin, with a reference of every name on postulo.plugins.api and every kind of plugin with the interface it implements and its entry-point group. docs/PLUGINS.md becomes a pointer, because six plugin repositories and the metadata of every published plugin link to it. The plugin API's own error message, "not part of the plugin surface. See …", points at the wiki page.
  • A test that keeps it whole. The wiki is a sibling repository, so a core test can check it wherever the workspace has it beside the core, and skip where it does not. It fails when an operation, an endpoint or a promised name is missing from its page. That stops this recurring the way #169 did.

Worth being careful about

  • The plugin guide is long. Moving it as one page keeps every anchor people may have linked to; splitting it can come later.
  • The pointer is what keeps the move from breaking things. The six pyproject.toml links are in six repositories, and published wheels cannot be changed at all.
## Decision The wiki documents the whole of Postulo's API surface, core and plugin, and is the only place that does (#169). ## Where it stands I measured it against the code rather than by reading. **The REST API** (`/api/v1`, from the API's own OpenAPI schema). *The API* names **all 39 operations**, three of them in shorthand, and points at `/api/v1/openapi.json` for the rest. Two details are missing: - the values `reason` takes when a listing is discarded (`not_for_me`, `pay`, `location`, `closed`, `other`); - what `GET /me` answers (`name`, `owner`, `scopes`, `expires_at`, `last_used_at`). *Corrected after filing:* the first version of this issue said three operations were never mentioned. `/me` is there as a full URL in *Using it*, and discard and restore are in the *Writing* table as `/shortlist · /discard (reason) · /restore`. My first check matched paths too literally. **The endpoints for machines outside `/api/v1`**: `/healthz`, `/logs` and `/metrics`. They appear only as rows in *Configuration* and notes in *Troubleshooting*. Nothing says what each returns, how it is guarded, or what a collector should expect. **The plugin surface** (`postulo.plugins.api`, the promise from #126). **None of it is in the wiki.** It lives in the core repository's `docs/PLUGINS.md`, 1,152 lines. That guide never names **8 of the 37 promised names**: the interfaces a plugin of each kind implements (`ConnectedPlugin`, `FeaturePlugin`, `ImporterPlugin`, `OutboxPlugin`, `SourcePlugin`, `StorePlugin`, `SyncPlugin`, `TransportPlugin`). It describes each kind in prose without saying what to write `class Mine(...)` against. ## What doing it is - **The API** gains the discard reasons and what `/me` answers. - **A page for the machine endpoints**: what `/healthz`, `/logs` and `/metrics` answer, with which token, at what rate, and how each is switched on. - **The plugin guide moves into the wiki** as *Writing a plugin*, with a reference of every name on `postulo.plugins.api` and every kind of plugin with the interface it implements and its entry-point group. `docs/PLUGINS.md` becomes a pointer, because six plugin repositories and the metadata of every published plugin link to it. The plugin API's own error message, *"not part of the plugin surface. See …"*, points at the wiki page. - **A test that keeps it whole.** The wiki is a sibling repository, so a core test can check it wherever the workspace has it beside the core, and skip where it does not. It fails when an operation, an endpoint or a promised name is missing from its page. That stops this recurring the way #169 did. ## Worth being careful about - **The plugin guide is long.** Moving it as one page keeps every anchor people may have linked to; splitting it can come later. - **The pointer is what keeps the move from breaking things.** The six `pyproject.toml` links are in six repositories, and published wheels cannot be changed at all.
tiagoagueda added this to the 0.3.0 milestone 2026-09-10 12:22:18 +00:00
tiagoagueda changed title from The wiki should document the whole API surface: three REST operations, the machine endpoints, and the plugin API to The wiki should document the whole API surface: the machine endpoints and the plugin API 2026-09-10 12:23:46 +00:00
Author
Owner

Done, in both repositories: postulo.wiki@d95b27d and postulo@d1c8f8b0.

The wiki now has the whole surface

  • The API: the discard reasons and what /me answers, plus one list of all 39 calls with method, path and scope, generated from the API's own routers. That is how I found each scope: the OpenAPI schema does not carry it.
  • Health, metrics and logs (new): /healthz, /metrics and /logs. What each returns, how it is switched on and guarded, the 401/429/503 answers, the parameters of /logs, and every metric with its labels, plus a Prometheus scrape configuration.
  • Writing a plugin (new, moved from docs/PLUGINS.md): it opens with every kind of plugin, its entry-point group and the interface it satisfies, and carries a reference of all 37 names postulo.plugins.api promises, the eight interfaces the guide never named among them.
  • The sidebar has a Building on it group for the API and the plugin guide, and the three pages that linked to docs/PLUGINS.md link here. All four pages render publicly.

In the core

  • docs/PLUGINS.md is a pointer to the wiki page. Six plugins' pyproject.toml files, and every published plugin's metadata, link to it.
  • The plugin API's refusal, "not part of the plugin surface. See …", points at the wiki page; the README, TRANSLATING.md and the comments follow.
  • tests/test_wiki_surface.py holds the three pages to the code, both ways. A call, metric, promised name or entry-point group without its line fails, and so does a line for a call that no longer exists. It reads ../postulo.wiki and skips where the wiki is not checked out, as in CI. I showed it failing by removing one line from each page, then restored them.
  • Full suite: 5012 passed.

Found while writing it, and not fixed here

Two names plugins use are not on the surface. A notifier's send() is handed postulo.notifications.base.Notification, and the guide's own example imports it from there. The client's DestinationRefused is what paperless catches (postulo-paperless#1). Writing a plugin says so plainly rather than pretending otherwise. Adding them to postulo.plugins.api is a surface change worth its own issue.

Correction, made on the issue body too: I first said three REST operations were undocumented. They were all there, /me as a full URL and discard and restore in shorthand; my matcher was too literal.

Done, in both repositories: **`postulo.wiki@d95b27d`** and **`postulo@d1c8f8b0`**. ## The wiki now has the whole surface - **The API**: the discard reasons and what `/me` answers, plus **one list of all 39 calls with method, path and scope**, generated from the API's own routers. That is how I found each scope: the OpenAPI schema does not carry it. - **Health, metrics and logs** (new): `/healthz`, `/metrics` and `/logs`. What each returns, how it is switched on and guarded, the `401`/`429`/`503` answers, the parameters of `/logs`, and **every metric with its labels**, plus a Prometheus scrape configuration. - **Writing a plugin** (new, moved from `docs/PLUGINS.md`): it opens with every kind of plugin, its entry-point group and the interface it satisfies, and carries a reference of **all 37 names** `postulo.plugins.api` promises, the eight interfaces the guide never named among them. - The sidebar has a **Building on it** group for the API and the plugin guide, and the three pages that linked to `docs/PLUGINS.md` link here. All four pages render publicly. ## In the core - `docs/PLUGINS.md` is a pointer to the wiki page. Six plugins' `pyproject.toml` files, and every published plugin's metadata, link to it. - The plugin API's refusal, *"not part of the plugin surface. See …"*, points at the wiki page; the README, `TRANSLATING.md` and the comments follow. - **`tests/test_wiki_surface.py` holds the three pages to the code, both ways.** A call, metric, promised name or entry-point group without its line fails, and so does a line for a call that no longer exists. It reads `../postulo.wiki` and skips where the wiki is not checked out, as in CI. I showed it failing by removing one line from each page, then restored them. - Full suite: 5012 passed. ## Found while writing it, and not fixed here **Two names plugins use are not on the surface.** A notifier's `send()` is handed `postulo.notifications.base.Notification`, and the guide's own example imports it from there. The client's `DestinationRefused` is what paperless catches (postulo-paperless#1). *Writing a plugin* says so plainly rather than pretending otherwise. Adding them to `postulo.plugins.api` is a surface change worth its own issue. *Correction, made on the issue body too:* I first said three REST operations were undocumented. They were all there, `/me` as a full URL and discard and restore in shorthand; my matcher was too literal.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#170
No description provided.