Skip to content

itx: repos/workspace/worker become platform defaults; origin-carrying delegation; durable-object refs#1482

Merged
jonastemplestein merged 2 commits into
mainfrom
itx-defaults-migration
Jun 10, 2026
Merged

itx: repos/workspace/worker become platform defaults; origin-carrying delegation; durable-object refs#1482
jonastemplestein merged 2 commits into
mainfrom
itx-defaults-migration

Conversation

@jonastemplestein

@jonastemplestein jonastemplestein commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

The remaining itx roadmap on latest main, no backcompat (prd is redeployable): §8's endgame — the hardwired kernel becomes ordinary capability definitions — plus the last unimplemented WorkerRef kind.

repos / workspace / worker are platform defaults now

All three move into the platform:project code context (src/itx/code-contexts.ts): repos and workspace as ReposCapability/WorkspaceCapability loopbacks, worker through the ProjectWorker forwarder (path-call hop, members inner replay against the default export). Their handle getters are deleted, their names leave the reserved list (shadowable — the point), and callWorkerFunction on the Project DO dies (the forwarder path covers it). What remains hardwired is exactly the composition a cap definition cannot express: caps, streams (the access model and global-namespace gating live there), fetch (Law 5), fork, project, projects, describe.

Chain delegation carries the originating context

The enabling protocol change (allowed by no-backcompat): itxInvoke gains origin, set by the first delegating hop and preserved up the chain; the registry's dial-time context attribution prop uses it. This is what makes a context-SCOPED cap correct as an inherited definition — workspace resolves to itx:ctx_… for a forked child and itx for the project even though the definition lives two links up. WorkspaceCapability now derives its workspace id from the injected context (explicit workspaceId still wins; the agent DO's pre-clone wiring is unchanged and uses the same derivation).

New e2e: a child fork writes to its workspace; neither the project context nor a sibling fork can read the file.

durable-object WorkerRef refs ship, config-gated

{ type: "durable-object", binding, name } resolves via env[binding].getByName(name) with the normal members/path-call dispatch. The namespace allowlist (DIALABLE_DURABLE_OBJECTS) is empty by default: every platform namespace (PROJECT, STREAM, …) is keyed across all projects, so allowlisting one would let any project handle dial any other project's objects by name. Deployments opt in via APP_CONFIG_ITX.dialableDurableObjects; per-project name scoping is the documented open design before any namespace can join the defaults. e2e asserts the define-time refusal.

Behavioral changes

  • itx.worker.fetch no longer special-cases to project ingress — it replays fetch on the worker's default export like any other member. The project homepage is the ingress URL.
  • describe() on a fresh project now lists four inherited caps (ai, repos, workspace, worker) with owner: "platform:project".

Deferred, with reasons (in itx-next.md)

  • streams stays kernel: resolution checks access, which a cap definition cannot express.
  • REPL-consumes-types.ts (§5): the hand-maintained editor typings stay, comments updated.
  • UrlDial through the egress intercept tunnel: the capnweb fork (Use our capnweb fork with WebSocket-over-RPC support #1474) only helps capnweb hops; the UrlDial → Project DO hop is Workers jsrpc, which still cannot carry a WebSocket-bearing Response.

Verification

pnpm typecheck / lint / knip / format green; apps/os unit tests green (210); itx e2e (itx, fork incl. the new isolation test, http, subscribe — 21 tests) green against a local dev server.

🤖 Generated with Claude Code


Note

High Risk
Protocol and dispatch changes (origin, workspace scoping, removed callWorkerFunction) affect fork isolation and worker routing; empty-default DO allowlist is security-critical if misconfigured.

Overview
Continues §8 “cap #0 disappears”: repos, workspace, and worker move from hardwired Itx getters into the platform:project code context as ordinary loopback caps (ReposCapability, WorkspaceCapability, ProjectWorker). Those names leave the reserved list (shadowable); callWorkerFunction on the Project DO is removed in favor of the existing forwarder path.

Chain delegation now carries the originating context: itxInvoke accepts optional origin, set on the first delegating hop and forwarded up the chain; the registry uses it for dial-time context attribution so inherited caps stay context-scoped—notably workspace (itx vs itx:ctx_… per fork). WorkspaceCapability derives workspaceId from injected context when not explicit.

durable-object WorkerRefs are implemented behind an empty-by-default DIALABLE_DURABLE_OBJECTS allowlist, widened only via APP_CONFIG_ITX.dialableDurableObjects.

Security / behavior: itx.project blocks itx* registry methods (spoofing origin); itx.worker.fetch no longer routes to ingress—it replays the worker default export like any member. Docs, REPL typings, and e2e (workspace isolation, DO define refusal) updated.

Reviewed by Cursor Bugbot for commit e3e0f0a. Bugbot is set up for automated code reviews on this repo. Configure here.

Environment Config Lease

No active environment config lease.

OS

Status: released
Commit: e3e0f0a
Preview: https://os.iterate-preview-5.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T22:06:09.374Z

…atform defaults; durable-object refs ship

The §8 endgame plus the last WorkerRef kind, no backcompat:

- repos, workspace, and worker are ordinary platform:project definitions
  now (ReposCapability/WorkspaceCapability loopbacks; the ProjectWorker
  forwarder with a members inner replay). Their handle getters, reserved
  names, and callWorkerFunction are deleted; shadowing works because
  shadowing already works. Remaining kernel: caps, streams, fetch, fork,
  project, projects, describe — the parts that ARE access checks and
  narrowing.
- Chain delegation carries the ORIGINATING context (itxInvoke gains
  origin; the registry injects it as the context attribution prop), so
  the context-SCOPED workspace still resolves per caller: a forked child
  gets itx:ctx_…, the project gets itx, even though the definition lives
  two links up. e2e proves sibling forks cannot see each other's files.
- durable-object WorkerRef refs are implemented (getByName + the normal
  invoke modes) behind an EMPTY namespace allowlist: every platform
  namespace is keyed across all projects, so deployments must opt
  namespaces in via APP_CONFIG_ITX.dialableDurableObjects.
- Behavioral change: itx.worker.fetch no longer special-cases to project
  ingress — it replays fetch on the worker's default export like any
  member.
- Deferred with reasons (itx-next.md): streams stays kernel (the access
  model lives there); REPL-consumes-types.ts; UrlDial-through-intercept
  (the capnweb fork only helps capnweb hops; UrlDial→DO is jsrpc).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0c01a32. Configure here.

itxInvoke carries the trusted chain-delegation origin and
itxProjectWorkerCall takes registry-merged props — reachable through
itx.project's whole-surface proxy, either would let any handle holder
spoof another context's identity (e.g. read a sibling fork's workspace).
The proxy now refuses path heads matching itx[A-Z]*; the registry's
reserved-segment gate stays as defense in depth for real chain traffic.
e2e probes the spoof route directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jonastemplestein jonastemplestein merged commit 252371f into main Jun 10, 2026
8 checks passed
@jonastemplestein jonastemplestein deleted the itx-defaults-migration branch June 10, 2026 22:03
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
… move

origin/main (#1482) moved repos off the handle's trust kernel onto the
platform:project cap, so the stub's static type no longer knows itx.repos.
The two repos routes now spell it itx.cap("repos") + Stubify — same
runtime fallthrough, typed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein added a commit that referenced this pull request Jun 11, 2026
Main independently shipped repos/workspace/worker as platform defaults with
origin-carrying chain delegation (the correct fix for inherited
context-scoped caps) and config-gated durable-object refs — that
implementation wins wholesale. Re-applied this branch's unique layers on
top: the egress capability (EgressPipe default + dialable, registry-
dispatching itx.fetch), the isolate-wiring unification in the registry's
loadWorker, the workers-RPC-safe onRpcBroken guard in provide, and
auth-routed id minting in ItxProjects.create. Dropped from this branch in
deference to main's choices: project-as-default (project stays a hardwired
built-in) and the PROJECT entry in DIALABLE_DURABLE_OBJECTS (allowlist
stays empty by default, config-gated).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein added a commit that referenced this pull request Jun 11, 2026
… names, REPL reads types.ts (#1490)

The four follow-ups the consolidation arc left written down in
itx-next.md, in one PR. Net **−578 lines** while adding a whole new
capability surface. No backcompat anywhere.

---

## 1. The egress intercept tunnel is deleted — shadowing was already the
feature

**Motivation.** #1487 made `fetch` an ordinary shadowable capability. At
that point the captun-based intercept tunnel was a second, hand-built
implementation of the same idea: "while I'm connected, route the
project's egress through me." It had its own DO field, accept route,
keepalive plumbing, a special `projectEgressInterceptActive` flag
threaded through secret substitution, a benchmark script, and a bespoke
test harness. All of it is expressible as one `caps.define`.

**Before** (the tunnel era):

```ts
// Client: dial a special captun endpoint on the project's ingress
const tunnel = await createCaptunTunnel({
  url: `${ingressUrl}/__iterate/intercept-project-egress`,
  headers: { Authorization: `Bearer ${adminToken}` },
  fetch: myFetch,
});
// Server: #projectEgressInterceptTunnel field, accept/replace/teardown
// lifecycle, an egressFetch branch, and a substitution mode that withheld
// real secret material while a tunnel was connected.
```

**After** (it's just a cap):

```ts
using itx = connectItx({ baseUrl, token, context: projectId });
class Interceptor extends RpcTarget {
  async call({ args }) { return await myFetch(args[0]); }  // args[0] is the Request
}
await itx.caps.define({ name: "fetch", invoke: "path-call", target: new Interceptor() });
// ALL project egress — itx.fetch() and bare fetch() in every loaded isolate —
// now flows through myFetch. Drop the session and the default pipe resurfaces.
```

The security property came along for free and got *simpler*: the tunnel
needed an explicit "withhold secrets while intercepted" mode inside
substitution; a shadow provider simply never reaches the substituting
pipe, so it sees `getSecret(...)` placeholders verbatim. The flag is
gone; substitution always yields real material on the one path that has
any.

The e2e fixture's `egressFetch` option keeps its exact API (now
implemented as above), and the workerd ingress test was rewritten to the
cap story — which caught a real bug: probing `onRpcBroken` on a
Workers-RPC live provider rejects unhandled (jsrpc proxies every
property as a remote method); the registry now treats it as best-effort.

## 2. `streams` joins the platform defaults

**Motivation.** After #1482/#1487 the kernel was `caps, streams, fork,
project, projects, describe`. `streams` was only still hardwired because
its access checks live in the handle. But split the concern in two and
the blocker dissolves: on a *project* context the namespace is
**forced** to the project (there is no access decision to make —
registry-injected `projectId` props pin it, definers can't point it
elsewhere), and only the *global* namespace genuinely needs the
connect-time access set.

**Before:** `ItxStreams`/`ItxStream` hardwired into `handle.ts`,
reserved name, not shadowable.

**After:**

```ts
// platform:project (code-contexts.ts) — just another definition:
caps.define({
  name: "streams",
  target: { type: "rpc", worker: { type: "loopback" }, entrypoint: "StreamsCap" },
});

// …which means a context can now shadow its own event-stream surface:
await itx.caps.define({ name: "streams", invoke: "path-call", target: myStreamsFake });
```

The collection/stream classes moved to `src/itx/caps/streams.ts`,
parameterized by an explicit `StreamsScope { access, exports }`; the
handle's getter branches — project handles resolve through the registry
(shadowable), global handles keep the kernel branch for the
deployment-wide `"global"` namespace gated on `access === "all"`.
Everything else is unchanged: absolute refs (`"ns:/path"`) still go
through the one access check with NOT_FOUND masking.

Two things made this safe to ship rather than scary:
- **Chained calls ride RPC promise pipelining.**
`itx.streams.get("/x").append(e)` crosses a boundary in every real
execution mode (capnweb from browsers/Node, jsrpc from loaded isolates),
and both transports pipeline follow-up calls onto returned RpcTargets —
the same shape `itx.agents.create().doThing()` already proved.
- **Subscriptions survive the extra hop.** `subscribe` callbacks now
cross client → registry DO → StreamsCap → Stream DO; the existing
dup-discipline in StreamsCapability holds, proven by the subscribe e2e
suite running unchanged.

## 3. Durable-object dials are name-scoped

**Motivation.** #1482 shipped `{ type: "durable-object", binding, name
}` refs behind an *empty* allowlist, because raw names meant an
allowlisted namespace would let any project dial any other project's
instances — documented as "the open design before any namespace can
join."

**Resolved:** the registry now dials

```ts
namespace.getByName(`itx:${projectId}:${name}`)
```

so every allowlisted namespace's itx-reachable instances are **disjoint
per project by construction** — the definer's `name` is a label inside
their project's slice, not a global address. Deployments can now
actually use `APP_CONFIG_ITX.dialableDurableObjects` for namespaces
designed for itx use. (Namespaces whose *existing* instances matter —
PROJECT, STREAM — still don't belong on the list: itx dials would reach
fresh, empty objects, which is exactly the point.)

## 4. The REPL editor consumes types.ts — drift is now structurally
impossible

**Motivation.** `apps/os/src/itx/types.ts` is the handwritten
design-of-record for the whole itx surface; the browser REPL's editor
carried a second, hand-maintained 353-line ambient declaration with a
"keep in sync" comment — the kind that's wrong within a week.

**After:** the REPL's TypeScript virtual FS loads `types.ts`
**verbatim** (`import source from "~/itx/types.ts?raw"`, which works in
both the vite-bundled worker and vitest). The hand-written file shrank
to a 120-line prelude declaring only what types.ts can't know: the
session globals (`itx`, `vars`, `projectId`, `RpcTarget`, `$_`). A bonus
improvement fell out: the capability fallthrough is now declared on the
official `KnownCaps` merge point, so handles returned by `itx.fork()` /
`itx.projects.get()` carry it too — `(await
itx.projects.get(id)).slack.chat.postMessage` typechecks in the editor,
which the old ambient got wrong. Tests assert the editor sees
types.ts-only markers, so regressions are loud.

---

## The kernel after this PR

```text
caps, fork, project, projects, describe        ← the trust kernel
streams (global namespace only)                ← connect-time access, by nature
─────────────────────────────────────────────
ai, fetch, streams, repos, workspace, worker   ← platform:project definitions,
                                                  i.e. literally the data
                                                  structures caps.define takes,
                                                  every one of them shadowable
```

## Verification

`pnpm typecheck` / `lint` / `knip` / `format` green; apps/os unit tests
222 green; workerd `test:project-ingress` 6/6 (including the rewritten
fetch-shadow-sees-placeholders test); itx e2e — 32 tests across
core/fork/http/subscribe suites, exercising streams through the registry
path, the example catalogue in every runtime, fork workspace isolation,
and the full fetch-shadow story — green against a local dev server.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches project egress, secret substitution, and ingress/DO routing
paths used in production and e2e; behavior changes are intentional but
need regression on fetch shadowing and streams via the registry.
> 
> **Overview**
> Removes the **Project Egress Intercept Tunnel** (captun route on the
Project DO/ingress, tunnel state, intercept-specific secret withholding,
benchmark script, and e2e captun helper) and replaces interception with
a **session-bound live `fetch` capability shadow** on the project itx
context—interceptors see `getSecret(...)` placeholders because
substitution only runs on the default egress pipe.
> 
> **`streams` becomes a shadowable `platform:project` default**
(`StreamsCap` loopback in `caps/streams.ts`); project handles resolve
`itx.streams` through the registry while the global `"global"` namespace
stays a kernel branch gated on admin access. **Durable-object capability
dials** now use `itx:<projectId>:<name>` so allowlisted namespaces are
per-project disjoint.
> 
> The **browser REPL editor** loads `~/itx/types.ts` verbatim via `?raw`
instead of a large hand-maintained ambient file; docs/ADR/context and
tests are updated accordingly, including registry best-effort
`onRpcBroken` for Workers-RPC live providers.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
93efa03. 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-11T07:07:45.989Z",
      "headSha": "93efa03e779290348755a99b9317ae9274c20261",
      "message": null,
      "publicUrl": "https://os.iterate-preview-2.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27329834559",
      "shortSha": "93efa03"
    }
  },
  "environmentConfigLease": {
    "dopplerConfig": "preview_2",
    "leasedUntil": 1781164984175,
    "leaseId": "eae024be-b4d7-4976-92e4-0013205b125e",
    "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-11T08:03:04.175Z

### OS
Status: deployed
Commit: `93efa03`
Preview: https://os.iterate-preview-2.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27329834559)
Updated: 2026-06-11T07:07:45.989Z
<!-- /CLOUDFLARE_PREVIEW -->

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein added a commit that referenced this pull request Jun 11, 2026
…tercept dies, kernel shrinks, auth mints, legacy afterAppend deleted (#1485)

## What

The remaining grand-cleanup workstreams in one deliberately breaking PR
(prd gets redeployed). DECISIONS **D23** is the canonical record. Three
main-side PRs landed mid-flight and overlap this work — all adopted
wholesale in the merges: **#1482** (repos/workspace/worker as platform
defaults with origin-carrying delegation), **#1487** (`fetch` is a
shadowable cap, `define` absorbs `provide`, shared registry host), and
**#1490** (intercept tunnel deleted, streams is a cap, best-effort
`onRpcBroken`). This PR contributes the layers below on top of them.

### §9 finished: the egress pipe is stateless
- #1487/#1490 made `fetch` a shadowable platform:project cap and deleted
the tunnel, but kept the DEFAULT pipe inside the Project DO
(`ProjectEgress.call` → `egressFetch`). This PR replaces that terminal
with the stateless **`EgressPipe`** loopback. The Project DO still
supervises every dispatch (live shadows resolve in its registry), but
egress secrets are D1 rows scoped by the registry-injected `projectId`,
so substitution + the real outbound fetch run in a plain isolate and
**secret material never enters the DO**.
- **The Project DO has no fetch surface at all** — no `fetch`, no
`ingressFetch`, no `egressFetch`.

### Worker-loading unification
- `itx/isolate.ts` is the ONE place the platform's trust posture (Law 4
ITERATE scoping, Law 5 egress outbound) is wired into loaded isolates;
the registry's source caps and the project worker both use it. (The
Workers-RPC-safe `onRpcBroken` guard this PR carried shipped
independently in #1490 — main's version adopted.)

### `ProjectCapability` dissolved
The hand-wired forwarder entrypoint is deleted; nothing called it.

### Auth is the ONLY project-id minter
New auth internal route `POST /internal/project/mint-project-id`
(service-authed); OS operator/recovery creates (project directory +
`itx.projects.create`) round-trip through it. `mintProjectId` is deleted
from OS — the `prj_` id space has exactly one source.

### Legacy afterAppend/runner-state deleted
The agent, slack-agent, slack-integration, and repo DOs lose their
`afterAppend` RPCs and fake runner shapes (delivery has been on the host
model for a while). Agent runtime state is now the honest `{ agentPath,
processors: { [slug]: snapshot } }`; slack `ensureReady` returns a plain
snapshot; the agent-stream benchmark updated.

## Deferred to main's posture (from the original plan)
- `project` stays a hardwired built-in (per #1482's kernel choice)
rather than a durable-object default; `DIALABLE_DURABLE_OBJECTS` stays
empty by default (config-gated).
- The egress cap is named `fetch` (per #1487), not `egress`.

## ⚠️ Merge order
**#1489 must merge (and auth deploy) first** — this PR's create paths
round-trip id minting through auth's new
`/internal/project/mint-project-id`, and previews point at production
auth. The preview e2e here 404s until that endpoint is live.

## Breaking changes (intended)
- Agent `runtimeState` shape changed (consumers were shape-agnostic or
updated).
- `egressFetch` is gone from every surface; use `itx.fetch` / the
`egress` cap.

## Testing
- Full repo gates green (typecheck, lint, 35/35 apps/os test files).
- Workers suites: project-ingress 6/6 (incl. live-shadow +
revoke-restores-default), itx-stream-subscribe 13/13.
- `project-mcp-server-connection` fails 2/3 **identically on the branch
base** (verified in a clean worktree) — pre-existing.
- Preview e2e exercises: the egress capability over capnweb (explicit +
implicit doors), the new live-shadow helper, and auth-routed minting.

## Out of scope
- Egress policy-as-data / hold-for-approval (the §9 follow-on).
- Stream processors taking a synchronous SQL client (jam).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **High Risk**
> Breaking egress and secret-handling semantics (DO no longer
substitutes secrets; interceptors see raw placeholders), new auth
dependency for id minting, and changed agent runtimeState shape affect
security-sensitive paths and deploy ordering.
> 
> **Overview**
> Completes **itx D23**: project egress is a shadowable **`fetch`**
capability whose default terminal is the stateless **`EgressPipe`**
(secret substitution + outbound fetch in a plain isolate), while the
Project DO only supervises registry dispatch. **`fetch` / `egressFetch`
are removed** from the Project DO; **`ProjectCapability`** is deleted.
> 
> Adds **`itx/isolate.ts`** so project workers, source caps, and the run
harness share one **ITERATE + `ProjectEgress` globalOutbound** wiring
path.
> 
> **Auth is the sole `prj_` minter**: OS drops local
**`mintProjectId`**; operator/admin and **`itx.projects.create`** call
auth’s **`mintProjectId`** internal route.
> 
> Removes legacy **`afterAppend`** / runner-shaped RPCs on agent, slack,
and repo DOs; agent **`runtimeState`** is **`{ agentPath, processors
}`** (benchmark updated). Docs mark §8/§9 shipped; live **`fetch`**
shadows see **raw** `getSecret(...)` placeholders (withheld-text mode
removed).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
df5965b. 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-11T10:23:48.699Z",
      "headSha": "df5965b9948016c979fa5a71ae3b991f66e8c42c",
      "message": null,
      "publicUrl": "https://os.iterate-preview-6.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27340067055",
      "shortSha": "df5965b"
    }
  },
  "environmentConfigLease": {
    "dopplerConfig": "preview_6",
    "leasedUntil": 1781176786363,
    "leaseId": "9c50031d-b4ce-4f00-a8fe-66a3ff9f9df5",
    "slug": "preview-6",
    "type": "environment-config-lease"
  }
}
-->
<!-- /CLOUDFLARE_PREVIEW_STATE -->
Lease: `preview-6`
Doppler config: `preview_6`
Type: `environment-config-lease`
Leased until: 2026-06-11T11:19:46.363Z

### OS
Status: deployed
Commit: `df5965b`
Preview: https://os.iterate-preview-6.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27340067055)
Updated: 2026-06-11T10:23:48.699Z
<!-- /CLOUDFLARE_PREVIEW -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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