@cat-factory/server 0.332.3 → 0.335.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.
Files changed (58) hide show
  1. package/dist/app.d.ts.map +1 -1
  2. package/dist/app.js +6 -0
  3. package/dist/app.js.map +1 -1
  4. package/dist/config/types.d.ts +6 -0
  5. package/dist/config/types.d.ts.map +1 -1
  6. package/dist/http/cors.d.ts +2 -0
  7. package/dist/http/cors.d.ts.map +1 -1
  8. package/dist/http/cors.js +3 -0
  9. package/dist/http/cors.js.map +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/modules/publicApi/PublicApiKeyController.d.ts.map +1 -1
  15. package/dist/modules/publicApi/PublicApiKeyController.js +55 -28
  16. package/dist/modules/publicApi/PublicApiKeyController.js.map +1 -1
  17. package/dist/modules/publicApi/PublicDirectoryController.d.ts +4 -0
  18. package/dist/modules/publicApi/PublicDirectoryController.d.ts.map +1 -0
  19. package/dist/modules/publicApi/PublicDirectoryController.js +107 -0
  20. package/dist/modules/publicApi/PublicDirectoryController.js.map +1 -0
  21. package/dist/modules/publicApi/PublicDiscoveryController.d.ts.map +1 -1
  22. package/dist/modules/publicApi/PublicDiscoveryController.js +2 -2
  23. package/dist/modules/publicApi/PublicDiscoveryController.js.map +1 -1
  24. package/dist/modules/publicApi/PublicKeyController.d.ts.map +1 -1
  25. package/dist/modules/publicApi/PublicKeyController.js +22 -11
  26. package/dist/modules/publicApi/PublicKeyController.js.map +1 -1
  27. package/dist/modules/publicApi/PublicPrAssessmentController.d.ts +4 -0
  28. package/dist/modules/publicApi/PublicPrAssessmentController.d.ts.map +1 -0
  29. package/dist/modules/publicApi/PublicPrAssessmentController.js +26 -0
  30. package/dist/modules/publicApi/PublicPrAssessmentController.js.map +1 -0
  31. package/dist/modules/publicApi/decisions/scope.d.ts +1 -1
  32. package/dist/modules/publicApi/decisions/scope.d.ts.map +1 -1
  33. package/dist/modules/publicApi/keyProjection.d.ts +15 -13
  34. package/dist/modules/publicApi/keyProjection.d.ts.map +1 -1
  35. package/dist/modules/publicApi/keyProjection.js +32 -12
  36. package/dist/modules/publicApi/keyProjection.js.map +1 -1
  37. package/dist/modules/publicApi/openapiDocument.generated.d.ts +1 -1
  38. package/dist/modules/publicApi/openapiDocument.generated.d.ts.map +1 -1
  39. package/dist/modules/publicApi/openapiDocument.generated.js +1 -1
  40. package/dist/modules/publicApi/openapiDocument.generated.js.map +1 -1
  41. package/dist/modules/publicApi/publicApiAuth.d.ts +24 -9
  42. package/dist/modules/publicApi/publicApiAuth.d.ts.map +1 -1
  43. package/dist/modules/publicApi/publicApiAuth.js +61 -4
  44. package/dist/modules/publicApi/publicApiAuth.js.map +1 -1
  45. package/dist/modules/workspaces/WorkspaceController.js +3 -3
  46. package/dist/modules/workspaces/WorkspaceController.js.map +1 -1
  47. package/dist/modules/workspaces/infraSetup.d.ts +28 -1
  48. package/dist/modules/workspaces/infraSetup.d.ts.map +1 -1
  49. package/dist/modules/workspaces/infraSetup.js +25 -0
  50. package/dist/modules/workspaces/infraSetup.js.map +1 -1
  51. package/dist/persistence/rpc-allowlist.d.ts.map +1 -1
  52. package/dist/persistence/rpc-allowlist.js +20 -0
  53. package/dist/persistence/rpc-allowlist.js.map +1 -1
  54. package/dist/runtime/directoryWebhooks.d.ts +12 -0
  55. package/dist/runtime/directoryWebhooks.d.ts.map +1 -0
  56. package/dist/runtime/directoryWebhooks.js +18 -0
  57. package/dist/runtime/directoryWebhooks.js.map +1 -0
  58. package/package.json +8 -8
@@ -0,0 +1,107 @@
1
+ import { deleteDirectoryWebhookContract, listDirectoryAccountMembershipsContract, listDirectoryChangesContract, listDirectoryReposContract, listDirectoryUsersContract, listDirectoryWorkspaceMembershipsContract, listDirectoryWebhooksContract, listDirectoryWorkspacesContract, putDirectoryWebhookContract, } from '@cat-factory/contracts';
2
+ import { ForbiddenError } from '@cat-factory/kernel';
3
+ import { buildHonoRoute } from '@toad-contracts/hono';
4
+ import { Hono } from 'hono';
5
+ import { requireCapability } from '../../http/guards.js';
6
+ import { authorizeAccount, refuse } from './publicApiAuth.js';
7
+ // The public DIRECTORY surface (`/api/v1/directory/*`): an account's workspaces, users,
8
+ // memberships and linked repositories, and the change feed that keeps a mirror of them current.
9
+ // Account-scoped, so no workspace header is read; the key's workspace reach becomes the reader's
10
+ // filter. The rules (visibility, cursor expiry, the snapshot watermark) are `DirectoryService`'s.
11
+ // Design: backend/docs/adr/0067-directory-sync.md.
12
+ export function publicDirectoryController() {
13
+ const app = new Hono();
14
+ buildHonoRoute(app, listDirectoryChangesContract, async (c) => {
15
+ const gate = await authorizeAccount(c, listDirectoryChangesContract.minScope);
16
+ if ('fail' in gate)
17
+ return refuse(c, gate.fail);
18
+ const { after, limit } = c.req.valid('query');
19
+ const page = await c.get('container').directory.changes(reader(gate.auth), after ?? 0, limit);
20
+ return c.json(page, 200);
21
+ });
22
+ buildHonoRoute(app, listDirectoryWorkspacesContract, async (c) => {
23
+ const gate = await authorizeAccount(c, listDirectoryWorkspacesContract.minScope);
24
+ if ('fail' in gate)
25
+ return refuse(c, gate.fail);
26
+ const { cursor, limit } = c.req.valid('query');
27
+ return c.json(await c.get('container').directory.workspaces(reader(gate.auth), cursor, limit), 200);
28
+ });
29
+ buildHonoRoute(app, listDirectoryUsersContract, async (c) => {
30
+ const gate = await authorizeAccount(c, listDirectoryUsersContract.minScope);
31
+ if ('fail' in gate)
32
+ return refuse(c, gate.fail);
33
+ const { cursor, limit } = c.req.valid('query');
34
+ return c.json(await c.get('container').directory.users(reader(gate.auth), cursor, limit), 200);
35
+ });
36
+ buildHonoRoute(app, listDirectoryAccountMembershipsContract, async (c) => {
37
+ const gate = await authorizeAccount(c, listDirectoryAccountMembershipsContract.minScope);
38
+ if ('fail' in gate)
39
+ return refuse(c, gate.fail);
40
+ const { cursor, limit } = c.req.valid('query');
41
+ const page = await c
42
+ .get('container')
43
+ .directory.accountMemberships(reader(gate.auth), cursor, limit);
44
+ return c.json(page, 200);
45
+ });
46
+ buildHonoRoute(app, listDirectoryWorkspaceMembershipsContract, async (c) => {
47
+ const gate = await authorizeAccount(c, listDirectoryWorkspaceMembershipsContract.minScope);
48
+ if ('fail' in gate)
49
+ return refuse(c, gate.fail);
50
+ const { cursor, limit } = c.req.valid('query');
51
+ const page = await c
52
+ .get('container')
53
+ .directory.workspaceMemberships(reader(gate.auth), cursor, limit);
54
+ return c.json(page, 200);
55
+ });
56
+ buildHonoRoute(app, listDirectoryReposContract, async (c) => {
57
+ const gate = await authorizeAccount(c, listDirectoryReposContract.minScope);
58
+ if ('fail' in gate)
59
+ return refuse(c, gate.fail);
60
+ const { cursor, limit } = c.req.valid('query');
61
+ return c.json(await c.get('container').directory.repos(reader(gate.auth), cursor, limit), 200);
62
+ });
63
+ // ---- Directory webhooks: account-level push endpoints --------------------------------------
64
+ // `admin` and account-wide only: an endpoint receives every directory change in the account, so
65
+ // a key limited to some workspaces could otherwise read the rest through a push it registered.
66
+ buildHonoRoute(app, listDirectoryWebhooksContract, async (c) => {
67
+ const gate = await authorizeAccount(c, listDirectoryWebhooksContract.minScope);
68
+ if ('fail' in gate)
69
+ return refuse(c, gate.fail);
70
+ const accountId = requireAccountWide(gate.auth);
71
+ return c.json({ webhooks: await webhooks(c).list(accountId) }, 200);
72
+ });
73
+ buildHonoRoute(app, putDirectoryWebhookContract, async (c) => {
74
+ const gate = await authorizeAccount(c, putDirectoryWebhookContract.minScope);
75
+ if ('fail' in gate)
76
+ return refuse(c, gate.fail);
77
+ const accountId = requireAccountWide(gate.auth);
78
+ const { webhookId } = c.req.valid('param');
79
+ return c.json(await webhooks(c).put(accountId, webhookId, c.req.valid('json')), 200);
80
+ });
81
+ buildHonoRoute(app, deleteDirectoryWebhookContract, async (c) => {
82
+ const gate = await authorizeAccount(c, deleteDirectoryWebhookContract.minScope);
83
+ if ('fail' in gate)
84
+ return refuse(c, gate.fail);
85
+ const accountId = requireAccountWide(gate.auth);
86
+ await webhooks(c).delete(accountId, c.req.valid('param').webhookId);
87
+ return c.body(null, 204);
88
+ });
89
+ return app;
90
+ }
91
+ function webhooks(c) {
92
+ return requireCapability(c.get('container').directoryWebhooks, 'Directory webhooks are not served here: they need ENCRYPTION_KEY to seal signing secrets, and a mothership-mode node leaves them to the mothership');
93
+ }
94
+ /** The key's account, refusing a key limited to some workspaces. */
95
+ function requireAccountWide(auth) {
96
+ if (auth.workspaceIds !== null) {
97
+ throw new ForbiddenError('Directory webhooks need a key that reaches every workspace', {
98
+ reason: 'account_scope_required',
99
+ });
100
+ }
101
+ return auth.accountId;
102
+ }
103
+ /** The directory reader a key is: its account, and its workspace reach as the filter. */
104
+ function reader(auth) {
105
+ return { accountId: auth.accountId, workspaceIds: auth.workspaceIds };
106
+ }
107
+ //# sourceMappingURL=PublicDirectoryController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublicDirectoryController.js","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicDirectoryController.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PublicDiscoveryController.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicDiscoveryController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAkB/C,wBAAgB,yBAAyB,IAAI,IAAI,CAAC,MAAM,CAAC,CAoDxD"}
1
+ {"version":3,"file":"PublicDiscoveryController.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicDiscoveryController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAkB/C,wBAAgB,yBAAyB,IAAI,IAAI,CAAC,MAAM,CAAC,CAgExD"}
@@ -26,10 +26,10 @@ export function publicDiscoveryController() {
26
26
  const gate = await authorize(c, getPublicIdentityContract.minScope);
27
27
  if ('fail' in gate)
28
28
  return refuse(c, gate.fail);
29
- const { keyId, accountId, workspaceId, scope, label, externalIdentity, createdAt } = gate.auth;
29
+ const { keyId, accountId, workspaceId, workspaceIds, scope, label, externalIdentity, createdAt, } = gate.auth;
30
30
  // Straight off the auth result: `authenticate` already loaded the row, so self-description
31
31
  // costs no read of its own.
32
- return c.json({ keyId, accountId, workspaceId, scope, label, externalIdentity, createdAt }, 200);
32
+ return c.json({ keyId, accountId, workspaceId, workspaceIds, scope, label, externalIdentity, createdAt }, 200);
33
33
  });
34
34
  buildHonoRoute(app, listPublicTaskTypesContract, async (c) => {
35
35
  // `read`, like every other discovery call: knowing what may be created is not creating it, and
@@ -1 +1 @@
1
- {"version":3,"file":"PublicDiscoveryController.js","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicDiscoveryController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAA;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAE3D,iGAAiG;AACjG,gEAAgE;AAChE,EAAE;AACF,mGAAmG;AACnG,mGAAmG;AACnG,mGAAmG;AACnG,kGAAkG;AAClG,qFAAqF;AACrF,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,4FAA4F;AAE5F,MAAM,UAAU,yBAAyB;IACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAU,CAAA;IAE9B,cAAc,CAAC,GAAG,EAAE,yBAAyB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACzD,8FAA8F;QAC9F,4FAA4F;QAC5F,6BAA6B;QAC7B,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,CAAC,EAAE,yBAAyB,CAAC,QAAQ,CAAC,CAAA;QACnE,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/C,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,IAAI,CAAA;QAC9F,2FAA2F;QAC3F,4BAA4B;QAC5B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,EAAE,GAAG,CAAC,CAAA;IAClG,CAAC,CAAC,CAAA;IAEF,cAAc,CAAC,GAAG,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC3D,+FAA+F;QAC/F,sEAAsE;QACtE,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,CAAC,EAAE,2BAA2B,CAAC,QAAQ,CAAC,CAAA;QACrE,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/C,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACpC,gGAAgG;QAChG,+FAA+F;QAC/F,+FAA+F;QAC/F,uFAAuF;QACvF,MAAM,UAAU,GAAG,MAAM,qBAAqB,CAC5C,SAAS,CAAC,oBAAoB,EAAE,OAAO,EACvC,IAAI,CAAC,IAAI,CAAC,WAAW,EACrB,SAAS,CAAC,MAAM,CACjB,CAAA;QACD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,qBAAqB,CAAC,SAAS,CAAC,gBAAgB,EAAE,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;IAClG,CAAC,CAAC,CAAA;IAEF,iGAAiG;IACjG,kGAAkG;IAClG,2FAA2F;IAC3F,gGAAgG;IAChG,8FAA8F;IAC9F,4EAA4E;IAC5E,EAAE;IACF,iGAAiG;IACjG,8FAA8F;IAC9F,4FAA4F;IAC5F,GAAG,CAAC,GAAG,CAAC,sBAAsB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1C,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;QACvC,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/C,6FAA6F;QAC7F,mFAAmF;QACnF,OAAO,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,EAAE,EAAE,cAAc,EAAE,iCAAiC,EAAE,CAAC,CAAA;IACzF,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,CAAA;AACZ,CAAC"}
1
+ {"version":3,"file":"PublicDiscoveryController.js","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicDiscoveryController.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PublicKeyController.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicKeyController.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAiD/C,wBAAgB,mBAAmB,IAAI,IAAI,CAAC,MAAM,CAAC,CA4DlD"}
1
+ {"version":3,"file":"PublicKeyController.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicKeyController.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAqD/C,wBAAgB,mBAAmB,IAAI,IAAI,CAAC,MAAM,CAAC,CA0ElD"}
@@ -2,7 +2,7 @@ import { createPublicKeyContract, listPublicKeysContract, revokePublicKeyContrac
2
2
  import { buildHonoRoute } from '@toad-contracts/hono';
3
3
  import { Hono } from 'hono';
4
4
  import { requireCapability } from '../../http/guards.js';
5
- import { publicApiKeyToWire } from './keyProjection.js';
5
+ import { assertReachCovers, assertWorkspacesInAccount, publicApiKeyToWire, } from './keyProjection.js';
6
6
  import { authorize, refuse } from './publicApiAuth.js';
7
7
  // HEADLESS key provisioning (`GET|POST|DELETE /api/v1/keys`): the external counterpart of the
8
8
  // session-authed `/workspaces/:ws/public-api-keys` routes, delegating to the SAME
@@ -51,20 +51,25 @@ export function publicKeyController() {
51
51
  const gate = await authorize(c, listPublicKeysContract.minScope);
52
52
  if ('fail' in gate)
53
53
  return refuse(c, gate.fail);
54
- const keys = keyStore(c);
55
- return c.json({ keys: (await keys.list(gate.auth.workspaceId)).map(publicApiKeyToWire) }, 200);
54
+ const { accountId, workspaceId } = gate.auth;
55
+ const keys = await keyStore(c).listReaching(accountId, workspaceId);
56
+ return c.json({ keys: keys.map((key) => publicApiKeyToWire(key, workspaceId)) }, 200);
56
57
  });
57
58
  buildHonoRoute(app, createPublicKeyContract, async (c) => {
58
59
  const gate = await authorize(c, createPublicKeyContract.minScope);
59
60
  if ('fail' in gate)
60
61
  return refuse(c, gate.fail);
61
62
  const keys = keyStore(c);
62
- const { label, scope, externalIdentity } = c.req.valid('json');
63
- // The account and workspace come from the KEY, never from the body: this surface mints into
64
- // the caller's own workspace and has no vocabulary for another one.
63
+ const { label, scope, externalIdentity, workspaceIds } = c.req.valid('json');
64
+ // The account comes from the KEY, never from the body. The reach defaults to the workspace this
65
+ // request acts on (what every mint produced before keys could span workspaces) and can never
66
+ // exceed the minting key's own.
67
+ const reach = workspaceIds === undefined ? [gate.auth.workspaceId] : workspaceIds;
68
+ assertReachCovers(gate.auth.workspaceIds, reach, 'mint a key with that reach');
69
+ await assertWorkspacesInAccount(c.get('container').workspaceService, gate.auth.accountId, reach);
65
70
  const { record, secret } = await keys.issue({
66
71
  accountId: gate.auth.accountId,
67
- workspaceId: gate.auth.workspaceId,
72
+ workspaceIds: reach,
68
73
  // No user minted this one. Attributing it to the human who minted the PARENT key would
69
74
  // be a guess dressed as provenance. The key is what acted, so the key is what is
70
75
  // recorded, and that is also what the revocation cascade follows.
@@ -82,21 +87,27 @@ export function publicKeyController() {
82
87
  // An integration that needs one asks the person to mint it.
83
88
  actsAsUserId: null,
84
89
  }, label, scope ?? DEFAULT_MINTED_SCOPE);
85
- return c.json({ key: publicApiKeyToWire(record), secret }, 201);
90
+ return c.json({ key: publicApiKeyToWire(record, gate.auth.workspaceId), secret }, 201);
86
91
  });
87
92
  buildHonoRoute(app, revokePublicKeyContract, async (c) => {
88
93
  const gate = await authorize(c, revokePublicKeyContract.minScope);
89
94
  if ('fail' in gate)
90
95
  return refuse(c, gate.fail);
91
- // Scoped to the caller's workspace by the service, and idempotent, so an unknown id is a 204
92
- // rather than a 404: this surface must not become an oracle for which key ids exist.
96
+ // Scoped to the caller's account, and idempotent, so an unknown id is a 204 rather than a 404:
97
+ // this surface must not become an oracle for which key ids exist. A key reaching workspaces
98
+ // the caller cannot is refused, so a restricted admin key cannot retire a wider credential.
93
99
  //
94
100
  // Revoking the CALLING key is allowed on purpose: a provisioning credential retiring itself at
95
101
  // the end of a run is the case, and the request is already authorized by the time it lands.
96
102
  // That key is always an app-minted `admin` one, since revoking needs the rung this surface
97
103
  // cannot mint. Self-revocation is the PROVISIONER standing down (taking everything it
98
104
  // handed out with it), never a provisioned key handing itself back.
99
- await keyStore(c).revoke(gate.auth.workspaceId, c.req.valid('param').keyId);
105
+ const keys = keyStore(c);
106
+ const target = await keys.getLive(gate.auth.accountId, c.req.valid('param').keyId);
107
+ if (target) {
108
+ assertReachCovers(gate.auth.workspaceIds, target.workspaceIds, 'revoke that key');
109
+ await keys.revoke(gate.auth.accountId, target.id);
110
+ }
100
111
  return c.body(null, 204);
101
112
  });
102
113
  return app;
@@ -1 +1 @@
1
- {"version":3,"file":"PublicKeyController.js","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicKeyController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PublicKeyController.js","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicKeyController.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Hono } from 'hono';
2
+ import type { AppEnv } from '../../http/env.js';
3
+ export declare function publicPrAssessmentController(): Hono<AppEnv>;
4
+ //# sourceMappingURL=PublicPrAssessmentController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublicPrAssessmentController.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicPrAssessmentController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAQ/C,wBAAgB,4BAA4B,IAAI,IAAI,CAAC,MAAM,CAAC,CAyB3D"}
@@ -0,0 +1,26 @@
1
+ import { assessPublicPullRequestContract } from '@cat-factory/contracts';
2
+ import { buildHonoRoute } from '@toad-contracts/hono';
3
+ import { Hono } from 'hono';
4
+ import { requireCapability } from '../../http/guards.js';
5
+ import { keyInitiatorRole } from '../../http/runAdmission.js';
6
+ import { authorize, refuse } from './publicApiAuth.js';
7
+ // The public PR assessment (`POST /api/v1/pr-assessments`): one synchronous model call that scores
8
+ // a pull request and says whether a risk policy would land it. Every rule lives in the service.
9
+ export function publicPrAssessmentController() {
10
+ const app = new Hono();
11
+ buildHonoRoute(app, assessPublicPullRequestContract, async (c) => {
12
+ const gate = await authorize(c, assessPublicPullRequestContract.minScope);
13
+ if ('fail' in gate)
14
+ return refuse(c, gate.fail);
15
+ // Same reason the risk-policy list answers with: the verdict is judged against that library.
16
+ const { service } = requireCapability(c.get('container').prAssessment, 'Risk policies are not configured', 'risk_policies_unwired');
17
+ // A key bound to a person reads the PR and calls the model as them, and is judged under their
18
+ // role as a run it starts would be; an unbound key runs on the workspace's credentials.
19
+ const { workspaceId, actsAsUserId } = gate.auth;
20
+ const role = await keyInitiatorRole(c.get('container'), workspaceId, actsAsUserId);
21
+ const assessment = await service.assess(workspaceId, { userId: actsAsUserId, role }, c.req.valid('json'));
22
+ return c.json(assessment, 200);
23
+ });
24
+ return app;
25
+ }
26
+ //# sourceMappingURL=PublicPrAssessmentController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublicPrAssessmentController.js","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicPrAssessmentController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEtD,mGAAmG;AACnG,gGAAgG;AAEhG,MAAM,UAAU,4BAA4B;IAC1C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAU,CAAA;IAE9B,cAAc,CAAC,GAAG,EAAE,+BAA+B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC/D,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,CAAC,EAAE,+BAA+B,CAAC,QAAQ,CAAC,CAAA;QACzE,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/C,6FAA6F;QAC7F,MAAM,EAAE,OAAO,EAAE,GAAG,iBAAiB,CACnC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,YAAY,EAC/B,kCAAkC,EAClC,uBAAuB,CACxB,CAAA;QACD,8FAA8F;QAC9F,wFAAwF;QACxF,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,IAAI,CAAA;QAC/C,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,CAAA;QAClF,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,MAAM,CACrC,WAAW,EACX,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,EAC9B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CACpB,CAAA;QACD,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;IAChC,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -55,7 +55,7 @@ export declare function requireScopedRun<E extends AppEnv>(c: Context<E>, worksp
55
55
  /** The error a gate rejected with, kept as DATA so each handler emits its own typed `c.json`. */
56
56
  export type GateFailure = {
57
57
  fail: {
58
- status: 401 | 403 | 404 | 428 | 503;
58
+ status: 401 | 403 | 404 | 422 | 428 | 503;
59
59
  code: string;
60
60
  message: string;
61
61
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../../src/modules/publicApi/decisions/scope.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../../src/modules/publicApi/decisions/scope.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,19 @@
1
- import type { PublicApiKey } from '@cat-factory/contracts';
2
- import type { PublicApiKeyRecord } from '@cat-factory/kernel';
1
+ import { type PublicApiKey } from '@cat-factory/contracts';
2
+ import { type PublicApiKeyRecord } from '@cat-factory/kernel';
3
3
  /**
4
- * Project a stored public-API key onto the secret-free wire resource.
4
+ * A key on the wire. `workspaceId` is the workspace the listing or mint was made for.
5
5
  *
6
- * ONE projection for both surfaces that expose a key: the session-authed management routes
7
- * (`PublicApiKeyController`) and the headless provisioning routes (`PublicKeyController`). They
8
- * had a copy each, which is how a field comes to be echoed on one and silently absent on the
9
- * other: `externalIdentity` is set by the headless mint and read on both, so a caller listing its
10
- * keys in the app would have seen every one of them claim to act for nobody.
11
- *
12
- * `secretHash` is the field this exists to leave behind. It is the only member of the record the
13
- * wire type has no place for, so an explicit projection (never a spread of the row) is what keeps
14
- * a future column from arriving on the wire by default.
6
+ * `secretHash` is the field this exists to leave behind, so it is an explicit projection and never
7
+ * a spread of the row: a future column must not arrive on the wire by default.
8
+ */
9
+ export declare function publicApiKeyToWire(record: PublicApiKeyRecord, workspaceId: string): PublicApiKey;
10
+ /**
11
+ * Refuse a requested reach naming any workspace outside `accountId`, in one batched read. A
12
+ * foreign board and a missing one answer the same 404, so the check cannot be used as a probe.
15
13
  */
16
- export declare function publicApiKeyToWire(record: PublicApiKeyRecord): PublicApiKey;
14
+ export declare function assertWorkspacesInAccount(workspaces: {
15
+ accountIdsOf(ids: string[]): Promise<Record<string, string | null>>;
16
+ }, accountId: string, workspaceIds: string[] | null): Promise<void>;
17
+ /** Refuse an operation on a reach the caller's own reach does not cover. */
18
+ export declare function assertReachCovers(holder: string[] | null, requested: string[] | null, action: string): void;
17
19
  //# sourceMappingURL=keyProjection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyProjection.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/keyProjection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAE7D;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAe3E"}
1
+ {"version":3,"file":"keyProjection.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/keyProjection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAEhG,OAAO,EAAiC,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAK5F;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAgBhG;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE;IAAE,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAA;CAAE,EACnF,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,GAC5B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EACvB,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAC1B,MAAM,EAAE,MAAM,GACb,IAAI,CAMN"}
@@ -1,21 +1,20 @@
1
+ import { PUBLIC_API_KEY_REACH_EXCEEDED_REASON } from '@cat-factory/contracts';
2
+ import { reachCovers } from '@cat-factory/integrations';
3
+ import { ForbiddenError, NotFoundError } from '@cat-factory/kernel';
4
+ // Shared by the session-authed key panel and the headless `/api/v1/keys` surface, so the two
5
+ // project a key and judge a requested reach identically.
1
6
  /**
2
- * Project a stored public-API key onto the secret-free wire resource.
7
+ * A key on the wire. `workspaceId` is the workspace the listing or mint was made for.
3
8
  *
4
- * ONE projection for both surfaces that expose a key: the session-authed management routes
5
- * (`PublicApiKeyController`) and the headless provisioning routes (`PublicKeyController`). They
6
- * had a copy each, which is how a field comes to be echoed on one and silently absent on the
7
- * other: `externalIdentity` is set by the headless mint and read on both, so a caller listing its
8
- * keys in the app would have seen every one of them claim to act for nobody.
9
- *
10
- * `secretHash` is the field this exists to leave behind. It is the only member of the record the
11
- * wire type has no place for, so an explicit projection (never a spread of the row) is what keeps
12
- * a future column from arriving on the wire by default.
9
+ * `secretHash` is the field this exists to leave behind, so it is an explicit projection and never
10
+ * a spread of the row: a future column must not arrive on the wire by default.
13
11
  */
14
- export function publicApiKeyToWire(record) {
12
+ export function publicApiKeyToWire(record, workspaceId) {
15
13
  return {
16
14
  id: record.id,
17
15
  accountId: record.accountId,
18
- workspaceId: record.workspaceId,
16
+ workspaceId,
17
+ workspaceIds: record.workspaceIds,
19
18
  label: record.label,
20
19
  scope: record.scope,
21
20
  createdByUserId: record.createdByUserId,
@@ -27,4 +26,25 @@ export function publicApiKeyToWire(record) {
27
26
  revokedAt: record.revokedAt,
28
27
  };
29
28
  }
29
+ /**
30
+ * Refuse a requested reach naming any workspace outside `accountId`, in one batched read. A
31
+ * foreign board and a missing one answer the same 404, so the check cannot be used as a probe.
32
+ */
33
+ export async function assertWorkspacesInAccount(workspaces, accountId, workspaceIds) {
34
+ if (workspaceIds === null)
35
+ return;
36
+ const owners = await workspaces.accountIdsOf(workspaceIds);
37
+ const foreign = workspaceIds.find((id) => owners[id] !== accountId);
38
+ if (foreign !== undefined) {
39
+ throw new NotFoundError('Workspace', foreign, { reason: 'workspace_not_found' });
40
+ }
41
+ }
42
+ /** Refuse an operation on a reach the caller's own reach does not cover. */
43
+ export function assertReachCovers(holder, requested, action) {
44
+ if (!reachCovers(holder, requested)) {
45
+ throw new ForbiddenError(`This key cannot ${action}: it reaches workspaces this key does not`, {
46
+ reason: PUBLIC_API_KEY_REACH_EXCEEDED_REASON,
47
+ });
48
+ }
49
+ }
30
50
  //# sourceMappingURL=keyProjection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyProjection.js","sourceRoot":"","sources":["../../../src/modules/publicApi/keyProjection.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"keyProjection.js","sourceRoot":"","sources":["../../../src/modules/publicApi/keyProjection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAqB,MAAM,wBAAwB,CAAA;AAChG,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,aAAa,EAA2B,MAAM,qBAAqB,CAAA;AAE5F,6FAA6F;AAC7F,yDAAyD;AAEzD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAA0B,EAAE,WAAmB;IAChF,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW;QACX,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,UAAmF,EACnF,SAAiB,EACjB,YAA6B;IAE7B,IAAI,YAAY,KAAK,IAAI;QAAE,OAAM;IACjC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,YAAY,CAAC,CAAA;IAC1D,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,CAAA;IACnE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAA;IAClF,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAC/B,MAAuB,EACvB,SAA0B,EAC1B,MAAc;IAEd,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,cAAc,CAAC,mBAAmB,MAAM,2CAA2C,EAAE;YAC7F,MAAM,EAAE,oCAAoC;SAC7C,CAAC,CAAA;IACJ,CAAC;AACH,CAAC"}