Skip to content

feat(host-cloudflare): add a read-only ExecutorUnfurl entrypoint for link previews - #35

Merged
fforres merged 1 commit into
skywardfrom
feat/artifact-unfurl-entrypoint
Oct 20, 2026
Merged

fforres merged 1 commit into
skywardfrom
feat/artifact-unfurl-entrypoint

Conversation

@fforres

@fforres fforres commented Oct 20, 2026

Copy link
Copy Markdown

Internal services that unfurl artifact links (chat link previews) need a title and description for any artifact a link names, whoever owns it. Whether the person clicking can open the artifact is still decided when they open the link.

Today the only way in is the full internal door. A subject-less read there sees only org-tier rows, so a personal artifact's link gets no preview.

Change

  • ExecutorUnfurl entrypoint (apps/host-cloudflare/src/worker.ts):
    • It's a WorkerEntrypoint reached only through a services binding with entrypoint: "ExecutorUnfurl". It has no fetch and no route, and takes no identity.
    • Its single RPC, artifactSummary(id), returns { title, description, updatedAt } | null.
  • artifactSummaryFor (apps/host-cloudflare/src/unfurl.ts):
    • It rejects any id not matching ^art_[a-z0-9]{1,60}$ before touching storage.
    • It reads through one subject-less platform-view executor per isolate (makePlatformExecutor(organizationId): tenant reach, writes denied). That executor uses the same D1 handle and tenant as the other doors, and its layers are db, plugins and host config, with no MCP.
  • ExecutorAdmin.artifactSummary (SDK): findFirst on artifact selecting only title, description and updated_at. No source, bindings, compiled output, preview, owner or subject leaves storage.
  • openDb moves to db/handle.ts unchanged, so the door doesn't import the MCP stack.

The owner policy and both existing doors are unchanged.

Tests

  • packages/core/sdk/src/platform-view.test.ts: summaries for an org artifact and for another member's personal artifact; null for an unknown id and for another tenant's artifact; and save/rename refused through the platform view.
  • apps/host-cloudflare/src/unfurl.test.ts: artifactSummaryFor over a sqlite-backed D1 returns a personal artifact, returns null for unknown and other-tenant ids, and returns null for six malformed ids with zero D1 calls.
  • Mutation-checked:
    • building the executor without platformView fails the personal-artifact test;
    • dropping the id check fails the malformed-id test;
    • bounding the admin reach fails it and 5 other admin tests.

…link previews

Internal services that unfurl artifact links need a title and description
for any artifact a link names, whoever owns it. Access to the artifact
itself is still decided when the link is opened.

`ExecutorUnfurl.artifactSummary(id)` is a service-binding-only RPC door
with no route and no identity. It validates the id, then reads through a
subject-less platform-view executor (tenant reach, writes denied) over the
same D1 database and tenant as the other doors. The new
`admin.artifactSummary` selects only title, description and updated_at, so
no source, bindings, compiled output, preview, owner or subject leaves
storage.

`openDb` moves to `db/handle.ts` so the door does not load the MCP stack.
@fforres
fforres merged commit 728addd into skyward Oct 20, 2026
4 of 40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant