Skip to content

Commit 2b49977

Browse files
integrate the 0.0.219 wave: gallery + federation-demo; docs true-up
compose/serve: gallery added (zero config — the point), the appPaths array and its comment deleted (all four shims self-reserve now), stale explicit-id comments replaced, gallery liveness probe added. Docs: 34 plugins / 407 tests; ISSUES' shipped-upstream table gains #601/#602/#583/#610/#596 (all consumed here); the reservePath and serverInfo entries in NOTES/REPORT/NEXT flip from 'missing seam' to 'landed & consumed', keeping the history and recording what consumption surfaced (bodyLimit sidestep in the mounted lane, matcher vs maxParamLength, the JRD-registry ask, streaming ends at core's front door); api.events is now the top open ask, then api.authorize.
1 parent aadcd1d commit 2b49977

8 files changed

Lines changed: 226 additions & 129 deletions

File tree

AGENT.md

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,31 @@ Full ranking in `NOTES.md`. The ones you'll hit:
8080
Workaround: loopback with the requester's own creds covers the common
8181
case; the issuer-authority case has no workaround — document it. (4
8282
consumers; the top blocking seam.)
83-
- **No `api.reservePath()`** — you can register routes outside your one
84-
`prefix`, but only `prefix` is WAC-exempt. `/.well-known/*` works by luck
85-
(core blanket-exempts it). Fixed roots like `/api`, `/xrpc`, `/ap` do
86-
**not** work until the operator passes `appPaths: ['/api',...]` to
87-
`createServer`. **If you build an API shim, your `test.js` must pass those
88-
`appPaths`, and your README must say the operator does too.** (4 shim
89-
consumers.)
83+
- **`api.reservePath()` — LANDED (JSS 0.0.219, #602) and consumed.** A
84+
plugin now claims + WAC-exempts its protocol-pinned paths itself:
85+
literal roots (`api.reservePath('/xrpc', { methods: [...] })` — see
86+
mastodon/bluesky/activitypub/matrix) and parameterized shapes
87+
(`api.reservePath('/:user/did.json')` — see didweb/). Reservations are
88+
READ-ONLY by default; widen `methods` to exactly the verbs your routes
89+
implement, never wider (an exempted verb with no route falls through to
90+
LDP's write wildcards unauthenticated). No more operator `appPaths`.
91+
`/.well-known/*` still also rides core's blanket exemption; reserving a
92+
well-known doc makes the claim deliberate and collision-guarded.
9093
- **No `api.events.onResourceChange`** — you can't react to pod writes, so a
9194
write-time index / cached feed is impossible; do read-time work (walk the
9295
container per request) and note the O(N) cost. See `sparql/`, `rss/`.
93-
- **No `api.serverInfo`** — a plugin can't learn its own origin at
94-
`activate` time. Take `config.baseUrl` (and `loopbackUrl`), and `throw` if
95-
missing. ~23 plugins do this.
96+
- **`api.serverInfo` — LANDED (JSS 0.0.218, #601), retrofit in progress.**
97+
Resolve the origin at REQUEST time (`api.serverInfo().baseUrl`; with an
98+
ephemeral port it only exists once listening), keep `config.baseUrl` as
99+
an optional reverse-proxy override. Consumed by webfinger/, didweb/,
100+
dashboard/, admin/, gallery/; ~18 plugins still carry the old
101+
config-pair pattern and retrofit the same way.
102+
- **`api.mountApp` — LANDED (JSS 0.0.219, #583).** A raw `(req,res)`
103+
handler with the UN-drained request stream — the streaming-upload lane
104+
Fastify's parsers can't give you. See gallery/ (the first consumer) for
105+
the contract's edges: the mounted lane sidesteps the host `bodyLimit`
106+
(cap your own if you don't forward to core), the two lanes can't share
107+
a prefix, and `getAgent` needs a shim for raw reqs.
96108
- **No `api.mcp.registerTool`** — MCP tools can't be added by a plugin
97109
(that's why #495/#496/#500/#501 aren't here).
98110
- **Can't set Fastify server options** — e.g. `maxParamLength` (100) 404s
@@ -140,7 +152,7 @@ Full ranking in `NOTES.md`. The ones you'll hit:
140152
|---|---|---|
141153
| a realtime/WebSocket service | `relay/` or `webrtc/` | `ws.route` + `pluginDir` |
142154
| a DAV-family protocol (CalDAV done, e.g. a filesystem/WebDAV variant) | `webdav/``carddav/``caldav/` | loopback + multistatus XML + ETag + Basic→Bearer, proven 3× |
143-
| an API shim (a new social/chat/proto) | `mastodon/` or `bluesky/` | token bridge + fixed-root `appPaths` |
155+
| an API shim (a new social/chat/proto) | `mastodon/` or `bluesky/` | token bridge + self-reserved fixed roots (#602) |
144156
| a `.well-known` discovery doc | `nip05/` or `webfinger/` | podsRoot scan + guarded absolute route |
145157
| a scoped-token / auth service | `capability/` or `otp/` | HMAC macaroon-lite + `pluginDir` |
146158
| a query/read/search over pod data | `sparql/`, `rss/`, `search/` | loopback container walk + forwarded auth |
@@ -158,6 +170,8 @@ Full ranking in `NOTES.md`. The ones you'll hit:
158170
| a storage protocol with conditional writes | `remotestorage/` | If-Match/If-None-Match pass through loopback intact |
159171
| a redirect / metadata micro-service | `shortlink/` | pluginDir JSON table + wildcard slug routes |
160172
| an operator/admin surface | `admin/` | compose probes + podsRoot scan behind an agent allowlist |
173+
| a streaming-body app (upload, media) | `gallery/` | `api.mountApp` raw handler → loopback PUT, auth forwarded |
174+
| a multi-server scenario (not a plugin) | `federation-demo/` | child-process instances (DATA_ROOT is process-global) |
161175
162176
## Footguns (every multi-boot suite rediscovered these)
163177
@@ -167,9 +181,11 @@ Full ranking in `NOTES.md`. The ones you'll hit:
167181
- **Ambient `~/.gitconfig`** (git-shelling plugins): spawn git with
168182
`GIT_CONFIG_NOSYSTEM=1` and no `HOME`, or the operator's `init.defaultBranch`
169183
leaks into created repos.
170-
- **Generic basename id**: the loader derives the id from the module
171-
basename; `<name>/plugin.js` all reduce to `plugin` and collide — always
172-
pass an explicit `id` in `compose.test.js`/`serve.js`.
184+
- **Generic basename id — FIXED in JSS 0.0.219 (#596)**: the loader now
185+
derives `<name>/plugin.js` → id `<name>` from the parent dir; explicit
186+
`id` fields are gone from `compose.test.js`/`serve.js`. Don't reintroduce
187+
them — but if a test hardcodes a pluginDir path, it's `.plugins/<name>/`
188+
now, not `.plugins/plugin/`.
173189
- **Dotted prefixes** (`/.foo`) fail the WS upgrade — core reserves dotted
174190
paths. Use a plain prefix for anything with a socket.
175191
- **`logger: false` kills plugin `onResponse` hooks**: core's access-log
@@ -179,10 +195,14 @@ Full ranking in `NOTES.md`. The ones you'll hit:
179195
180196
## Current state
181197
182-
33 plugins (7 ports + 26 features), 386 tests, all green (`npm test`).
198+
34 plugins (7 ports + 27 features) + the two-server `federation-demo/`,
199+
407 tests, all green (`npm test`).
183200
A four-axis security review ([SECURITY.md](./SECURITY.md)) hardened the
184201
inbound-federation and query surfaces; the WAC-deferral pattern held.
185-
`compose.test.js` runs every one on a single server from pure config. Two
186-
core PRs (#590 `api.mountApp`, #591 `/idp/refresh`) sit upstream, unmerged,
187-
for the maintainer's call. Everything else lives here, by design.
188-
[REPORT.md](./REPORT.md) is the maintainer-facing summary of it all.
202+
`compose.test.js` runs every plugin on a single server from pure config.
203+
Four seams have now LANDED upstream and are consumed here: `api.serverInfo`
204+
(#601), `api.reservePath` (#602), `api.mountApp` (#583), `api.plugins`
205+
(#610) — all in JSS 0.0.218/0.0.219, which this repo pins. Still open:
206+
`api.events` (#603) and `api.authorize` (#604). Everything else lives
207+
here, by design. [REPORT.md](./REPORT.md) is the maintainer-facing
208+
summary of it all.

ISSUES.md

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,28 @@ evidence. Legend:
2323
| #507 | `webdav/` | mount a pod in Finder/Nautilus/Windows |
2424
| #509 | `sparql/` | SPARQL SELECT + UPDATE over pod JSON-LD (write-index → 🔩 `api.events`) |
2525
| #322 | `gitscratch/` | ephemeral Solid-authed git remotes (git-http-backend CGI) |
26-
| #515 / #516 | `mastodon/` | Mastodon-API shim — a client can log into its own pod (needs `appPaths` widened → 🔩) |
26+
| #515 / #516 | `mastodon/` | Mastodon-API shim — a client can log into its own pod (self-reserves `/api`+`/oauth`#602 consumed) |
2727
| #505 | `otp/` | one-time-password session flow (account *recovery* → 🔩 core auth) |
2828
| #157 | `carddav/` | CardDAV contact sync (iOS/macOS/Thunderbird/DAVx5) |
29-
| #211 | `bluesky/` | AT-Protocol XRPC shim — a client logs into its own pod (needs `appPaths` widened → 🔩) |
29+
| #211 | `bluesky/` | AT-Protocol XRPC shim — a client logs into its own pod (self-reserves `/xrpc`#602 consumed) |
3030
| #157-sib | `caldav/` | CalDAV calendar sync — completes the DAV family |
3131
| #164 | `webfinger/` | `/.well-known/webfinger` — the WebFinger half of #164 |
32-
| #51/#164 | `activitypub/` | federate a pod as an AS2 actor (needs `appPaths` widened → 🔩) |
32+
| #51/#164 | `activitypub/` | federate a pod as an AS2 actor (self-reserves `/ap`#602 consumed) |
3333
|| `rss/` | any pod container as an Atom/RSS feed |
3434
|| `matrix/` | Matrix Client-Server API shim (chat) |
3535
|| `search/` | full-text search over pod resources |
3636
|| `didweb/` | `did:web` DID-document resolver |
3737
|| `s3/` | S3-compatible object-storage gateway |
38-
|| `micropub/` | IndieWeb Micropub endpoint (media upload → 🔩 raw-body stream #583) |
38+
|| `micropub/` | IndieWeb Micropub endpoint (media upload: #583 landed as api.mountApp — parser work remains, see gallery/) |
3939
|| `backup/` | pod → `.tar.gz` export (incremental backup → 🔩 `api.events`) |
4040
|| `metrics/` | `/healthz` + Prometheus exporter (core-pipeline metrics → 🔩 gated hooks) |
41-
|| `dashboard/` | plugin status page (sibling discovery → 🔩 app-registry #463/#464) |
41+
|| `dashboard/` | plugin status page (sibling discovery via api.plugins #610 — consumed) |
4242
|| `oembed/` | oEmbed provider (discovery injection → 🔩 header/content hooks) |
43-
|| `jmap/` | JMAP mail over the pod (push/delta → 🔩 `api.events`; blobs → 🔩 #583) |
43+
|| `jmap/` | JMAP mail over the pod (push/delta → 🔩 `api.events`; blobs: #583 landed as api.mountApp — retrofit pending) |
4444
| #163 | `remotestorage/` | remoteStorage server — 7th port; witnessed the webfinger collision |
4545
|| `shortlink/` | link shortener for pod URLs (pluginDir persistence, 11th witness) |
4646
| #463/#464-adj | `admin/` | operator home — the wp-admin gap analysis; every missing pillar names a seam |
47+
| #583-adj | `gallery/` | pod photo/media gallery — first `api.mountApp` consumer; zero required config |
4748

4849
Plus seven **ports of bundled features** proving the migration path for
4950
#564 / #164: `relay/` `webrtc/` `terminal/` `tunnel/` `notifications/`
@@ -58,6 +59,11 @@ Plus seven **ports of bundled features** proving the migration path for
5859
| #582 | `appPaths` WAC exemption, v0.0.213 |
5960
| #584 | `api.auth.getAgent`, v0.0.214 |
6061
| #588 | `api.ws.route` WebSocket routing, v0.0.215 |
62+
| #601 | `api.serverInfo` — the server's own origin, v0.0.218; consumed by webfinger/didweb/dashboard/admin/gallery |
63+
| #602 | `api.reservePath` — literal + parameterized, v0.0.218; consumed by the four shims + didweb |
64+
| #583 | `api.mountApp` — raw handler, un-drained stream, v0.0.218; consumed by gallery/ |
65+
| #610 | `api.plugins` — the loaded-plugin roster, v0.0.218; consumed by dashboard/ + admin/ |
66+
| #596 | generic-basename id derivation fix, v0.0.219 — the explicit-id workaround deleted repo-wide |
6167
| #564 | the core/plugin line — answered empirically in [NOTES.md](./NOTES.md) |
6268

6369
## Plugin-able, not yet built 🧩
@@ -76,10 +82,9 @@ Each names the seam and the consumer that proves it. Ranked in
7682
|---|---|
7783
| #509 (write-index), #501 | `api.events.onResourceChange` — react to pod writes |
7884
| #382 (per-pod ACL), #506 (pod grants) | `api.authorize(request, path, mode)` — ask the host's WAC |
79-
| #515/#516, #211 (both built, but need operator `appPaths`) | `api.reservePath()` — an API-shim owns fixed roots outside its prefix and can't self-exempt them from WAC; **two independent confirmations** |
8085
| #505 (recovery) | `api.auth.mintSession` / `api.identity.addAuthKey` — turn a proven channel into pod authority |
8186
| #495 #496 #500 #501 | `api.mcp.registerTool` — MCP has no plugin-tool seam; all four MCP issues want new tools a plugin can't add today |
82-
| #463 #464 | app-registry primitive — surfacing installed plugins as Solid resources; `dashboard/` is the live proof-of-need (it must be handed a hand-copied duplicate of the `createServer` list) |
87+
| #463 #464 | app-registry primitive — surfacing installed plugins as *Solid resources*; the in-process half landed as `api.plugins` (#610, consumed by dashboard/+admin/), the as-pod-resources half is still open |
8388

8489
## Core, not a plugin 🏛️ (a finding, not a gap)
8590

@@ -107,15 +112,18 @@ exists.
107112

108113
Of ~40 plugin-tagged issues: **14 built as plugins here** (plus `rss/`,
109114
`matrix/`, `search/`, `didweb/`, `s3/`, `micropub/`, `backup/`, `metrics/`,
110-
`dashboard/`, `oembed/`, `jmap/`, `shortlink/`, `admin/` — capability
111-
demonstrations with no single issue), **7 ported**, **5 shipped upstream**,
112-
**2 more plugin-able with no blocker**, **6 clusters blocked on a named
113-
seam** (each with a proof-of-need consumer), the rest core-by-nature or
114-
product-scale. **33 plugins total, 386 tests.** The plugin api reaches most
115-
of the backlog today; ranked by demand, the seams that would unlock the
116-
most next are `api.authorize` (4 consumers, 3 hard-blocked), `api.events` (7
117-
consumers — matrix `/sync` needs live push, jmap can't do push or delta
118-
sync, backup can only pull-on-demand),
119-
`api.reservePath` (API-shims + didweb's parameterized case + the
120-
**witnessed** webfinger collision), and
121-
`api.serverInfo` (broadest: ~23 plugins hand-roll their own origin).
115+
`dashboard/`, `oembed/`, `jmap/`, `shortlink/`, `admin/`, `gallery/`
116+
capability demonstrations with no single issue), **7 ported**, **10
117+
shipped upstream** (the loader plus four seams and the id fix in JSS
118+
0.0.218/0.0.219, each consumed here), **2 more plugin-able with no
119+
blocker**, **4 clusters blocked on a named seam** (each with a
120+
proof-of-need consumer), the rest core-by-nature or product-scale.
121+
**34 plugins total, 407 tests** (plus the two-server `federation-demo/`
122+
scenario). The plugin api reaches most of the backlog today; ranked by
123+
demand, the seams that would unlock the most next are `api.authorize`
124+
(4 consumers, 3 hard-blocked) and `api.events` (7 consumers — matrix
125+
`/sync` needs live push, jmap can't do push or delta sync, backup can
126+
only pull-on-demand). `api.reservePath` and `api.serverInfo` — the
127+
former #3 and #4 — are **landed and consumed**; what remains of them is
128+
the JRD/link registry for shared discovery documents and the ~18
129+
mechanical serverInfo retrofits.

0 commit comments

Comments
 (0)