Skip to content

Commit 1a83a44

Browse files
Integrate bluesky: 16 plugins, 151 tests
- compose.test.js + serve.js load all 16 (appPaths widened to /api,/oauth,/xrpc); 17-check compose. - README/ISSUES tables updated; tally now 10 built as plugins. - NOTES: the multi-prefix finding upgraded to TWO independent confirmations (mastodon + bluesky, separate shims) and renamed the seam api.reservePath() — bluesky needs only ONE fixed root and still can't self-exempt it, so the ask is 'declare owned paths independent of the mount prefix', now the #3 most-demanded seam after api.authorize and api.events.
1 parent aecb638 commit 1a83a44

5 files changed

Lines changed: 38 additions & 22 deletions

File tree

ISSUES.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ evidence. Legend:
2626
| #515 / #516 | `mastodon/` | Mastodon-API shim — a client can log into its own pod (needs `appPaths` widened → 🔩) |
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 → 🔩) |
2930

3031
Plus six **ports of bundled features** proving the migration path for #564 /
3132
#164: `relay/` `webrtc/` `terminal/` `tunnel/` `notifications/`, and `pay/`
@@ -45,7 +46,6 @@ Plus six **ports of bundled features** proving the migration path for #564 /
4546

4647
| Issue | Shape |
4748
|---|---|
48-
| #211 | Bluesky / AT-Protocol shim — same shape as `mastodon/`, different wire format |
4949
| #527 | Tunnel client mode — extends `tunnel/` to dial *out* to a relay |
5050
| #277 | MongoDB-backed relay — `relay/` with a Mongo store (needs the infra) |
5151

@@ -58,7 +58,7 @@ Each names the seam and the consumer that proves it. Ranked in
5858
|---|---|
5959
| #509 (write-index), #501 | `api.events.onResourceChange` — react to pod writes |
6060
| #382 (per-pod ACL), #506 (pod grants) | `api.authorize(request, path, mode)` — ask the host's WAC |
61-
| #515/#516 (fixed roots), #211 | `api.appPaths.add()` / multi-prefix — an API-shim owns roots outside one prefix and can't self-exempt them from WAC |
61+
| #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** |
6262
| #505 (recovery) | `api.auth.mintSession` / `api.identity.addAuthKey` — turn a proven channel into pod authority |
6363
| #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 |
6464
| #463 #464 | app-registry primitive — surfacing installed plugins as Solid resources |
@@ -87,8 +87,8 @@ exists.
8787

8888
## Tally
8989

90-
Of ~40 plugin-tagged issues: **9 built as plugins here**, **6 ported**, **5
91-
shipped upstream**, **3 more plugin-able with no blocker**, **6 clusters
90+
Of ~40 plugin-tagged issues: **10 built as plugins here**, **6 ported**, **5
91+
shipped upstream**, **2 more plugin-able with no blocker**, **6 clusters
9292
blocked on a named seam** (each with a proof-of-need consumer), the rest
9393
core-by-nature or product-scale. The plugin api reaches most of the backlog
9494
today; ranked by how many independent plugins demanded them, the seams that

NOTES.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,22 @@ ports reached for it without coordinating.
6464
`appPaths`). Consequences, in increasing severity:
6565
- nip05/webdav/carddav land on `/.well-known/*`, which core *happens* to
6666
blanket-exempt — so they work **by luck**, not by contract.
67-
- mastodon needs `/api` **and** `/oauth`, which core does **not** exempt,
68-
so **the plugin cannot serve its own surface** — every call 401s at the
69-
WAC hook until the operator hand-passes `appPaths: ['/api','/oauth']`.
70-
A plugin has no `api.appPaths.add()` to fix this itself.
67+
- **mastodon** needs `/api` **and** `/oauth`, and **bluesky** needs
68+
`/xrpc` — none of which core exempts, so **the plugin cannot serve its
69+
own surface**: every call 401s at the WAC hook until the operator
70+
hand-passes `appPaths: ['/api','/oauth','/xrpc']`. A plugin has no way
71+
to exempt a path it owns. **Two independent confirmations** (two
72+
protocol shims, built separately), and bluesky sharpens it: it needs
73+
only *one* extra root and still can't reach it, so the seam is not
74+
"more prefixes" but **"a plugin declares the paths it owns,
75+
independent of its mount prefix."**
7176
- none of it has conflict detection: a future core route at a
7277
plugin-claimed path throws `FST_ERR_DUPLICATED_ROUTE` at boot.
73-
The seam: entries declare the paths they own (`paths: ['/api','/oauth']`
74-
or a plural `prefix`), the loader exempts and claims them deliberately
75-
and reports collisions. This is the seam **API-shim plugins**
76-
(mastodon, and any future Bluesky/ActivityPub/gateway) structurally
77-
require.
78+
The seam: `api.reservePath('/xrpc')` (or `paths: [...]` in the entry) —
79+
the loader exempts *and* claims each deliberately and reports collisions.
80+
This is the seam **every API-shim plugin** (mastodon, bluesky, and any
81+
future ActivityPub/Matrix/gateway) structurally requires; it's the third
82+
most-demanded after `api.authorize` and `api.events`.
7883
6. **Can't set fastify server options** — consumer: capability/ hit
7984
`maxParamLength` (100) silently 404ing long tokens in named params;
8085
workaround is a wildcard route. A plugin has no way to raise per-route

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ README and NOTES.md, ship the closest honest approximation.
6464
| `otp/` | #505 | one-time-password session flow; account recovery needs core auth |
6565
| `carddav/` | #157 | contact sync (iOS/macOS/Thunderbird/DAVx5); the DAV bridge, generalized |
6666
| `mastodon/` | #515/#516 | Mastodon-API shim — a client logs into its own pod; needs `appPaths` widened |
67+
| `bluesky/` | #211 | AT-Protocol XRPC shim — same shape, `/xrpc` root; token bridge generalizes |
6768

68-
**~130 tests, all green** (`npm test`), including `compose.test.js` — all
69-
fifteen plugins on one server from pure config, pods + WAC intact beside
69+
**~150 tests, all green** (`npm test`), including `compose.test.js` — all
70+
sixteen plugins on one server from pure config, pods + WAC intact beside
7071
them. Findings consolidated in [NOTES.md](./NOTES.md); the full
7172
plugin-tagged backlog triaged in [ISSUES.md](./ISSUES.md).
7273

compose.test.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('composition: every plugin on one server', () => {
4646

4747
after(async () => { if (jss) await jss.close(); });
4848

49-
it('boots pods + idp + fifteen plugins from config', async () => {
49+
it('boots pods + idp + sixteen plugins from config', async () => {
5050
const port = await probePort();
5151
base = `http://127.0.0.1:${port}`;
5252
wsBase = `ws://127.0.0.1:${port}`;
@@ -55,9 +55,10 @@ describe('composition: every plugin on one server', () => {
5555
port,
5656
root,
5757
idp: true,
58-
// mastodon owns fixed roots outside its prefix; a plugin can't
59-
// self-exempt them (finding), so the operator widens appPaths.
60-
appPaths: ['/api', '/oauth'],
58+
// mastodon (/api,/oauth) and bluesky (/xrpc) own fixed roots outside
59+
// their prefix; a plugin can't self-exempt them (finding), so the
60+
// operator widens appPaths.
61+
appPaths: ['/api', '/oauth', '/xrpc'],
6162
// Explicit ids: the <name>/plugin.js convention makes every basename
6263
// reduce to 'plugin' — the loader's duplicate-id guard requires ids
6364
// here (finding: derive from the parent dir for generic basenames).
@@ -82,6 +83,7 @@ describe('composition: every plugin on one server', () => {
8283
{ id: 'otp', module: at('otp/plugin.js'), prefix: '/otp', config: {} },
8384
{ id: 'carddav', module: at('carddav/plugin.js'), prefix: '/carddav', config: { baseUrl: base, loopbackUrl: base } },
8485
{ id: 'mastodon', module: at('mastodon/plugin.js'), prefix: '/mastodon', config: { baseUrl: base, loopbackUrl: base } },
86+
{ id: 'bluesky', module: at('bluesky/plugin.js'), prefix: '/bluesky', config: { baseUrl: base, loopbackUrl: base } },
8587
],
8688
});
8789
assert.ok(jss.base);
@@ -215,6 +217,11 @@ describe('composition: every plugin on one server', () => {
215217
assert.ok('title' in (await res.json()));
216218
});
217219

220+
it('bluesky: the XRPC describeServer answers (fixed /xrpc root)', async () => {
221+
const res = await fetch(`${base}/xrpc/com.atproto.server.describeServer`);
222+
assert.strictEqual(res.status, 200);
223+
});
224+
218225
it('pods still work beside all of it (idp register + WAC)', async () => {
219226
let res = await fetch(`${base}/idp/register`, {
220227
method: 'POST',

serve.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ const fastify = createServer({
2828
root: PODS,
2929
idp: true,
3030
idpIssuer: PUBLIC_URL,
31-
// mastodon owns fixed roots (/api, /oauth) outside its prefix; a plugin
32-
// can't self-exempt them from WAC, so the operator widens appPaths.
33-
appPaths: ['/api', '/oauth'],
31+
// mastodon (/api,/oauth) and bluesky (/xrpc) own fixed roots outside
32+
// their prefix; a plugin can't self-exempt them from WAC, so the
33+
// operator widens appPaths.
34+
appPaths: ['/api', '/oauth', '/xrpc'],
3435
// Explicit ids — the <name>/plugin.js convention collides on basename.
3536
plugins: [
3637
{ id: 'relay', module: at('relay/plugin.js'), prefix: '/relay' },
@@ -53,6 +54,7 @@ const fastify = createServer({
5354
{ id: 'otp', module: at('otp/plugin.js'), prefix: '/otp', config: {} },
5455
{ id: 'carddav', module: at('carddav/plugin.js'), prefix: '/carddav', config: { baseUrl: PUBLIC_URL, loopbackUrl: `http://127.0.0.1:${PORT}` } },
5556
{ id: 'mastodon', module: at('mastodon/plugin.js'), prefix: '/mastodon', config: { baseUrl: PUBLIC_URL, loopbackUrl: `http://127.0.0.1:${PORT}` } },
57+
{ id: 'bluesky', module: at('bluesky/plugin.js'), prefix: '/bluesky', config: { baseUrl: PUBLIC_URL, loopbackUrl: `http://127.0.0.1:${PORT}` } },
5658
],
5759
});
5860

@@ -81,3 +83,4 @@ console.log(` sparql: POST ${PUBLIC_URL}/sparql (auth)`);
8183
console.log(` otp: POST ${PUBLIC_URL}/otp/request`);
8284
console.log(` carddav: ${PUBLIC_URL}/carddav/ (contact sync)`);
8385
console.log(` mastodon: GET ${PUBLIC_URL}/api/v1/instance (point a client here)`);
86+
console.log(` bluesky: GET ${PUBLIC_URL}/xrpc/com.atproto.server.describeServer`);

0 commit comments

Comments
 (0)