77 lines
4.8 KiB
Markdown
77 lines
4.8 KiB
Markdown
# API Endpoint Versioning Scope (Design in Progress)
|
|
|
|
Status: decision deferred. The `/api/` prefix has already been changed to `/api/v1/` for every
|
|
route under that prefix (inventory items, storage locations, search, friends, friend requests,
|
|
groups, group invites, id mapping, tags/properties/categories/availability policies/info,
|
|
files/item files/staged files, and account export/import/delete). Whether that blanket approach is
|
|
the right scope, or whether versioning should be narrower, is the open question below.
|
|
|
|
## Problem
|
|
|
|
A versioned URL prefix matters most where two independently-deployed servers need to agree on a
|
|
wire contract: federation. An endpoint that's only ever called by a server's own frontend against
|
|
its own backend is deployed in lockstep with that backend, so it has no independent version to
|
|
track. Applying a uniform prefix everywhere is simpler, but it also prefixes a large number of
|
|
routes that never cross a domain boundary, and skips one non-`/api/` route
|
|
(`/auth/user/<handle>/`, used to fetch a friend's profile/avatar) and the file/thumbnail serving
|
|
routes (`/media/...`), both of which do cross a domain boundary and arguably belong in the
|
|
versioned surface more than some of the routes currently in it.
|
|
|
|
One exception either way: the version/capability discovery route (`GET /api/version/`) has to stay
|
|
unprefixed no matter which option is chosen — it's how a caller finds out which versions a server
|
|
speaks in the first place, so it can't itself live behind a version segment.
|
|
|
|
## Options
|
|
|
|
**Option A: version every real endpoint uniformly.**
|
|
Every route under `/api/`, `/auth/`, `/admin/`, and `/media/` gets the `/api/v1/` prefix (except
|
|
`/api/version/`), regardless of whether it's ever called across a domain boundary. Simple to state
|
|
and apply; doesn't require maintaining a classification of which routes are federation-facing.
|
|
|
|
**Option B: version only routes that actually cross a domain boundary.**
|
|
Only routes a server's own frontend calls against a *different* server (directly, or that another
|
|
server calls against this one) get the `/api/v1/` prefix. Everything else keeps its existing,
|
|
unprefixed path. Smaller versioned surface, but requires keeping the classification below current
|
|
as routes change, and means a single URL path can't ever serve both a same-domain and
|
|
cross-domain purpose without the whole path being versioned.
|
|
|
|
## Current classification
|
|
|
|
Verified against actual call sites (which routes are dispatched against a friend's/foreign
|
|
server rather than the caller's own home server), not just against which routes are technically
|
|
reachable by a non-local caller — several routes accept a broader signature-based authentication
|
|
than they actually need, and one route family enforces "local caller only" itself even though its
|
|
authentication layer would allow a remote identity through.
|
|
|
|
**Crosses a domain boundary today:**
|
|
- `POST /friendrequests/` (also called against the recipient's own server, not just the sender's)
|
|
- `GET/POST /groupinvites/` (invite delivery is posted to the invitee's own server)
|
|
- `POST /group_invites/accept/` (posted to the group's own home server)
|
|
- `GET /groups/<handle>/`, `DELETE /groups/<handle>/members/<id>/`, `POST /groups/<handle>/invites/`
|
|
- `* /inventory_items/<handle>/...`, `* /storage_locations/<handle>/...` (group-owned or
|
|
friend-owned items/locations)
|
|
- `GET /search/` (fans out to every known server)
|
|
- `GET /resolve_short_id/<kind>/<owner_id>/<local_id>/`
|
|
- `GET /auth/user/<handle>/` (friend profile/avatar lookup — not currently under `/api/`)
|
|
- `GET /media/...`, `GET /thumbnail/<size>/...` (image bytes fetched from the owning server —
|
|
not currently under `/api/`)
|
|
|
|
**Never observed leaving the caller's own home server:**
|
|
- `GET/POST /friends/`, `DELETE /friends/<id>/`, `DELETE /friendrequests/<id>/`
|
|
- `GET /groups/` (the collection route, as opposed to `/groups/<handle>/`)
|
|
- `DELETE /groupinvites/<id>/`, `POST /groupinvites/<id>/accept/`, `GET /groupmemberships/`
|
|
- `GET /idmap/` (distinct from `/resolve_short_id/...`, which does cross domains)
|
|
- `* /workflows/...`
|
|
- `GET /tags/`, `GET /properties/`, `GET /categories/`, `GET /availability_policies/`, `GET /info/`,
|
|
`GET /domains/` (also not currently called from the frontend at all)
|
|
- `POST /import/`, `GET /export/`, `DELETE /account_data/`, `DELETE /account/` — these accept the
|
|
same broad authentication as the federation-facing routes, but reject any non-local caller
|
|
themselves
|
|
- `GET /files/`, `* /item_files/...`, `* /staged_files/...`
|
|
- `GET/PATCH /auth/user/`, `POST /auth/register/`, `POST /auth/token/`, `GET /auth/preferences/`,
|
|
`GET/PUT /auth/self/preferences/`, `DELETE /auth/self/preferences/<key>/`, `* /auth/users/...`
|
|
(admin management)
|
|
- everything under `/admin/` (domain/category/property/tag administration)
|
|
|
|
**Exception regardless of which option is chosen:**
|
|
- `GET /api/version/`
|