@nylorun/runtime 0.16.0-beta → 0.17.0-beta

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 (120) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +39 -27
  3. package/dist/admin-openapi.json +307 -7
  4. package/dist/api/ag-ui/endpoint.js +1 -2
  5. package/dist/api/ag-ui/routes.d.ts +1 -1
  6. package/dist/api/ag-ui/routes.js +10 -12
  7. package/dist/api/components.d.ts +54 -116
  8. package/dist/api/components.js +8 -11
  9. package/dist/api/http/app.js +2 -0
  10. package/dist/api/http/define.d.ts +9 -12
  11. package/dist/api/http/define.js +11 -26
  12. package/dist/api/http/routes/access.d.ts +3 -19
  13. package/dist/api/http/routes/access.js +12 -172
  14. package/dist/api/http/routes/actions.js +0 -1
  15. package/dist/api/http/routes/artifacts.js +1 -3
  16. package/dist/api/http/routes/me.d.ts +13 -0
  17. package/dist/api/http/routes/me.js +45 -0
  18. package/dist/api/http/routes/sandboxes.d.ts +2 -2
  19. package/dist/api/http/routes/sandboxes.js +1 -3
  20. package/dist/api/http/routes/sessions.js +6 -12
  21. package/dist/api/http/routes/vaults.d.ts +12 -3
  22. package/dist/api/http/routes/vaults.js +122 -54
  23. package/dist/api/openapi.js +4 -10
  24. package/dist/artifacts/links.js +6 -6
  25. package/dist/artifacts/service.js +1 -1
  26. package/dist/gates/mcp-handler.js +5 -1
  27. package/dist/gates/tenant-vaults.d.ts +18 -9
  28. package/dist/gates/tenant-vaults.js +17 -12
  29. package/dist/host/admin-api.d.ts +12 -5
  30. package/dist/host/admin-api.js +72 -6
  31. package/dist/host/app.d.ts +0 -1
  32. package/dist/host/app.js +13 -23
  33. package/dist/host/config-for.d.ts +4 -0
  34. package/dist/host/config-for.js +2 -0
  35. package/dist/host/config.d.ts +0 -6
  36. package/dist/host/create-host.d.ts +3 -10
  37. package/dist/host/create-host.js +4 -1
  38. package/dist/host/gates.js +6 -1
  39. package/dist/host/main.js +32 -10
  40. package/dist/host/stack-config.d.ts +11 -8
  41. package/dist/host/stack-config.js +23 -12
  42. package/dist/index.d.ts +3 -0
  43. package/dist/index.js +2 -0
  44. package/dist/keys/client.js +9 -1
  45. package/dist/keys/keys.d.ts +12 -2
  46. package/dist/keys/keys.js +8 -0
  47. package/dist/mcp/connect.js +2 -1
  48. package/dist/mcp/pool.d.ts +1 -0
  49. package/dist/mcp/pool.js +1 -0
  50. package/dist/openapi.json +2359 -4096
  51. package/dist/store/postgres/drizzle/0008_installation_vaults.sql +2 -0
  52. package/dist/store/postgres/drizzle/0009_oauth_pending.sql +19 -0
  53. package/dist/store/postgres/drizzle/0010_oss_auth_removals.sql +3 -0
  54. package/dist/store/postgres/drizzle/meta/_journal.json +21 -0
  55. package/dist/store/postgres/schema.d.ts +194 -170
  56. package/dist/store/postgres/schema.js +46 -28
  57. package/dist/store/postgres/store.js +51 -100
  58. package/dist/store/postgres/tenant.d.ts +1 -1
  59. package/dist/store/postgres/tenant.js +2 -2
  60. package/dist/store/types.d.ts +24 -30
  61. package/dist/streams/control.d.ts +0 -5
  62. package/dist/streams/control.js +0 -8
  63. package/dist/streams/types.d.ts +2 -11
  64. package/dist/streams/types.js +1 -1
  65. package/dist/tenant/auth.d.ts +13 -9
  66. package/dist/tenant/auth.js +25 -32
  67. package/dist/tenant/commands.js +3 -7
  68. package/dist/tenant/context.d.ts +20 -22
  69. package/dist/tenant/delivery-token.js +4 -4
  70. package/dist/tenant/effects.d.ts +5 -1
  71. package/dist/tenant/effects.js +5 -16
  72. package/dist/tenant/ephemeral.d.ts +14 -2
  73. package/dist/tenant/ephemeral.js +8 -1
  74. package/dist/tenant/host-token.js +5 -5
  75. package/dist/tenant/identity-file.d.ts +75 -0
  76. package/dist/tenant/identity-file.js +202 -0
  77. package/dist/tenant/issuers.d.ts +48 -0
  78. package/dist/tenant/issuers.js +311 -0
  79. package/dist/tenant/jwt.js +3 -3
  80. package/dist/tenant/operator-keys.d.ts +18 -0
  81. package/dist/tenant/operator-keys.js +55 -0
  82. package/dist/tenant/outbound.d.ts +30 -0
  83. package/dist/tenant/outbound.js +142 -10
  84. package/dist/tenant/principals.d.ts +2 -11
  85. package/dist/tenant/principals.js +10 -30
  86. package/dist/tenant/run-token.js +3 -3
  87. package/dist/tenant/runtime.d.ts +4 -0
  88. package/dist/tenant/runtime.js +14 -1
  89. package/dist/tenant/sandboxes.d.ts +5 -5
  90. package/dist/tenant/sandboxes.js +5 -5
  91. package/dist/tenant/session-streams.d.ts +6 -10
  92. package/dist/tenant/session-streams.js +9 -27
  93. package/dist/tenant/sessions.d.ts +2 -2
  94. package/dist/tenant/sessions.js +1 -1
  95. package/dist/tenant/signing-keys.d.ts +2 -1
  96. package/dist/tenant/signing-keys.js +2 -2
  97. package/dist/tenant/streams.d.ts +2 -3
  98. package/dist/tenant/streams.js +1 -5
  99. package/dist/tenant/types.d.ts +21 -0
  100. package/dist/vault/crypto.d.ts +7 -0
  101. package/dist/vault/crypto.js +16 -0
  102. package/dist/vault/oauth.d.ts +55 -0
  103. package/dist/vault/oauth.js +186 -0
  104. package/dist/vault/service.d.ts +54 -7
  105. package/dist/vault/service.js +261 -17
  106. package/dist/vault/sources.d.ts +54 -0
  107. package/dist/vault/sources.js +185 -0
  108. package/dist/version.d.ts +1 -1
  109. package/dist/version.js +1 -1
  110. package/package.json +4 -3
  111. package/dist/host/cors.d.ts +0 -27
  112. package/dist/host/cors.js +0 -64
  113. package/dist/tenant/access-policy.d.ts +0 -30
  114. package/dist/tenant/access-policy.js +0 -52
  115. package/dist/tenant/browser.d.ts +0 -15
  116. package/dist/tenant/browser.js +0 -39
  117. package/dist/tenant/subject-limits.d.ts +0 -16
  118. package/dist/tenant/subject-limits.js +0 -45
  119. package/dist/tenant/tokens.d.ts +0 -14
  120. package/dist/tenant/tokens.js +0 -187
@@ -1,14 +1,8 @@
1
1
  import { z } from "zod";
2
- import { newPublishableKey, newPublishableKeyId } from "@nylorun/core/compatibility";
3
- import { CreatePublishableKeyRequestSchema, CreateTokenRequestSchema, PutAccessPolicyRequestSchema, RevokeSigningKeyRequestSchema, RevokeSubjectRequestSchema, RotateSigningKeysRequestSchema, UpdatePublishableKeyRequestSchema, } from "@nylorun/core/contracts";
4
- import { AccessPolicyResponse, CreatePublishableKeyRequest, CreateTokenRequest, CreateTokenResponse, Jwks, ListPublishableKeysResponse, PublishableKey, PutAccessPolicyRequest, RevokeSigningKeyRequest, RevokeSubjectRequest, RevokeSubjectResponse, RotateSigningKeysRequest, SigningKeyList, SigningKeyView, UpdatePublishableKeyRequest, } from "../../components.js";
5
- import { signalSubjectRevoked } from "../../../streams/control.js";
6
- import { readPolicy, writePolicy } from "../../../tenant/access-policy.js";
7
- import { currentBasin, endSubjectStreams } from "../../../tenant/session-streams.js";
2
+ import { RevokeSigningKeyRequestSchema, RotateSigningKeysRequestSchema, TOKEN_TTL_MAX_SECONDS, } from "@nylorun/core/contracts";
3
+ import { Jwks, RevokeSigningKeyRequest, RotateSigningKeysRequest, SigningKeyList, SigningKeyView, } from "../../components.js";
8
4
  import { requireApplication } from "../../../tenant/auth.js";
9
- import { fail } from "../../../tenant/http.js";
10
5
  import { publicJwk, signingKeyView } from "../../../tenant/signing-keys.js";
11
- import { mintToken } from "../../../tenant/tokens.js";
12
6
  import { readJson } from "../body.js";
13
7
  import { tenantRoute } from "../define.js";
14
8
  import { jsonResponse } from "../respond.js";
@@ -21,21 +15,19 @@ const body = (schema) => ({
21
15
  required: true,
22
16
  content: { "application/json": { schema } },
23
17
  });
24
- const keyId = z.object({ keyId: z.string() });
25
18
  export function accessRoutes(api) {
26
19
  tenantRoute(api, {
27
- credentials: ["application", "subject", "token", "publishable"],
20
+ credentials: ["application", "subject", "token"],
28
21
  scopes: "any",
29
- browser: true,
30
22
  // Public keys: an Action endpoint verifies delivery tokens with them and holds no key.
31
23
  anonymous: true,
32
24
  }, {
33
25
  method: "get",
34
26
  path: "/v1/access/jwks",
35
27
  tags: ["Access"],
36
- summary: "Get the public keys subject and delivery tokens are signed with",
37
- description: "A JSON Web Key Set, to verify a subject token or a delivery token without calling the " +
38
- "Runtime. No credential is needed.",
28
+ summary: "Get the public keys delivery tokens are signed with",
29
+ description: "A JSON Web Key Set, to verify a delivery token without calling the Runtime. No " +
30
+ "credential is needed.",
39
31
  responses: { 200: json(Jwks, "The public keys") },
40
32
  }, async (c) => {
41
33
  // Anonymous: reads the public keys. Only when the current or standby key is missing does
@@ -49,41 +41,6 @@ export function accessRoutes(api) {
49
41
  }
50
42
  return jsonResponse(200, { keys: rows.map(publicJwk) });
51
43
  });
52
- tenantRoute(api, APPLICATION, {
53
- method: "post",
54
- path: "/v1/tokens",
55
- tags: ["Access"],
56
- summary: "Mint a subject token",
57
- description: "A token for one person and one role of the access policy, for their browser or app to call the Runtime with. At most 15 minutes.",
58
- request: { body: body(CreateTokenRequest) },
59
- responses: { 200: json(CreateTokenResponse, "The token, its scopes and expiry") },
60
- }, async (c) => {
61
- requireApplication(c.get("scope"));
62
- return jsonResponse(200, await mintToken(c.env.tenant, CreateTokenRequestSchema.parse(await readJson(c.req.raw))));
63
- });
64
- tenantRoute(api, APPLICATION, {
65
- method: "get",
66
- path: "/v1/access/policy",
67
- tags: ["Access"],
68
- summary: "Get the access policy",
69
- responses: { 200: json(AccessPolicyResponse, "Roles, the anonymous grant and token lifetime") },
70
- }, async (c) => {
71
- requireApplication(c.get("scope"));
72
- return jsonResponse(200, { policy: await c.env.tenant.store.tx((t) => readPolicy(t)) });
73
- });
74
- tenantRoute(api, APPLICATION, {
75
- method: "put",
76
- path: "/v1/access/policy",
77
- tags: ["Access"],
78
- summary: "Set the access policy",
79
- request: { body: body(PutAccessPolicyRequest) },
80
- responses: { 200: json(AccessPolicyResponse, "The policy now in force") },
81
- }, async (c) => {
82
- requireApplication(c.get("scope"));
83
- const request = PutAccessPolicyRequestSchema.parse(await readJson(c.req.raw));
84
- await c.env.tenant.store.tx((t) => writePolicy(t, request.policy));
85
- return jsonResponse(200, { policy: request.policy });
86
- });
87
44
  tenantRoute(api, APPLICATION, {
88
45
  method: "get",
89
46
  path: "/v1/access/signing-keys",
@@ -106,7 +63,7 @@ export function accessRoutes(api) {
106
63
  path: "/v1/access/signing-keys/rotate",
107
64
  tags: ["Access"],
108
65
  summary: "Rotate the signing keys",
109
- description: "The standby key signs from now on; the current one still verifies tokens it signed. `force` also ends every outstanding token.",
66
+ description: "The standby key signs from now on; the current one still verifies tokens it signed. `force` also ends every outstanding delivery token and capability link.",
110
67
  request: { body: body(RotateSigningKeysRequest) },
111
68
  responses: {
112
69
  200: json(SigningKeyList, "The keys after rotation"),
@@ -116,8 +73,11 @@ export function accessRoutes(api) {
116
73
  requireApplication(c.get("scope"));
117
74
  const ctx = c.env.tenant;
118
75
  const request = RotateSigningKeysRequestSchema.parse(await readJson(c.req.raw));
119
- const maxTtlSeconds = await ctx.store.tx(async (t) => (await readPolicy(t)).tokens.maxTtlSeconds);
120
- const keys = await ctx.keys.rotateSigningKeys({ maxTtlSeconds, force: request.force === true });
76
+ // The longest any token the Runtime signs lives (delivery tokens, capability links).
77
+ const keys = await ctx.keys.rotateSigningKeys({
78
+ maxTtlSeconds: TOKEN_TTL_MAX_SECONDS,
79
+ force: request.force === true,
80
+ });
121
81
  ctx.config.logger.info("signing keys rotated", { force: request.force === true });
122
82
  return jsonResponse(200, { keys });
123
83
  });
@@ -139,126 +99,6 @@ export function accessRoutes(api) {
139
99
  ctx.config.logger.info("signing key revoked", { kid: row.id });
140
100
  return jsonResponse(200, signingKeyView(row));
141
101
  });
142
- tenantRoute(api, APPLICATION, {
143
- method: "get",
144
- path: "/v1/access/publishable-keys",
145
- tags: ["Access"],
146
- summary: "List publishable keys",
147
- responses: { 200: json(ListPublishableKeysResponse, "The keys, revoked ones included") },
148
- }, async (c) => {
149
- requireApplication(c.get("scope"));
150
- return jsonResponse(200, {
151
- keys: (await c.env.tenant.store.tx((t) => t.publishableKeys())).map(publishableKeyView),
152
- });
153
- });
154
- tenantRoute(api, APPLICATION, {
155
- method: "post",
156
- path: "/v1/access/publishable-keys",
157
- tags: ["Access"],
158
- summary: "Create a publishable key",
159
- description: "A key a browser page on one of the origins sends as `Nylorun-Key`. Public by design; it grants the policy's anonymous scopes.",
160
- request: { body: body(CreatePublishableKeyRequest) },
161
- responses: {
162
- 200: json(PublishableKey, "The key"),
163
- 409: { description: "A key with this name exists" },
164
- },
165
- }, async (c) => {
166
- requireApplication(c.get("scope"));
167
- const ctx = c.env.tenant;
168
- const request = CreatePublishableKeyRequestSchema.parse(await readJson(c.req.raw));
169
- const row = {
170
- id: newPublishableKeyId(),
171
- key: newPublishableKey(ctx.config.tenantId),
172
- name: request.name,
173
- originsJson: JSON.stringify(request.origins),
174
- createdAt: new Date().toISOString(),
175
- revokedAt: null,
176
- };
177
- await ctx.store.tx(async (t) => {
178
- if ((await t.publishableKeys()).some((other) => other.name === request.name))
179
- fail(409, `A publishable key named ${request.name} exists`);
180
- await t.insertPublishableKey(row);
181
- });
182
- ctx.config.logger.info("publishable key created", { keyId: row.id });
183
- return jsonResponse(200, publishableKeyView(row));
184
- });
185
- for (const method of ["put", "delete"])
186
- tenantRoute(api, APPLICATION, {
187
- method,
188
- path: "/v1/access/publishable-keys/{keyId}",
189
- tags: ["Access"],
190
- summary: method === "put" ? "Set a publishable key's origins" : "Revoke a publishable key",
191
- request: {
192
- params: keyId,
193
- ...(method === "put" ? { body: body(UpdatePublishableKeyRequest) } : {}),
194
- },
195
- responses: {
196
- 200: json(PublishableKey, method === "put" ? "The key" : "The key, revoked (again: unchanged)"),
197
- },
198
- }, async (c) => {
199
- requireApplication(c.get("scope"));
200
- const ctx = c.env.tenant;
201
- const id = c.req.param("keyId");
202
- const patch = method === "put"
203
- ? {
204
- originsJson: JSON.stringify(UpdatePublishableKeyRequestSchema.parse(await readJson(c.req.raw)).origins),
205
- }
206
- : { revokedAt: new Date().toISOString() };
207
- const row = await ctx.store.tx(async (t) => {
208
- const existing = (await t.publishableKey(id)) ?? fail(404, "Publishable key not found");
209
- // Revoking twice keeps the first time.
210
- if (method === "delete" && existing.revokedAt !== null)
211
- return existing;
212
- await t.updatePublishableKey(id, patch);
213
- return { ...existing, ...patch };
214
- });
215
- ctx.config.logger.info(method === "put" ? "publishable key updated" : "publishable key revoked", { keyId: row.id });
216
- return jsonResponse(200, publishableKeyView(row));
217
- });
218
- tenantRoute(api, APPLICATION, {
219
- method: "post",
220
- path: "/v1/access/revocations",
221
- tags: ["Access"],
222
- summary: "Revoke a subject",
223
- description: "Ends every token minted for the subject so far, and their open streams on every process. Turns in progress continue.",
224
- request: { body: body(RevokeSubjectRequest) },
225
- responses: { 200: json(RevokeSubjectResponse, "The subject's new epoch") },
226
- }, async (c) => {
227
- requireApplication(c.get("scope"));
228
- const request = RevokeSubjectRequestSchema.parse(await readJson(c.req.raw));
229
- return jsonResponse(200, await revokeSubject(c.env.tenant, request.subject));
230
- });
231
- }
232
- /** A publishable key as the API shows it. */
233
- export function publishableKeyView(row) {
234
- return {
235
- id: row.id,
236
- name: row.name,
237
- key: row.key,
238
- origins: JSON.parse(row.originsJson),
239
- createdAt: row.createdAt,
240
- revokedAt: row.revokedAt,
241
- };
242
- }
243
- /**
244
- * Ends every token of `subject` minted so far: bumps its epoch, then ends its open streams
245
- * here and, through `tenant/control`, on every other process. Running turns continue.
246
- */
247
- export async function revokeSubject(ctx, subject) {
248
- const epoch = await ctx.store.tx(async (t) => {
249
- const next = await t.bumpSubjectEpoch(subject, new Date().toISOString());
250
- t.afterCommit(async () => {
251
- endSubjectStreams(ctx.sessionStreams, subject, next);
252
- const streams = ctx.sessionStreams.wiring?.streams;
253
- if (streams)
254
- await signalSubjectRevoked(streams, currentBasin(ctx), subject, next).catch((error) => ctx.config.logger.warn("subject revocation signal failed", {
255
- message: error instanceof Error ? error.message : String(error),
256
- }));
257
- });
258
- return next;
259
- });
260
- ctx.config.logger.info("subject revoked", { epoch });
261
- return { subject, epoch };
262
102
  }
263
103
  /** True when the Tenant has a current and a standby signing key. */
264
104
  function complete(rows) {
@@ -57,7 +57,6 @@ export function actionRoutes(api) {
57
57
  tenantRoute(api, {
58
58
  credentials: ["application", "subject", "token"],
59
59
  scopes: ["sessions:own"],
60
- browser: true,
61
60
  }, {
62
61
  method: "post",
63
62
  path: "/v1/sessions/{sessionId}/commands",
@@ -32,15 +32,13 @@ import { jsonResponse } from "../respond.js";
32
32
  const OWN = {
33
33
  credentials: ["application", "subject", "token"],
34
34
  scopes: ["sessions:own"],
35
- browser: true,
36
35
  };
37
36
  /** An upload: the same callers, with the file's bytes as the body. */
38
37
  const UPLOAD = { ...OWN, bytes: true };
39
38
  /** A capability link: no credential, no protocol header; the token in the path is the grant. */
40
39
  const LINK = {
41
- credentials: ["application", "subject", "token", "publishable"],
40
+ credentials: ["application", "subject", "token"],
42
41
  scopes: "any",
43
- browser: true,
44
42
  anonymous: true,
45
43
  unversioned: true,
46
44
  };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `GET /v1/me` (Host feature `trusted-issuers`): who the Runtime takes the caller to be, for any
3
+ * credential that acts on the Tenant. It shows what a trusted issuer's token renders to (its
4
+ * subject, scopes, agents and sandbox grants), so an operator can check an identity file, and
5
+ * Studio can check a forwarded token for the `studio` scope.
6
+ */
7
+ import type { OpenAPIHono } from "@hono/zod-openapi";
8
+ import { type MeResponse as MeBody } from "@nylorun/core/contracts";
9
+ import type { AuthScope } from "../../../tenant/context.js";
10
+ import type { TenantEnv } from "../app.js";
11
+ /** The caller as `GET /v1/me` reports it. */
12
+ export declare function describeCaller(scope: AuthScope): MeBody;
13
+ export declare function meRoutes(api: OpenAPIHono<TenantEnv>): void;
@@ -0,0 +1,45 @@
1
+ import { CALLER_SCOPES, SUBJECT_SCOPES, } from "@nylorun/core/contracts";
2
+ import { MeResponse } from "../../components.js";
3
+ import { fail } from "../../../tenant/http.js";
4
+ import { tenantRoute } from "../define.js";
5
+ import { jsonResponse } from "../respond.js";
6
+ function ordered(scopes) {
7
+ const held = new Set(scopes);
8
+ return CALLER_SCOPES.filter((scope) => held.has(scope));
9
+ }
10
+ /** The caller as `GET /v1/me` reports it. */
11
+ export function describeCaller(scope) {
12
+ switch (scope.kind) {
13
+ case "application":
14
+ return { scopes: [...SUBJECT_SCOPES], agents: "*", via: `application:${scope.principalId}` };
15
+ case "subject":
16
+ return { subject: scope.subject, scopes: ordered(scope.scopes), agents: "*", via: "subject" };
17
+ case "token":
18
+ return {
19
+ subject: scope.subject,
20
+ scopes: ordered(scope.scopes),
21
+ agents: scope.agents === "*" ? "*" : [...scope.agents].sort(),
22
+ sandboxes: [...(scope.sandboxes ?? [])],
23
+ via: `issuer:${scope.issuer}`,
24
+ };
25
+ default:
26
+ return fail(403, "This credential acts on no Tenant resource");
27
+ }
28
+ }
29
+ export function meRoutes(api) {
30
+ tenantRoute(api, { credentials: ["application", "subject", "token"], scopes: "any" }, {
31
+ method: "get",
32
+ path: "/v1/me",
33
+ tags: ["Access"],
34
+ summary: "Get who the caller is",
35
+ description: "The subject, scopes, agents and sandbox grants of the credential that sends it: an " +
36
+ "application key (alone or acting for a subject), or a trusted issuer's token " +
37
+ "(`via: issuer:<name>`).",
38
+ responses: {
39
+ 200: {
40
+ description: "The caller",
41
+ content: { "application/json": { schema: MeResponse } },
42
+ },
43
+ },
44
+ }, (c) => jsonResponse(200, describeCaller(c.get("scope"))));
45
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Sandboxes as a resource (`/v1/sandboxes/**`, Host feature `sandboxes`): create or find one by
3
3
  * id, read it, list them by label, read its lifecycle stream, and delete it. Ids may hold `/`,
4
- * sent percent-encoded as one path segment (`team-a%2Fproj-42`). A subject token reaches only
5
- * the ids its `sbx` grants match, and changes them only with `sandboxes:write`.
4
+ * sent percent-encoded as one path segment (`team-a%2Fproj-42`). A token caller reaches only
5
+ * the ids its sandbox grants match, and changes them only with `sandboxes:write`.
6
6
  */
7
7
  import type { OpenAPIHono } from "@hono/zod-openapi";
8
8
  import type { TenantEnv } from "../app.js";
@@ -10,13 +10,11 @@ import { jsonResponse } from "../respond.js";
10
10
  const READ = {
11
11
  credentials: ["application", "subject", "token"],
12
12
  scopes: ["sessions:own", "sandboxes:write"],
13
- browser: true,
14
13
  };
15
14
  /** Creating or deleting one. */
16
15
  const WRITE = {
17
16
  credentials: ["application", "subject", "token"],
18
17
  scopes: ["sandboxes:write"],
19
- browser: true,
20
18
  };
21
19
  const json = (schema, description) => ({
22
20
  description,
@@ -50,7 +48,7 @@ export function sandboxRoutes(api) {
50
48
  path: "/v1/sandboxes",
51
49
  tags: ["Sandboxes"],
52
50
  summary: "List sandboxes",
53
- description: "Every sandbox with all the labels asked for. A subject token sees only the sandboxes its grants reach.",
51
+ description: "Every sandbox with all the labels asked for. A token caller sees only the sandboxes its grants reach.",
54
52
  request: {
55
53
  query: z.object({
56
54
  label: z
@@ -8,7 +8,7 @@ import { accessOf, requireApplication } from "../../../tenant/auth.js";
8
8
  import { loadSession, sessionOf } from "../../../tenant/context.js";
9
9
  import { fail } from "../../../tenant/http.js";
10
10
  import { sandboxGrantsOf } from "../../../tenant/sandboxes.js";
11
- import { readHistory, requestCursor, streamSessionEvents } from "../../../tenant/session-streams.js";
11
+ import { readHistory, requestCursor, streamHolderOf, streamSessionEvents } from "../../../tenant/session-streams.js";
12
12
  import { listAgentsPublic, listDefinitions, listSessions, putDefinition, putSession, sessionView, } from "../../../tenant/sessions.js";
13
13
  import { readJson } from "../body.js";
14
14
  import { tenantRoute } from "../define.js";
@@ -16,7 +16,6 @@ import { jsonResponse } from "../respond.js";
16
16
  const OWN_SESSIONS = {
17
17
  credentials: ["application", "subject", "token"],
18
18
  scopes: ["sessions:own"],
19
- browser: true,
20
19
  };
21
20
  const json = (schema, description) => ({
22
21
  description,
@@ -47,23 +46,20 @@ async function sessionBelow(ctx, incoming, id, access) {
47
46
  }
48
47
  export function sessionRoutes(api) {
49
48
  tenantRoute(api, {
50
- credentials: ["application", "subject", "token", "publishable"],
49
+ credentials: ["application", "subject", "token"],
51
50
  scopes: ["agents:read", "agents:write"],
52
- browser: true,
53
51
  }, {
54
52
  method: "get",
55
53
  path: "/v1/agents",
56
54
  tags: ["Agents"],
57
55
  summary: "List agents",
58
- description: "With an application key, every definition, manifest included. With a subject token or a publishable key, only the agents it may use, by name.",
56
+ description: "With an application key, every definition, manifest included. With a trusted issuer's token, only the agents it may use, by name.",
59
57
  responses: {
60
58
  200: json(z.union([ListAgentsResponse, ListPublicAgentsResponse]), "The agents"),
61
59
  },
62
60
  }, async (c) => {
63
61
  const scope = c.get("scope");
64
62
  const ctx = c.env.tenant;
65
- if (scope.kind === "publishable")
66
- return jsonResponse(200, await listAgentsPublic(ctx, scope.agents));
67
63
  return jsonResponse(200, scope.kind === "token"
68
64
  ? await listAgentsPublic(ctx, scope.agents)
69
65
  : await listDefinitions(ctx));
@@ -107,7 +103,7 @@ export function sessionRoutes(api) {
107
103
  const request = PutSessionRequestSchema.parse(await readJson(c.req.raw));
108
104
  // Agent code may trust `info`: only an app server sets it.
109
105
  if (scope.kind === "token" && request.info !== undefined)
110
- fail(403, "A subject token cannot set session info", { code: "scope_required" });
106
+ fail(403, "A token caller cannot set session info", { code: "scope_required" });
111
107
  const grants = sandboxGrantsOf(scope);
112
108
  const session = await putSession(ctx, c.req.param("sessionId"), request, accessOf(scope), grants === undefined ? {} : { sandboxGrants: grants });
113
109
  return jsonResponse(200, await ctx.store.tx((t) => sessionView(t, session)));
@@ -152,7 +148,7 @@ export function sessionRoutes(api) {
152
148
  path: "/v1/sessions/{sessionId}/events",
153
149
  tags: ["Sessions"],
154
150
  summary: "Follow a session's events",
155
- description: "Server-sent events, each with its type as `event` and its cursor as `id`, from the cursor on. Events are on the `nylorun.event/2` envelope, typed by `SessionEvent`; a client ignores a type it does not know. A subject token's stream ends with `event: nylorun.closed` when the token expires or the subject is revoked. A `: keepalive` comment every 15 seconds.",
151
+ description: "Server-sent events, each with its type as `event` and its cursor as `id`, from the cursor on. Events are on the `nylorun.event/2` envelope, typed by `SessionEvent`; a client ignores a type it does not know. A trusted issuer's token's stream ends with `event: nylorun.closed` when the token expires. A `: keepalive` comment every 15 seconds.",
156
152
  request: { params: sessionId, query: resume },
157
153
  responses: {
158
154
  200: {
@@ -170,9 +166,7 @@ export function sessionRoutes(api) {
170
166
  const { incoming, outgoing } = c.env;
171
167
  const id = c.req.param("sessionId");
172
168
  const cursor = await sessionBelow(ctx, incoming, id, accessOf(scope));
173
- await streamSessionEvents(ctx, incoming, outgoing, id, cursor, scope.kind === "token"
174
- ? { subject: scope.subject, epoch: scope.epoch, expiresAt: scope.expiresAt }
175
- : undefined);
169
+ await streamSessionEvents(ctx, incoming, outgoing, id, cursor, streamHolderOf(scope));
176
170
  return RESPONSE_ALREADY_SENT;
177
171
  });
178
172
  tenantRoute(api, { credentials: ["application"], scopes: "never" }, {
@@ -1,8 +1,17 @@
1
1
  /**
2
- * Vaults and their credentials (`/v1/vaults/**`): secrets a session's tools use, owned by one
3
- * person. Acting for a subject reaches only the subject's own vaults, and never on another's
4
- * behalf.
2
+ * Vaults and their credentials (`/v1/vaults/**`): secrets a session's tools use. Only an
3
+ * application key acting for no one (an operator key, Studio) reaches these routes (protocol 7):
4
+ * a request acting for a subject, or a trusted issuer's token, is `403 scope_required`. The
5
+ * installation's own vaults (`scope: "installation"`) attach to any session; a vault of one
6
+ * person (`ownerUserId`) attaches only to that person's sessions. A person's own credentials
7
+ * come from the operator's credential resolver (`vault/sources.ts`), not from these routes.
8
+ *
9
+ * MCP OAuth connect (F9 C2): an application key starts one into an installation vault
10
+ * (`/oauth/start`); the authorization server sends the browser back to the anonymous, unversioned
11
+ * `GET /v1/oauth/callback`. Both only route: the gateway's keys module does the OAuth (F9-D14).
5
12
  */
6
13
  import type { OpenAPIHono } from "@hono/zod-openapi";
7
14
  import type { TenantEnv } from "../app.js";
15
+ /** Where the authorization server sends the browser back. */
16
+ export declare const OAUTH_CALLBACK_PATH = "/v1/oauth/callback";
8
17
  export declare function vaultRoutes(api: OpenAPIHono<TenantEnv>): void;