feat: editor remote read + compose/edit — closes #62 (part 3) #71

Merged
claude-bot merged 2 commits from feat/v7-editor-remote-edit into main 2026-06-30 14:11:23 +00:00
Contributor

Final part of #62 — completes the editor side and the v7 round-trip.

What it does

The editor's instance now acts as a client of the host project:

  • GET /api/remote-projects/:id/entries — signed-fetches the host's GET /ap/projects/:id (part 1) and returns its entries with their AP ids.
  • POST .../entries — delivers a signed Create (Note, context = the host project URL) to the host owner's inbox.
  • PUT .../entries — delivers a signed Update targeting an entry's AP id.
  • RemoteProjectDetailPage — read the host's entries, compose new ones, edit existing ones; "Shared with me" items link into it.

End-to-end: invited editor → reads host entries → creates/edits → host applies non-conflicting writes automatically and queues conflicts for the owner (#11). Create and update across instances — the milestone's "done when."

🔒 SSRF guard (flagged by the commit security review)

host_project_url / host_actor_url come from a remote actor's Invite, and isSafeUrl only checks the scheme — a crafted Invite could point the server's fetch at an internal address. So every egress (the collection fetch, the owner-actor fetch, and the inbox it advertises) is now restricted to origins that are enabled federated_instances — an admin-curated allowlist, symmetric with #9 (the host requires the editor's instance to be federated). Non-federated host → 403.

Verification (dev server)

remoteEditorEndpoints.test.ts6 tests: unknown invitation → 404; federated-but-unreachable host → 502 (auth + SSRF guard passed, network attempted); empty create → 400; unresolvable inbox → 502; update missing entry_ap_id400; non-federated host → 403. api tsc clean; app build clean.

The cross-instance fetch/delivery itself runs over real HTTP, so the live round-trip is validated with two instances; the host side it targets is the same code the host-side tests exercise.

Closes #62. With #9/#10/#11/#12/#61 already merged, this completes the v7.0.0 milestone.

🤖 Generated with Claude Code

Final part of **#62** — completes the editor side and the v7 round-trip. ## What it does The editor's instance now acts as a *client* of the host project: - **`GET /api/remote-projects/:id/entries`** — signed-fetches the host's `GET /ap/projects/:id` (part 1) and returns its entries with their AP ids. - **`POST .../entries`** — delivers a signed `Create` (Note, `context` = the host project URL) to the host owner's inbox. - **`PUT .../entries`** — delivers a signed `Update` targeting an entry's AP id. - **`RemoteProjectDetailPage`** — read the host's entries, compose new ones, edit existing ones; "Shared with me" items link into it. End-to-end: invited editor → reads host entries → creates/edits → host applies non-conflicting writes automatically and queues conflicts for the owner (#11). **Create *and* update across instances** — the milestone's "done when." ## 🔒 SSRF guard (flagged by the commit security review) `host_project_url` / `host_actor_url` come from a remote actor's `Invite`, and `isSafeUrl` only checks the scheme — a crafted Invite could point the server's fetch at an internal address. So every egress (the collection fetch, the owner-actor fetch, **and the inbox it advertises**) is now restricted to origins that are **enabled `federated_instances`** — an admin-curated allowlist, symmetric with #9 (the host requires the editor's instance to be federated). Non-federated host → `403`. ## Verification (dev server) `remoteEditorEndpoints.test.ts` — **6 tests**: unknown invitation → `404`; federated-but-unreachable host → `502` (auth + SSRF guard passed, network attempted); empty create → `400`; unresolvable inbox → `502`; update missing `entry_ap_id` → `400`; **non-federated host → `403`**. api `tsc` clean; app build clean. The cross-instance fetch/delivery itself runs over real HTTP, so the live round-trip is validated with two instances; the host side it targets is the same code the host-side tests exercise. Closes #62. With #9/#10/#11/#12/#61 already merged, this completes the **v7.0.0** milestone. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(p62): editor remote read + compose/edit, closing the round-trip (#62)
All checks were successful
CI / App — typecheck, test & build (push) Successful in 2m12s
CI / API — typecheck & tests (push) Successful in 3m40s
b9e9d55e8b
The editor's instance acts as a client of the host project.

- GET /api/remote-projects/:id/entries: signed-fetch the host collection
  (GET /ap/projects/:id) and return its entries (with their AP ids).
- POST /api/remote-projects/:id/entries: deliver a signed Create (Note,
  context = the host project URL) to the host owner's inbox.
- PUT /api/remote-projects/:id/entries: deliver a signed Update targeting an
  entry's AP id.
- RemoteProjectDetailPage: read the host's entries, compose new ones, edit
  existing ones; "Shared with me" items link into it.

Cross-instance fetch/delivery is validated end-to-end with two live instances;
tests cover the auth + validation layers (a reachable host surfaces as 502).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(security): SSRF guard on editor remote endpoints (#62)
All checks were successful
CI / App — typecheck, test & build (push) Successful in 1m27s
CI / API — typecheck & tests (push) Successful in 2m33s
CI / App — typecheck, test & build (pull_request) Successful in 1m24s
CI / API — typecheck & tests (pull_request) Successful in 2m33s
980f332669
host_project_url / host_actor_url come from a remote actor's AP Invite, so a
crafted Invite could point the server's fetch/delivery at an internal address.
Restrict every egress (host collection fetch, owner-actor fetch, and the inbox
it advertises) to origins that are enabled federated_instances — an
admin-curated allowlist, symmetric with the host requiring the editor's
instance to be federated (#9). Adds a non-federated-host -> 403 test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot scheduled this pull request to auto merge when all checks succeed 2026-06-30 14:08:41 +00:00
claude-bot deleted branch feat/v7-editor-remote-edit 2026-06-30 14:11:23 +00:00
Sign in to join this conversation.
No reviewers
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
rbrooks/TeaLeaves!71
No description provided.