@cat-factory/server 0.159.0 → 0.161.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,7 @@
1
1
  import { Hono } from 'hono';
2
2
  import { TOKEN_AUDIENCE, signerFor } from '../../auth/signing.js';
3
+ import { authorizeMachineSubscribe } from '../../events/machineSubscribe.js';
4
+ import { param } from '../../http/params.js';
3
5
  /**
4
6
  * Upper bound on a relayed `payload` frame (characters). A serialized `WorkspaceEvent` is small
5
7
  * (ids + a reason + a compact instance snapshot); 1 MiB is a generous ceiling that still brakes a
@@ -8,7 +10,9 @@ import { TOKEN_AUDIENCE, signerFor } from '../../auth/signing.js';
8
10
  */
9
11
  const MAX_RELAYED_PAYLOAD_CHARS = 1_000_000;
10
12
  /**
11
- * The mothership-mode real-time UPSTREAM machine API: `POST /internal/events/publish`.
13
+ * The mothership-mode real-time machine API BOTH directions:
14
+ * `POST /internal/events/publish` (upstream) and
15
+ * `GET /internal/events/subscribe/:workspaceId` (inbound).
12
16
  *
13
17
  * A mothership-mode local node runs the engine locally but delegates org/durable state to the
14
18
  * mothership. Its engine events must ALSO reach the mothership's real-time fan-out, so a hosted
@@ -91,6 +95,51 @@ export function eventsRelayController() {
91
95
  }
92
96
  return c.json({ ok: true });
93
97
  });
98
+ // The INBOUND leg: the node subscribes to a workspace's stream so org activity raised on the
99
+ // mothership (a hosted teammate, or a peer laptop relaying upstream) reaches the laptop's SPA.
100
+ //
101
+ // It is deliberately NOT a new fan-out. The upgrade is handed to the SAME per-workspace realtime
102
+ // transport the browser stream uses (`gateways.realtime.upgrade` — the `WorkspaceEventsHub`
103
+ // Durable Object on Cloudflare), so a subscribed node is just another socket in the workspace's
104
+ // room and every event reaches laptops and browsers through one code path. The node's stable
105
+ // `?cid=` rides through to that transport, which is what lets the mothership skip echoing the
106
+ // node's OWN relayed events back to it (the outbound leg stamps the same id as
107
+ // `originConnectionId`).
108
+ //
109
+ // Auth is the shared `authorizeMachineSubscribe` (token pin → capability → account scope), the
110
+ // same helper the Node facade calls from its HTTP-server `upgrade` listener — Node can't upgrade
111
+ // from a Hono `Response`, so this handler is unreachable there, exactly like the browser stream's
112
+ // GET in `eventsController`.
113
+ app.get(`/internal/events/subscribe/:workspaceId`, async (c) => {
114
+ const container = c.get('container');
115
+ const workspaceId = param(c, 'workspaceId');
116
+ // The scope resolution reuses the mothership's own repository registry (attached alongside the
117
+ // relay on every mothership), exactly like the publish handler above. The registry is
118
+ // reflective (`Record<string, Record<string, fn>>`), so narrow the one method we call.
119
+ const workspaceRepository = container.repositories?.workspaceRepository;
120
+ const auth = await authorizeMachineSubscribe({
121
+ auth: container.config.auth,
122
+ token: c.req.header('authorization'),
123
+ workspaceId,
124
+ accountOf: workspaceRepository?.accountOf
125
+ ? (id) => workspaceRepository.accountOf(id)
126
+ : undefined,
127
+ });
128
+ if (!auth.ok) {
129
+ return c.json({
130
+ ok: false,
131
+ error: { code: auth.status === 404 ? 'not_found' : 'forbidden', message: auth.message },
132
+ }, auth.status);
133
+ }
134
+ // Checked AFTER auth so the handshake shape isn't probeable without a token.
135
+ if (c.req.header('Upgrade')?.toLowerCase() !== 'websocket') {
136
+ return c.text('expected a websocket upgrade', 426);
137
+ }
138
+ const upgraded = await container.gateways.realtime.upgrade(workspaceId, c.req.raw);
139
+ if (!upgraded)
140
+ return c.text('real-time events are not enabled', 501);
141
+ return upgraded;
142
+ });
94
143
  return app;
95
144
  }
96
145
  //# sourceMappingURL=EventsRelayController.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EventsRelayController.js","sourceRoot":"","sources":["../../../src/modules/events/EventsRelayController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAuB,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAItF;;;;;GAKG;AACH,MAAM,yBAAyB,GAAG,SAAS,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAU,CAAA;IAE9B,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC/C,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAEpC,0FAA0F;QAC1F,yBAAyB;QACzB,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAA;QAClD,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAA;QACvE,MAAM,OAAO,GAAG,MAAM;YACpB,CAAC,CAAC,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC,MAAM,CAAiB,KAAK,EAAE,EAAE,GAAG,EAAE,cAAc,CAAC,OAAO,EAAE,CAAC;YACxF,CAAC,CAAC,IAAI,CAAA;QACR,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE,EAC7E,GAAG,CACJ,CAAA;QACH,CAAC;QAED,MAAM,KAAK,GAAG,SAAS,CAAC,iBAAiB,CAAA;QACzC,yFAAyF;QACzF,4FAA4F;QAC5F,MAAM,mBAAmB,GAAG,SAAS,CAAC,YAAY,EAAE,mBAAmB,CAAA;QACvE,IAAI,CAAC,KAAK,IAAI,CAAC,mBAAmB,EAAE,SAAS,EAAE,CAAC;YAC9C,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE,EAC9E,GAAG,CACJ,CAAA;QACH,CAAC;QAED,IAAI,IAA0B,CAAA;QAC9B,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAyB,CAAA;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,sBAAsB,EAAE,EAAE,EAC7E,GAAG,CACJ,CAAA;QACH,CAAC;QACD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACtF,OAAO,CAAC,CAAC,IAAI,CACX;gBACE,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,sCAAsC,EAAE;aAC/E,EACD,GAAG,CACJ,CAAA;QACH,CAAC;QACD,iGAAiG;QACjG,wFAAwF;QACxF,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,yBAAyB,EAAE,CAAC;YACpD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;QAChG,CAAC;QAED,iGAAiG;QACjG,kGAAkG;QAClG,+FAA+F;QAC/F,mBAAmB;QACnB,MAAM,SAAS,GAAG,CAAC,MACjB,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAC/C,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAA8B,CAAA;QACtD,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChE,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE,EAC3E,GAAG,CACJ,CAAA;QACH,CAAC;QAED,+FAA+F;QAC/F,gGAAgG;QAChG,kGAAkG;QAClG,6EAA6E;QAC7E,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,MAAM,CAAC;gBACjB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,kBAAkB,EAChB,OAAO,IAAI,CAAC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI;aAC/E,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;QACvC,CAAC;QACD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,CAAA;AACZ,CAAC"}
1
+ {"version":3,"file":"EventsRelayController.js","sourceRoot":"","sources":["../../../src/modules/events/EventsRelayController.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { PersistenceMethodTable } from './rpc.js';
2
+ /**
3
+ * The mothership-mode persistence allow-list: the core domain repositories plus the
4
+ * workspace-scoped reads a board load (`GET /workspaces/:id`) and an execution exercise.
5
+ * Every method here binds to an account via its {@link ScopeRule} so a call outside the
6
+ * machine token's scope is refused as 404.
7
+ *
8
+ * The cross-service board-composition reads keyed on `serviceIds[]`/`accountId`
9
+ * (`listByServices`, `serviceRepository.listByIds`/`listByAccount`, `countByServiceIds`) and the
10
+ * entity-id-keyed `blockRepository.findById` are allow-listed here too, each bound by the
11
+ * {@link ScopeRule} `serviceList` / `block` / `account` kinds that resolve the entity's owning
12
+ * account server-side before the scope check.
13
+ *
14
+ * Still EXCLUDED (added in later gate slices, with their own scope rules, or kept
15
+ * mothership-internal):
16
+ * - `subscriptionActivationRepository.deleteByExecution` — the activation row is the local
17
+ * `node:sqlite` bucket (per the per-repo checklist), not the remote surface, so it is not
18
+ * exposed here.
19
+ * - Global sweeper methods (`listStale`, `deleteOlderThan`) and high-impact unscoped ops
20
+ * (`workspaceRepository.delete`, `accountRepository.create`).
21
+ *
22
+ * Admin-gated mutations are also EXCLUDED here. The RPC dispatches over the raw repository,
23
+ * bypassing the service layer that normally enforces per-user role checks — e.g.
24
+ * `AccountService.requireAdmin` guards `accountRepository.rename`/`updateSettings` and
25
+ * `membershipRepository.upsert`/`remove`. A machine token is scoped to whole ACCOUNTS, not to
26
+ * a role within them, so exposing those repo methods would let any account member self-promote
27
+ * to admin or rewrite memberships over the wire. They stay mothership-internal until a later
28
+ * slice adds a role dimension to the scope (or routes them through the service). Only the
29
+ * account/membership READS a board load needs are remotely callable. Board-level mutations
30
+ * (`workspaceRepository.rename`/`setDescription`, block/pipeline/execution CRUD) are
31
+ * member-level in the service layer, so they remain. The board mutations that stay OUT are
32
+ * `workspaceRepository.setAccessMode` (the access-mode flip) and `linkAccount` (the legacy-board
33
+ * auto-heal that adopts a board into an account) — both are `members.manage` (admin-tier), so like
34
+ * the account/membership admin mutations they must not be reachable over the role-blind machine RPC.
35
+ */
36
+ export declare const REMOTE_PERSISTENCE_METHODS: PersistenceMethodTable;
37
+ //# sourceMappingURL=rpc-allowlist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-allowlist.d.ts","sourceRoot":"","sources":["../../src/persistence/rpc-allowlist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAgBtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,0BAA0B,EAAE,sBAo1BxC,CAAA"}