Skip to content

Commit 00f7b7b

Browse files
integrate forge/: compose + serve entries, probe, docs to 35 plugins / 456 tests
The demo server now seeds itself into its own forge — push this repo to /forge/<you>/plugins.git and browse the forge's source in the forge. NEXT records the tier-2 plan (issues as pod resources) and its seam asks (api.authorize collaborators, api.events webhooks).
1 parent 35bb6a6 commit 00f7b7b

6 files changed

Lines changed: 29 additions & 6 deletions

File tree

AGENT.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ Full ranking in `NOTES.md`. The ones you'll hit:
161161
| an object-storage / S3-style gateway | `s3/` | loopback LDP + hand-rolled XML + SigV4 |
162162
| a DID / identity document | `didweb/` | podsRoot scan + key derivation |
163163
| a dev/tooling subsystem | `gitscratch/` | shell a system binary via CGI |
164+
| a hosted-content web app (forge, wiki, …) | `forge/` | CGI plumbing + server-rendered GitHub-light UI + JSON API, zero deps/build |
164165
| a posting protocol (IndieWeb-style, client-discovered endpoint) | `micropub/` | pod bearer as the protocol token + loopback writes |
165166
| a data-export / archive download | `backup/` | loopback container walk streamed into a hand-rolled format |
166167
| an ops/observability endpoint | `metrics/` | node builtins + an `api.fastify` hook (scope: all plugins, never core) |
@@ -195,8 +196,8 @@ Full ranking in `NOTES.md`. The ones you'll hit:
195196
196197
## Current state
197198
198-
34 plugins (7 ports + 27 features) + the two-server `federation-demo/`,
199-
407 tests, all green (`npm test`).
199+
35 plugins (7 ports + 28 features) + the two-server `federation-demo/`,
200+
456 tests, all green (`npm test`).
200201
A four-axis security review ([SECURITY.md](./SECURITY.md)) hardened the
201202
inbound-federation and query surfaces; the WAC-deferral pattern held.
202203
`compose.test.js` runs every plugin on a single server from pure config.

ISSUES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ evidence. Legend:
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 |
4747
| #583-adj | `gallery/` | pod photo/media gallery — first `api.mountApp` consumer; zero required config |
48+
| #322-next | `forge/` | personal git forge — persistent push-to-create hosting, GitHub-light UI, JSON API |
4849

4950
Plus seven **ports of bundled features** proving the migration path for
5051
#564 / #164: `relay/` `webrtc/` `terminal/` `tunnel/` `notifications/`
@@ -118,7 +119,7 @@ shipped upstream** (the loader plus four seams and the id fix in JSS
118119
0.0.218/0.0.219, each consumed here), **2 more plugin-able with no
119120
blocker**, **4 clusters blocked on a named seam** (each with a
120121
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+
**35 plugins total, 456 tests** (plus the two-server `federation-demo/`
122123
scenario). The plugin api reaches most of the backlog today; ranked by
123124
demand, the seams that would unlock the most next are `api.authorize`
124125
(4 consumers, 3 hard-blocked) and `api.events` (7 consumers — matrix

NEXT.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ plugin), `NOTES.md` (the findings/seams — the real deliverable), and
88

99
## Where things stand
1010

11-
- **34 plugins, 407 tests, all green** (`npm test`; the three
11+
- **35 plugins, 456 tests, all green** (`npm test`; the three
1212
`notifications/` fs.watch tests need a free inotify instance — see
1313
Footguns), all pushed to
1414
`github.com/JavaScriptSolidServer/plugins` (branch `gh-pages`).
@@ -180,6 +180,12 @@ Still genuinely plugin-shaped and distinct:
180180
(capability + shortlink composition), a music/podcast pod (gallery's
181181
Range finding makes byte-range playback free), a turn-based game over
182182
relay + pod state. Demos compose existing plugins; loopback only.
183+
- **forge/ tier 2** (tier 1 SHIPPED 2026-07-12: push-to-create hosting,
184+
GitHub-light UI, JSON API — see forge/README Findings). Tier 2 =
185+
issues + comments **as pod resources** (WAC-governed, portable — the
186+
thing Gitea structurally can't offer), repo settings, profile pages.
187+
Tier 3 = PRs/forks (git CLI can merge; the UI is the work). Wants:
188+
api.authorize for collaborators, api.events for webhooks.
183189
- **Phanpy Wave B — real federation** (Wave A is DONE, 2026-07-12: the
184190
mastodon/ shim is Phanpy-grade for local use — timelines, threads,
185191
media, notifications from the AP inbox, PKCE; serve a Phanpy dist at

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,15 @@ README and NOTES.md, ship the closest honest approximation.
9292
| `shortlink/` || link shortener for pod URLs (deliberately not an open redirector) |
9393
| `admin/` || **the capstone**: wp-admin-style operator home; each missing pillar names its seam |
9494
| `gallery/` || pod photo/media gallery — first `api.mountApp` (#583) consumer; zero required config |
95+
| `forge/` | #322-next | personal git forge — push-to-create hosting, GitHub-light UI, JSON API; zero deps, zero build |
9596

9697
Plus one directory that is a scenario, not a plugin:
9798
[`federation-demo/`](./federation-demo/)**two** JSS instances federating
9899
over loopback (webfinger discovery, a cross-origin Follow, plugin-signed
99100
delivery); run `node federation-demo/demo.js` for the narrated version.
100101

101-
**407 tests, all green** (`npm test`), including `compose.test.js` — all
102-
thirty-four plugins on one server from pure config, pods + WAC intact
102+
**456 tests, all green** (`npm test`), including `compose.test.js` — all
103+
thirty-five plugins on one server from pure config, pods + WAC intact
103104
beside them. Findings consolidated in [NOTES.md](./NOTES.md); the full
104105
plugin-tagged backlog triaged in [ISSUES.md](./ISSUES.md); the build guide
105106
is [AGENT.md](./AGENT.md); the maintainer-facing summary — what to add

compose.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ describe('composition: every plugin on one server', () => {
9696
// Zero config on purpose — gallery/ is the first plugin needing none
9797
// (origin via api.serverInfo, container defaults to the caller's pod).
9898
{ module: at('gallery/plugin.js'), prefix: '/gallery' },
99+
{ module: at('forge/plugin.js'), prefix: '/forge' },
99100
{
100101
module: at('remotestorage/plugin.js'),
101102
prefix: '/remotestorage',
@@ -350,6 +351,17 @@ describe('composition: every plugin on one server', () => {
350351
assert.strictEqual(res.status, 400);
351352
});
352353

354+
it('forge: the repo list answers; an anonymous push is refused', async () => {
355+
const page = await fetch(`${base}/forge/`);
356+
assert.strictEqual(page.status, 200);
357+
assert.match(page.headers.get('content-type') || '', /text\/html/);
358+
const api = await fetch(`${base}/forge/api/repos`);
359+
assert.strictEqual(api.status, 200);
360+
assert.ok(Array.isArray((await api.json()).repos));
361+
const push = await fetch(`${base}/forge/nobody/x.git/info/refs?service=git-receive-pack`);
362+
assert.strictEqual(push.status, 401);
363+
});
364+
353365
it('gallery: the page answers anonymously; an anonymous upload is refused', async () => {
354366
const page = await fetch(`${base}/gallery`);
355367
assert.strictEqual(page.status, 200);

serve.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ const fastify = createServer({
104104
// Zero config on purpose — gallery/ needs none (api.serverInfo origin,
105105
// container defaults to the caller's own pod).
106106
{ module: at('gallery/plugin.js'), prefix: '/gallery' },
107+
{ module: at('forge/plugin.js'), prefix: '/forge' },
107108
// webfinger/ above owns /.well-known/webfinger — the witnessed collision
108109
// (remotestorage/README.md) — so remotestorage stands down here.
109110
{ module: at('remotestorage/plugin.js'), prefix: '/remotestorage', config: { baseUrl: PUBLIC_URL, loopbackUrl: `http://localhost:${PORT}`, claimWellKnown: false } },
@@ -188,6 +189,7 @@ console.log(` backup: GET ${PUBLIC_URL}/backup/<pod>/ → .tar.gz of w
188189
console.log(` shortlink: POST ${PUBLIC_URL}/short (auth; local targets only)`);
189190
console.log(` oembed: GET ${PUBLIC_URL}/oembed?url=<pod-resource-url> (link unfurling)`);
190191
console.log(` gallery: ${PUBLIC_URL}/gallery (photo/media gallery; streaming upload)`);
192+
console.log(` forge: ${PUBLIC_URL}/forge/ (git hosting; push http://.../forge/<you>/<repo>.git)`);
191193
console.log(` jmap: GET ${PUBLIC_URL}/jmap/session (JMAP mail over the pod)`);
192194
console.log(` remotestorage: ${PUBLIC_URL}/remotestorage/<user>/<category>/… (rS clients)`);
193195
console.log(` metrics: GET ${PUBLIC_URL}/metrics/healthz | /metrics/metrics (Prometheus${process.env.METRICS_TOKEN ? ', token-guarded' : ''})`);

0 commit comments

Comments
 (0)