From 38c0fcbd9d9e32932432b4ab8678e61b282e1403 Mon Sep 17 00:00:00 2001 From: Garza OS Agent Date: Sat, 30 May 2026 17:07:54 +0000 Subject: [PATCH 1/4] feat(env): add optional DEFAULT_REPLICA_ID binding --- replicas-matrix-bridge/src/index.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/replicas-matrix-bridge/src/index.ts b/replicas-matrix-bridge/src/index.ts index 397a12d7..1c800e35 100644 --- a/replicas-matrix-bridge/src/index.ts +++ b/replicas-matrix-bridge/src/index.ts @@ -21,6 +21,14 @@ export interface Env { REPLICAS_AGENT_OVERRIDE?: string; REPLICAS_MODEL_OVERRIDE?: string; REPLICAS_THINKING_OVERRIDE?: string; + // Single-workspace mode. When set, dispatch reuses this replica id for + // any room whose KV mapping is missing instead of POST /v1/replica. + // On first message in a new room we pin the mapping (room: -> + // DEFAULT_REPLICA_ID) so subsequent traffic flows the normal + // `existing` path and never falls back. Unset to keep per-room + // auto-spawn behavior. Set via `wrangler secret put DEFAULT_REPLICA_ID` + // or under [vars] in wrangler.toml. + DEFAULT_REPLICA_ID?: string; // JSON array of Megolm session keys (Element key-export format, decrypted // out of band and stashed here). Used by the listener to decrypt // m.room.encrypted events whose session_id we hold. From 9181d7e2b9797c10fd6c52ae1434e30881ea689b Mon Sep 17 00:00:00 2001 From: Garza OS Agent Date: Sat, 30 May 2026 17:07:55 +0000 Subject: [PATCH 2/4] feat(dispatch): route fresh rooms to DEFAULT_REPLICA_ID when set --- replicas-matrix-bridge/src/dispatch.ts | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/replicas-matrix-bridge/src/dispatch.ts b/replicas-matrix-bridge/src/dispatch.ts index 209ec782..1dc1e040 100644 --- a/replicas-matrix-bridge/src/dispatch.ts +++ b/replicas-matrix-bridge/src/dispatch.ts @@ -126,6 +126,42 @@ export async function handleMatrixMessage( replicaId = await createReplica(env, roomId, eventId, text); spawnedFresh = true; } + } else if (env.DEFAULT_REPLICA_ID) { + // Single-workspace mode: forward to the configured default replica + // instead of POST /v1/replica. Pin the mapping on success so this + // path only fires once per room. On any send failure (gone, 5xx, + // network blip) fall through to fresh-spawn — same safety stance + // as the existing-replica error path above. + const defaultId = env.DEFAULT_REPLICA_ID; + console.log(`[dispatch] using DEFAULT_REPLICA_ID=${defaultId} for fresh room=${roomId}`); + const followUpP = sendFollowUp(defaultId, text, env, roomId, eventId); + const watcherP = startWatcher(env, defaultId, roomId, eventId, text, undefined, undefined); + const followUp = await followUpP; + await watcherP; + if (followUp.ok) { + replicaId = defaultId; + // Pin the mapping so subsequent messages route via the normal + // `existing` branch without re-checking DEFAULT_REPLICA_ID. + // Honour REPLICA_TTL_SECONDS the same way the fresh-spawn + // path does below. + const ttlEnv = parseInt(env.REPLICA_TTL_SECONDS, 10); + const opts: KVNamespacePutOptions = ttlEnv > 0 + ? { expirationTtl: Math.max(60, ttlEnv) } + : {}; + await env.MAP.put(key, defaultId, opts); + // spawnedFresh stays false — we already started the watcher + // above so we want the follow-up path below, not the + // fresh-spawn startWatcher call. + } else { + console.log( + `[dispatch] DEFAULT_REPLICA_ID=${defaultId} sendFollowUp failed gone=${followUp.gone} — falling back to fresh spawn`, + ); + env.WATCHER.get(env.WATCHER.idFromName(defaultId)) + .fetch("https://watcher/cancel", { method: "POST" }) + .catch(() => {}); + replicaId = await createReplica(env, roomId, eventId, text); + spawnedFresh = true; + } } else { replicaId = await createReplica(env, roomId, eventId, text); spawnedFresh = true; From 1c9481eee70d140823d5c104a191bcd82bfcd059 Mon Sep 17 00:00:00 2001 From: Garza OS Agent Date: Sat, 30 May 2026 17:07:55 +0000 Subject: [PATCH 3/4] feat(wrangler): document DEFAULT_REPLICA_ID single-workspace mode --- replicas-matrix-bridge/wrangler.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/replicas-matrix-bridge/wrangler.toml b/replicas-matrix-bridge/wrangler.toml index 47fbd8c2..19131ed6 100644 --- a/replicas-matrix-bridge/wrangler.toml +++ b/replicas-matrix-bridge/wrangler.toml @@ -60,6 +60,13 @@ REPLICA_TTL_SECONDS = "0" REPLICAS_AGENT_OVERRIDE = "claude" REPLICAS_MODEL_OVERRIDE = "claude-sonnet-4-6" REPLICAS_THINKING_OVERRIDE = "medium" +# Single-workspace mode. When set, dispatch reuses this replica id for +# rooms whose KV mapping is missing instead of POST /v1/replica. Pinned +# on first message so subsequent messages route via the normal existing +# path. Leave commented (or unset) to keep the per-room auto-spawn +# behavior. Can also be set as a secret with: +# wrangler secret put DEFAULT_REPLICA_ID +# DEFAULT_REPLICA_ID = "00000000-0000-0000-0000-000000000000" # Bot device id on matrix.org (the device the MATRIX_ACCESS_TOKEN was minted # for). Used as the id in the Olm device-key upload + signature subject. MATRIX_DEVICE_ID = "Ww3fWv0z7s" From de6faea0f2079170e48e9c29cc1a9843fbbb7695 Mon Sep 17 00:00:00 2001 From: Garza OS Agent Date: Sat, 30 May 2026 17:29:39 +0000 Subject: [PATCH 4/4] docs: add SINGLE_WORKSPACE_MODE operator guide --- .../docs/SINGLE_WORKSPACE_MODE.md | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 replicas-matrix-bridge/docs/SINGLE_WORKSPACE_MODE.md diff --git a/replicas-matrix-bridge/docs/SINGLE_WORKSPACE_MODE.md b/replicas-matrix-bridge/docs/SINGLE_WORKSPACE_MODE.md new file mode 100644 index 00000000..a9acad70 --- /dev/null +++ b/replicas-matrix-bridge/docs/SINGLE_WORKSPACE_MODE.md @@ -0,0 +1,97 @@ +# Single-Workspace Mode for replicas-matrix-bridge + +The bridge's default behavior is **one Replicas workspace per Matrix room**: on +first message in a previously-unseen room, `dispatch.ts` calls +`POST /v1/replica` to spawn a workspace named `mx--` and +writes the binding to KV (`room:`). + +This document describes how to opt into **single-workspace mode**: every +KV-miss room shares one configured replica. Useful when you have a single +long-lived workspace and want every new project room to drop into it +without paying for/managing a separate sandbox per chat. + +## What the fix changes + +PR #25 adds an optional `DEFAULT_REPLICA_ID` binding to `Env`. + +- If unset → behavior is unchanged. Per-room spawn continues. +- If set → in `dispatch.ts`, the `else` branch of the existing/KV-miss + check now forwards to `DEFAULT_REPLICA_ID` instead of `createReplica`, + pins the KV mapping on success (so future messages route via the normal + `existing` branch), and falls through to legacy `createReplica` only if + the default replica's `sendFollowUp` fails. The bot never goes silent. + +Touched files: `src/index.ts`, `src/dispatch.ts`, `wrangler.toml`. + +## How to enable + +After this PR is merged and the worker is redeployed: + +```sh +cd replicas-matrix-bridge +echo "" | wrangler secret put DEFAULT_REPLICA_ID +# or set under [vars] in wrangler.toml for non-secret value +``` + +Find a replica id with: + +```sh +curl -s -H "Authorization: Bearer $REPLICAS_API_KEY" \ + -H "Replicas-Org-Id: 778b1aa3-4327-45a4-9874-c8a3a72df610" \ + "https://api.replicas.dev/v1/replica?limit=100" \ + | jq '.replicas[] | {id,name,status}' +``` + +## Alternative: pre-pin KV without a deploy + +If you don't want to (or can't) redeploy the worker, you can achieve the +same effect by **pre-populating the KV mapping for a room before any +message arrives** in it. The bridge's `dispatch.ts` only spawns when +`env.MAP.get("room:" + roomId)` returns null, so pre-writing the key +suppresses the spawn. + +The Garza OS workspace ships two helpers for this: + +- **`~/.replicas/bin/garza-project-provision`** — creates a Matrix room + with `m.room.name`, topic, invitees, power levels, and a + `dev.garza.agent.config` state event marking it as Garza-managed. It + honours `GARZA_ROUTER_REPLICA_ID` (falling back to `WORKSPACE_ID`) and + writes `room:` into the bridge's KV namespace + (`036b2f9231de4d21bf0cdf120b5b4869`) immediately after `createRoom`. + Result: new rooms never spawn. + +- **`~/.replicas/bin/garza-rebind-rooms`** — bulk-rewrites all (or a + filtered subset of) existing `room:*` KV entries to point at one + replica id. Useful for consolidating historical sprawl. Use + `--status` for read-only inspection and `--dry-run` to preview a + rebind without writing. The orphaned per-room workspaces remain + alive until you `DELETE /v1/replica/{id}` them, so cleanup is a + separate explicit step. + +## KV schema (for reference) + +Namespace: `036b2f9231de4d21bf0cdf120b5b4869` (`MAP` binding). + +| Key | Value | Purpose | +|---|---|---| +| `room:` | replica UUID | Per-room binding read by dispatch | +| `members:` | JSON member list | Cached member set for size gating | +| `session:` | Olm session metadata | Encryption state | +| `model:` | model id string | Per-room `!model` override | + +Direct KV write via the Cloudflare API: + +```sh +curl -X PUT \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: text/plain" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/storage/kv/namespaces/036b2f9231de4d21bf0cdf120b5b4869/values/room:!ROOM:matrix.org" \ + --data-binary "" +``` + +## Verification + +After enabling either mechanism, create a new test room and post a message. +Expected: no new `mx-*` workspace appears in `GET /v1/replica`. The +existing workspace receives the message via the normal `existing` path. +