itx kernel: CapTarget — capabilities are a name plus a typed target#1433
Closed
jonastemplestein wants to merge 1 commit into
Closed
itx kernel: CapTarget — capabilities are a name plus a typed target#1433jonastemplestein wants to merge 1 commit into
jonastemplestein wants to merge 1 commit into
Conversation
Implements the first slice of itx-next.md §1/§2 (design of record:
src/itx/types.ts):
- protocol.ts: SerializableCapTarget (rpc | url) and WorkerRef (binding |
loopback | project-worker | durable-object | source). CapKind becomes
the target's type ("live" | "rpc" | "url"); legacy "worker"/"facet"
rows normalize on read. CapSource gains cacheKey (codeId kept as
deprecated alias) and exportType ("worker-entrypoint" |
"durable-object", replacing kind: "facet"). CapMeta is open metadata
with the `instructions` convention, lifted into CapDescription.
- registry.ts: borrowTarget is now the two-case switch — live table, or
resolve the stored target. Implemented refs: binding (env lookup via a
new host hook), loopback (entrypoint allowlist + attribution props),
source (existing loader/facet paths). url / durable-object /
project-worker refs fail with informative not-implemented errors at
define time. New target_json column (guarded ALTER); source_json kept
in sync for source targets so pre-CapTarget code can read rows written
by this version.
- Security: binding and loopback refs reach PLATFORM resources, so they
are gated on hardcoded allowlists (DIALABLE_BINDINGS = {AI},
DIALABLE_LOOPBACKS = {BindingCapability}) at define time (fail fast)
and dial time (authoritative). Config-driven lists are a follow-up.
- entrypoint.ts: BindingCapability — the thin policy wrapper for
platform bindings (itx-next §2): a path-call loopback that replays the
dotted path onto env[props.binding], with its own allowlist check
(props are definer-controlled). Gateway/quota policy slots in here.
- e2e: new test proving itx.ai.models() via a raw binding ref, the same
through the BindingCapability wrapper, allowlist refusals (DB binding,
ItxEntrypoint loopback), and describe() reporting new kinds +
instructions. Existing describe assertion updated ("worker" → "rpc").
Backward compatible: caps.define({ source, kind }) still works and
normalizes to an rpc/source target; existing stored rows keep working.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
e2e validated: ran the full |
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
…1436) Supersedes #1433, which GitHub auto-closed when its base branch (`medieval-fibre`, merged as #1428) was deleted. Same commit, now based on main. ## What First implementation slice of the CapTarget design from #1428 (design of record: `src/itx/types.ts`). **A capability is now a name plus a typed target.** `caps.define` accepts: ```ts // raw platform binding — members replay applies the path onto env.AI await itx.caps.define({ name: "ai", target: { type: "rpc", worker: { type: "binding", binding: "AI" } }, }); await itx.ai.models(); // the same binding through the thin policy wrapper (itx-next §2 pattern) await itx.caps.define({ invoke: "path-call", name: "aiWrapped", target: { type: "rpc", worker: { type: "loopback" }, entrypoint: "BindingCapability", props: { binding: "AI" } }, }); ``` - `registry.borrowTarget()` is the two-case switch the design promised: live table, or resolve the stored target. - Implemented worker refs: `binding`, `loopback`, `source` (the old worker/facet paths, unchanged underneath). `url` / `durable-object` / `project-worker` fail with informative not-implemented errors **at define time**. - `BindingCapability`: new loopback entrypoint, the thin policy wrapper for bindings — gateway/quota policy slots in there later. - `kind: "facet"` is gone from the API (legacy input still accepted): statefulness is `source.exportType: "worker-entrypoint" | "durable-object"`. `cacheKey` replaces `codeId` (deprecated alias kept). - `CapMeta` is open metadata with the `instructions` convention, lifted into `describe()` output. ## Security `binding` and `loopback` refs reach **platform** resources (an open list would let any project handle reach the deployment D1, or mint itx handles on arbitrary projects via ItxEntrypoint props). They are gated on hardcoded allowlists — `DIALABLE_BINDINGS = {AI}`, `DIALABLE_LOOPBACKS = {BindingCapability}` — checked at define time (fail fast) and again at dial time (authoritative). `BindingCapability` re-checks the binding allowlist itself since its props are definer-controlled. Config-driven lists are a follow-up. ## Compatibility - `caps.define({ source, kind: "worker" | "facet" })` still works — normalizes to an rpc/source target. - Stored rows from before this PR (kind worker/facet + source_json) normalize on read; new rows also write `source_json` for source targets so a rollback can still read them. New `target_json` column added via guarded ALTER. - `describe()` now reports `kind: "rpc"` for stored-source caps (e2e assertion updated). ## Testing - `pnpm typecheck` / `lint` / `format` / `test` (190 unit tests) all green. - **Full `pnpm e2e:itx` suite ran green against a live deployment** (19 tests, 5 files, via the dev_jonas tunnel serving this branch): `itx.ai.models()` through the real AI binding raw + wrapped, allowlist refusals, and all pre-existing egress/facet/fork/HTTP tests — no regressions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Introduces platform binding and loopback dialing gated only on small hardcoded allowlists; misconfiguration or future allowlist expansion could expose env bindings like D1 or arbitrary loopback exports to project handles. > > **Overview** > **Capabilities are now a name plus a typed `SerializableCapTarget`**, not legacy `source` + `kind: worker | facet`. `caps.define` accepts `target` (e.g. `rpc` with `binding`, `loopback`, or `source` worker refs); old `source`/`kind` input still normalizes to `rpc`/`source`. > > **Registry dispatch** is refactored to the two-case model: live in-memory connections, or `resolveTarget()` at invoke time for stored targets. SQLite gains `target_json` (with legacy row normalization via `targetOf()`); `describe()` reports `kind: "rpc"` and lifts `meta.instructions`. > > **Platform binding exposure** adds `BindingCapability` (loopback path-call wrapper), host `binding` resolvers on Project/Context DOs, and **allowlists** `DIALABLE_BINDINGS` / `DIALABLE_LOOPBACKS` enforced at define and dial time. > > **Protocol updates**: `CapKind` becomes `live | rpc | url`; `cacheKey` replaces `codeId`; `exportType` replaces facet kind; `url` / `project-worker` / `durable-object` refs fail at define time until implemented. > > **E2E** covers raw `AI` binding caps, wrapped `BindingCapability`, and allowlist rejections. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 13ab9e5. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- CLOUDFLARE_PREVIEW --> ## Environment Config Lease <!-- CLOUDFLARE_PREVIEW_STATE --> <!-- { "apps": { "os": { "appDisplayName": "OS", "appSlug": "os", "status": "deployed", "updatedAt": "2026-06-10T12:11:14.008Z", "headSha": "13ab9e5ccc39a1348d29b74fd55086747b727bd6", "message": null, "publicUrl": "https://os.iterate-preview-2.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27275038072", "shortSha": "13ab9e5" } }, "environmentConfigLease": { "dopplerConfig": "preview_2", "leasedUntil": 1781096892787, "leaseId": "f42f975c-7672-4369-bcb7-2db7a29b7c06", "slug": "preview-2", "type": "environment-config-lease" } } --> <!-- /CLOUDFLARE_PREVIEW_STATE --> Lease: `preview-2` Doppler config: `preview_2` Type: `environment-config-lease` Leased until: 2026-06-10T13:08:12.787Z ### OS Status: deployed Commit: `13ab9e5` Preview: https://os.iterate-preview-2.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27275038072) Updated: 2026-06-10T12:11:14.008Z <!-- /CLOUDFLARE_PREVIEW --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
First implementation slice of the CapTarget design from #1428 (stacked on that branch — design of record:
src/itx/types.ts).A capability is now a name plus a typed target.
caps.defineaccepts:registry.borrowTarget()is the two-case switch the design promised: live table, or resolve the stored target.binding,loopback,source(the old worker/facet paths, unchanged underneath).url/durable-object/project-workerfail with informative not-implemented errors at define time.BindingCapability: new loopback entrypoint, the thin policy wrapper for bindings — gateway/quota policy slots in there later.kind: "facet"is gone from the API (legacy input still accepted): statefulness issource.exportType: "worker-entrypoint" | "durable-object".cacheKeyreplacescodeId(deprecated alias kept).CapMetais open metadata with theinstructionsconvention, lifted intodescribe()output.Security
bindingandloopbackrefs reach platform resources (an open list would let any project handle reach the deployment D1, or mint itx handles on arbitrary projects via ItxEntrypoint props). They are gated on hardcoded allowlists —DIALABLE_BINDINGS = {AI},DIALABLE_LOOPBACKS = {BindingCapability}— checked at define time (fail fast) and again at dial time (authoritative).BindingCapabilityre-checks the binding allowlist itself since its props are definer-controlled. Config-driven lists are a follow-up.Compatibility
caps.define({ source, kind: "worker" | "facet" })still works — normalizes to an rpc/source target.source_jsonfor source targets so a rollback can still read them. Newtarget_jsoncolumn added via guarded ALTER.describe()now reportskind: "rpc"for stored-source caps (e2e assertion updated).Testing
pnpm typecheck/lint/format/test(190 unit tests) all green.pnpm e2e:itx): rawitx.ai.models(), the wrapped equivalent, allowlist refusals, and describe() shape. Not yet run against a deployment — needs this branch deployed to a dev/preview stage first.🤖 Generated with Claude Code
Note
High Risk
Changes capability registration and invoke resolution in the itx kernel, including new platform binding/loopback dial paths gated by hardcoded allowlists; misconfiguration or allowlist gaps could expose env bindings or break existing caps until legacy normalization is exercised.
Overview
Capabilities are now defined as a name plus a typed
SerializableCapTarget, not only legacysource+worker/facetkinds.caps.defineacceptstarget(e.g.rpcwithbinding,loopback, orsourceworker refs); oldsource/kindinput still normalizes torpc/source.The registry
borrowTarget/resolveTargetsplit replaces the old kind switch: live connections vs resolving stored targets at invoke time. SQLite gainstarget_json(with legacy read path viatargetOf);describe()reportskind: "rpc"and liftsmeta.instructions.Platform binding exposure adds
BindingCapability(loopback path-call wrapper), hostbindingresolvers on Project/Context DOs, andDIALABLE_BINDINGS/DIALABLE_LOOPBACKSenforced at define and dial time.CapSourcegainscacheKey(aliascodeId) andexportTypefor worker vs durable-object facets.E2e covers raw
AIbinding caps, wrappedBindingCapability, and allowlist rejections.Reviewed by Cursor Bugbot for commit bc7396e. Bugbot is set up for automated code reviews on this repo. Configure here.
Environment Config Lease
Lease:
preview-9Doppler config:
preview_9Type:
environment-config-leaseLeased until: 2026-06-10T12:53:19.666Z
OS
Status: deployed
Commit:
bc7396ePreview: https://os.iterate-preview-9.com
Workflow run
Updated: 2026-06-10T11:56:06.745Z