@fungi.computer/oyster 0.6.0 → 0.8.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 (163) hide show
  1. package/README.md +133 -78
  2. package/dist/agent-archive-projection.d.ts +8 -0
  3. package/dist/agent-archive-projection.js +49 -0
  4. package/dist/agent-schedules.d.ts +61 -0
  5. package/dist/agent-schedules.js +19 -0
  6. package/dist/agents.d.ts +35 -14
  7. package/dist/agents.js +93 -144
  8. package/dist/app-builds.d.ts +7 -2
  9. package/dist/app-builds.js +19 -2
  10. package/dist/approval-capability.d.ts +1 -1
  11. package/dist/approval-capability.js +11 -16
  12. package/dist/approval-home.d.ts +2 -1
  13. package/dist/approval-home.js +5 -3
  14. package/dist/apps/catalog.js +1 -1
  15. package/dist/apps/gate.d.ts +4 -7
  16. package/dist/apps/gate.js +14 -29
  17. package/dist/apps/index.d.ts +1 -1
  18. package/dist/apps/installation.d.ts +4 -42
  19. package/dist/apps/installation.js +7 -75
  20. package/dist/apps/operations.js +0 -10
  21. package/dist/apps/permissions.d.ts +18 -8
  22. package/dist/apps/permissions.js +7 -1
  23. package/dist/auth.d.ts +1 -24
  24. package/dist/auth.js +9 -39
  25. package/dist/authorization.d.ts +307 -0
  26. package/dist/authorization.js +542 -0
  27. package/dist/boot-host-script.js +1 -1
  28. package/dist/build/build-jobs.d.ts +2 -2
  29. package/dist/build/candidate-builder.d.ts +2 -0
  30. package/dist/build/candidate-builder.js +5 -1
  31. package/dist/build/candidate-cli.js +3 -0
  32. package/dist/build/candidate-cli.mjs +70 -22
  33. package/dist/build/job-log.d.ts +23 -0
  34. package/dist/build/job-log.js +88 -0
  35. package/dist/build/local-build.d.ts +9 -1
  36. package/dist/build/local-build.js +11 -9
  37. package/dist/cloudflare.d.ts +4 -1
  38. package/dist/cloudflare.js +1 -1
  39. package/dist/files.js +9 -8
  40. package/dist/git-admission.d.ts +3 -3
  41. package/dist/git-admission.js +4 -6
  42. package/dist/git-door.d.ts +8 -7
  43. package/dist/git-door.js +18 -17
  44. package/dist/git.d.ts +1 -1
  45. package/dist/host.d.ts +33 -40
  46. package/dist/host.js +156 -275
  47. package/dist/installation-execution.js +14 -5
  48. package/dist/installation-operations.d.ts +4 -180
  49. package/dist/installation-operations.js +1 -5
  50. package/dist/installation-store-generation.d.ts +4 -1
  51. package/dist/installation-store-generation.js +59 -6
  52. package/dist/internal/delivery.js +0 -1
  53. package/dist/internal/host-gate.d.ts +3 -2
  54. package/dist/internal/host-gate.js +5 -18
  55. package/dist/internal/invocation-timing.d.ts +16 -0
  56. package/dist/internal/invocation-timing.js +23 -0
  57. package/dist/internal/registry-installations.js +1 -0
  58. package/dist/invocation.d.ts +15 -14
  59. package/dist/invocation.js +58 -26
  60. package/dist/keys.d.ts +2 -19
  61. package/dist/keys.js +1 -37
  62. package/dist/mcp.d.ts +15 -27
  63. package/dist/mcp.js +105 -155
  64. package/dist/page/agent-browser.d.ts +15 -0
  65. package/dist/page/agent-browser.js +162 -0
  66. package/dist/page/app-permissions-port.d.ts +3 -0
  67. package/dist/page/app-permissions-port.js +43 -0
  68. package/dist/page/app-permissions.d.ts +11 -0
  69. package/dist/page/app-permissions.js +296 -0
  70. package/dist/page/isolated-page-theme.d.ts +2 -0
  71. package/dist/page/isolated-page-theme.js +11 -0
  72. package/dist/page/mount.d.ts +2 -13
  73. package/dist/page/mount.js +7 -93
  74. package/dist/page-script.d.ts +0 -3
  75. package/dist/page-script.js +3 -4
  76. package/dist/standalone/agent-archive-owner.d.ts +42 -0
  77. package/dist/standalone/agent-archive-owner.js +116 -0
  78. package/dist/standalone/agent-archive-schedules.d.ts +6 -0
  79. package/dist/standalone/agent-archive-schedules.js +76 -0
  80. package/dist/standalone/agent-archive.d.ts +31 -0
  81. package/dist/standalone/agent-archive.js +67 -0
  82. package/dist/standalone/agent-computers.d.ts +7 -6
  83. package/dist/standalone/agent-computers.js +4 -7
  84. package/dist/standalone/agent-mail.d.ts +2 -2
  85. package/dist/standalone/agents.d.ts +186 -20
  86. package/dist/standalone/agents.js +251 -30
  87. package/dist/standalone/app-builds-binding.js +1 -1
  88. package/dist/standalone/app-set.js +2 -54
  89. package/dist/standalone/app-source.d.ts +3 -4
  90. package/dist/standalone/app-source.js +12 -9
  91. package/dist/standalone/builds.d.ts +2 -14
  92. package/dist/standalone/builds.js +15 -13
  93. package/dist/standalone/computer-fleet.d.ts +4 -4
  94. package/dist/standalone/computer-fleet.js +13 -42
  95. package/dist/standalone/computer.d.ts +4 -3
  96. package/dist/standalone/computer.js +52 -53
  97. package/dist/standalone/dashboard-approval.d.ts +10 -0
  98. package/dist/standalone/dashboard-approval.js +66 -0
  99. package/dist/standalone/machine-link.d.ts +4 -221
  100. package/dist/standalone/machine-link.js +21 -15
  101. package/dist/standalone/media.d.ts +1 -0
  102. package/dist/standalone/media.js +20 -14
  103. package/dist/standalone/owner-routes.d.ts +16 -0
  104. package/dist/standalone/owner-routes.js +21 -0
  105. package/dist/standalone/permission-resources.d.ts +5 -0
  106. package/dist/standalone/permission-resources.js +31 -0
  107. package/dist/standalone/registry.d.ts +7 -0
  108. package/dist/standalone/registry.js +3 -0
  109. package/dist/standalone/tenancy.d.ts +0 -3
  110. package/dist/standalone/tenancy.js +2 -5
  111. package/dist/standalone/worker-routes.d.ts +19 -0
  112. package/dist/standalone/worker-routes.js +9 -0
  113. package/dist/standalone/worker.d.ts +1 -1
  114. package/dist/standalone/worker.js +27 -35
  115. package/dist/standalone/workflow-binding.d.ts +10 -3
  116. package/dist/standalone/workflow-binding.js +22 -5
  117. package/dist/standalone/workflow-git-capture.js +2 -1
  118. package/dist/standalone/workflow-host.d.ts +8 -103
  119. package/dist/standalone/workflow-host.js +1 -1
  120. package/dist/standalone/workflow-node-capture.js +2 -2
  121. package/dist/standalone/workflow-root-authority.d.ts +1 -1
  122. package/dist/standalone/workflow-root-authority.js +1 -1
  123. package/dist/standalone/workflow-schedules.d.ts +31 -4
  124. package/dist/standalone/workflow-schedules.js +18 -5
  125. package/dist/standalone/workflow-webhooks.d.ts +4 -18
  126. package/dist/standalone/workflow-webhooks.js +5 -5
  127. package/dist/standalone/workflows.d.ts +53 -27
  128. package/dist/standalone/workflows.js +9 -3
  129. package/dist/storage-frame.d.ts +6 -6
  130. package/dist/testing/capabilities.d.ts +7 -6
  131. package/dist/testing/capabilities.js +3 -6
  132. package/dist/testing/installation.d.ts +0 -1
  133. package/dist/testing/installation.js +2 -5
  134. package/dist/workers-git.d.ts +12 -4
  135. package/dist/workers-git.js +38 -35
  136. package/dist/workers.d.ts +9 -1
  137. package/dist/workers.js +44 -8
  138. package/dist/workflows.d.ts +4 -0
  139. package/dist/workflows.js +2 -0
  140. package/docs/api/README.md +7 -0
  141. package/docs/api/agent-schedules.md +176 -0
  142. package/docs/api/agents.md +34 -13
  143. package/docs/api/app-storage.md +1 -1
  144. package/docs/api/apps.md +323 -225
  145. package/docs/api/auth.md +0 -55
  146. package/docs/api/authorization.md +1300 -0
  147. package/docs/api/build.md +42 -38
  148. package/docs/api/cloudflare.md +3 -3
  149. package/docs/api/git.md +20 -11
  150. package/docs/api/host.md +64 -192
  151. package/docs/api/index.md +1 -1
  152. package/docs/api/invocation.md +14 -14
  153. package/docs/api/keys.md +1 -98
  154. package/docs/api/mcp.md +43 -40
  155. package/docs/api/modules.md +2 -0
  156. package/docs/api/page/mount.md +16 -19
  157. package/docs/api/workers-git.md +46 -12
  158. package/docs/api/workers.md +111 -222
  159. package/package.json +41 -22
  160. package/dist/page/computer-target.d.ts +0 -3
  161. package/dist/page/computer-target.js +0 -9
  162. package/dist/standalone/channel-session-door.d.ts +0 -4
  163. package/dist/standalone/channel-session-door.js +0 -72
@@ -33,6 +33,10 @@ export async function executeInstallation(host, input) {
33
33
  !("role" in principal) ||
34
34
  principal.role !== "owner")
35
35
  return refused(403, "install_authority_unavailable");
36
+ // A Workflow can publish a build and update an approved installation, but
37
+ // creating an installation always needs the owner's direct confirmation.
38
+ if (command.admission.origin !== "owner-direct" && command.baseline === null)
39
+ return refused(409, "needs_consent");
36
40
  if ((await host.installations.generation()) !== command.generation)
37
41
  return refused(409, "stale_authority");
38
42
  let verified;
@@ -49,12 +53,10 @@ export async function executeInstallation(host, input) {
49
53
  const declaration = verified.declaration ?? emptyAppDeclaration;
50
54
  const baseline = command.baseline;
51
55
  const declaredOperations = await deriveDeclaredOperations(verified.release, declaration, host.capabilities ?? []);
52
- // A Workflow reuses the owner's approval; any capability or anonymous
53
- // method its baseline has not approved waits for the owner.
56
+ // A Workflow reuses the owner's approval; any capability its baseline
57
+ // has not approved waits for the owner.
54
58
  if (command.admission.origin === "workflow" &&
55
- (pendingCapabilities(baseline?.authority ?? { approvedGrants: [] }, declaration, declaredOperations).length > 0 ||
56
- declaredOperations.anonymousMethods.some((method) => !(baseline?.approvedAnonymousMethods ?? []).some((approved) => approved.name === method.name &&
57
- approved.digest === method.digest))))
59
+ pendingCapabilities(baseline?.authority ?? { approvedGrants: [] }, declaration, declaredOperations).length > 0)
58
60
  return refused(409, "needs_consent");
59
61
  const reduced = applyInstallationCommand(baseline, baseline === null
60
62
  ? {
@@ -77,6 +79,13 @@ export async function executeInstallation(host, input) {
77
79
  installationId: command.installationId,
78
80
  instanceId: command.instanceId,
79
81
  },
82
+ // Only a fresh owner-direct Tourtois install in the configured home
83
+ // slot receives the ordinary persisted Shell grant. App/workflow
84
+ // requests cannot opt in; selection only intersects existing grants.
85
+ builtInShell: baseline === null &&
86
+ command.admission.origin === "owner-direct" &&
87
+ verified.release.appId === "app_tourtois" &&
88
+ command.installationId === host.homeInstallationId,
80
89
  });
81
90
  if (Result.isFailure(reduced))
82
91
  return refused(409, reduced.failure.code);
@@ -60,28 +60,6 @@ declare const installationOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
60
60
  disabled: "disabled";
61
61
  deleted: "deleted";
62
62
  }>;
63
- visibility: z.ZodEnum<{
64
- private: "private";
65
- public: "public";
66
- }>;
67
- approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
68
- name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
69
- digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
70
- }, z.core.$strict>>>>;
71
- anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
72
- by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
73
- kind: import("zod/mini").ZodMiniLiteral<"member">;
74
- memberId: import("zod/mini").ZodMiniString<string>;
75
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
76
- kind: import("zod/mini").ZodMiniLiteral<"agent">;
77
- agentId: import("zod/mini").ZodMiniString<string>;
78
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
79
- kind: import("zod/mini").ZodMiniLiteral<"team-key">;
80
- keyId: import("zod/mini").ZodMiniString<string>;
81
- }, z.core.$strip>], "kind">;
82
- at: z.ZodNumber;
83
- authorityVersion: z.ZodLazy<z.ZodNumber>;
84
- }, z.core.$strict>>>>;
85
63
  }, z.core.$strict>;
86
64
  creation: z.ZodEnum<{
87
65
  fresh: "fresh";
@@ -300,28 +278,6 @@ export declare const installationOperationSchema: z.ZodDiscriminatedUnion<[z.Zod
300
278
  disabled: "disabled";
301
279
  deleted: "deleted";
302
280
  }>;
303
- visibility: z.ZodEnum<{
304
- private: "private";
305
- public: "public";
306
- }>;
307
- approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
308
- name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
309
- digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
310
- }, z.core.$strict>>>>;
311
- anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
312
- by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
313
- kind: import("zod/mini").ZodMiniLiteral<"member">;
314
- memberId: import("zod/mini").ZodMiniString<string>;
315
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
316
- kind: import("zod/mini").ZodMiniLiteral<"agent">;
317
- agentId: import("zod/mini").ZodMiniString<string>;
318
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
319
- kind: import("zod/mini").ZodMiniLiteral<"team-key">;
320
- keyId: import("zod/mini").ZodMiniString<string>;
321
- }, z.core.$strip>], "kind">;
322
- at: z.ZodNumber;
323
- authorityVersion: z.ZodLazy<z.ZodNumber>;
324
- }, z.core.$strict>>>>;
325
281
  }, z.core.$strict>>;
326
282
  }, z.core.$strict>, z.ZodObject<{
327
283
  state: z.ZodLiteral<"refused">;
@@ -400,8 +356,8 @@ declare const installationAdmissionSchema: z.ZodObject<{
400
356
  "create-only": "create-only";
401
357
  }>;
402
358
  origin: z.ZodEnum<{
403
- workflow: "workflow";
404
359
  "owner-direct": "owner-direct";
360
+ workflow: "workflow";
405
361
  }>;
406
362
  }, z.core.$strict>;
407
363
  export type InstallationAdmission = z.output<typeof installationAdmissionSchema>;
@@ -439,8 +395,8 @@ export declare const retainedInstallationCommandSchema: z.ZodObject<{
439
395
  "create-only": "create-only";
440
396
  }>;
441
397
  origin: z.ZodEnum<{
442
- workflow: "workflow";
443
398
  "owner-direct": "owner-direct";
399
+ workflow: "workflow";
444
400
  }>;
445
401
  }, z.core.$strict>;
446
402
  scope: z.ZodObject<{
@@ -503,28 +459,6 @@ export declare const retainedInstallationCommandSchema: z.ZodObject<{
503
459
  disabled: "disabled";
504
460
  deleted: "deleted";
505
461
  }>;
506
- visibility: z.ZodEnum<{
507
- private: "private";
508
- public: "public";
509
- }>;
510
- approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
511
- name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
512
- digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
513
- }, z.core.$strict>>>>;
514
- anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
515
- by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
516
- kind: import("zod/mini").ZodMiniLiteral<"member">;
517
- memberId: import("zod/mini").ZodMiniString<string>;
518
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
519
- kind: import("zod/mini").ZodMiniLiteral<"agent">;
520
- agentId: import("zod/mini").ZodMiniString<string>;
521
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
522
- kind: import("zod/mini").ZodMiniLiteral<"team-key">;
523
- keyId: import("zod/mini").ZodMiniString<string>;
524
- }, z.core.$strip>], "kind">;
525
- at: z.ZodNumber;
526
- authorityVersion: z.ZodLazy<z.ZodNumber>;
527
- }, z.core.$strict>>>>;
528
462
  }, z.core.$strict>>;
529
463
  acceptedAt: z.ZodNumber;
530
464
  creation: z.ZodEnum<{
@@ -586,28 +520,6 @@ export declare const retainedInstallationCommandSchema: z.ZodObject<{
586
520
  disabled: "disabled";
587
521
  deleted: "deleted";
588
522
  }>;
589
- visibility: z.ZodEnum<{
590
- private: "private";
591
- public: "public";
592
- }>;
593
- approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
594
- name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
595
- digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
596
- }, z.core.$strict>>>>;
597
- anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
598
- by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
599
- kind: import("zod/mini").ZodMiniLiteral<"member">;
600
- memberId: import("zod/mini").ZodMiniString<string>;
601
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
602
- kind: import("zod/mini").ZodMiniLiteral<"agent">;
603
- agentId: import("zod/mini").ZodMiniString<string>;
604
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
605
- kind: import("zod/mini").ZodMiniLiteral<"team-key">;
606
- keyId: import("zod/mini").ZodMiniString<string>;
607
- }, z.core.$strip>], "kind">;
608
- at: z.ZodNumber;
609
- authorityVersion: z.ZodLazy<z.ZodNumber>;
610
- }, z.core.$strict>>>>;
611
523
  }, z.core.$strict>;
612
524
  hasBackend: z.ZodBoolean;
613
525
  prepared: z.ZodBoolean;
@@ -731,8 +643,8 @@ declare const installationOperationRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
731
643
  "create-only": "create-only";
732
644
  }>;
733
645
  origin: z.ZodEnum<{
734
- workflow: "workflow";
735
646
  "owner-direct": "owner-direct";
647
+ workflow: "workflow";
736
648
  }>;
737
649
  }, z.core.$strict>;
738
650
  }, z.core.$strict>, z.ZodObject<{
@@ -810,28 +722,6 @@ declare const installationOperationRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
810
722
  disabled: "disabled";
811
723
  deleted: "deleted";
812
724
  }>;
813
- visibility: z.ZodEnum<{
814
- private: "private";
815
- public: "public";
816
- }>;
817
- approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
818
- name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
819
- digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
820
- }, z.core.$strict>>>>;
821
- anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
822
- by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
823
- kind: import("zod/mini").ZodMiniLiteral<"member">;
824
- memberId: import("zod/mini").ZodMiniString<string>;
825
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
826
- kind: import("zod/mini").ZodMiniLiteral<"agent">;
827
- agentId: import("zod/mini").ZodMiniString<string>;
828
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
829
- kind: import("zod/mini").ZodMiniLiteral<"team-key">;
830
- keyId: import("zod/mini").ZodMiniString<string>;
831
- }, z.core.$strip>], "kind">;
832
- at: z.ZodNumber;
833
- authorityVersion: z.ZodLazy<z.ZodNumber>;
834
- }, z.core.$strict>>>>;
835
725
  }, z.core.$strict>;
836
726
  hasBackend: z.ZodBoolean;
837
727
  }, z.core.$strict>, z.ZodObject<{
@@ -1052,28 +942,6 @@ declare const installationOperationReplySchema: z.ZodDiscriminatedUnion<[z.ZodOb
1052
942
  disabled: "disabled";
1053
943
  deleted: "deleted";
1054
944
  }>;
1055
- visibility: z.ZodEnum<{
1056
- private: "private";
1057
- public: "public";
1058
- }>;
1059
- approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
1060
- name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
1061
- digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
1062
- }, z.core.$strict>>>>;
1063
- anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
1064
- by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
1065
- kind: import("zod/mini").ZodMiniLiteral<"member">;
1066
- memberId: import("zod/mini").ZodMiniString<string>;
1067
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
1068
- kind: import("zod/mini").ZodMiniLiteral<"agent">;
1069
- agentId: import("zod/mini").ZodMiniString<string>;
1070
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
1071
- kind: import("zod/mini").ZodMiniLiteral<"team-key">;
1072
- keyId: import("zod/mini").ZodMiniString<string>;
1073
- }, z.core.$strip>], "kind">;
1074
- at: z.ZodNumber;
1075
- authorityVersion: z.ZodLazy<z.ZodNumber>;
1076
- }, z.core.$strict>>>>;
1077
945
  }, z.core.$strict>>;
1078
946
  }, z.core.$strict>, z.ZodObject<{
1079
947
  state: z.ZodLiteral<"refused">;
@@ -1154,8 +1022,8 @@ declare const installationOperationReplySchema: z.ZodDiscriminatedUnion<[z.ZodOb
1154
1022
  "create-only": "create-only";
1155
1023
  }>;
1156
1024
  origin: z.ZodEnum<{
1157
- workflow: "workflow";
1158
1025
  "owner-direct": "owner-direct";
1026
+ workflow: "workflow";
1159
1027
  }>;
1160
1028
  }, z.core.$strict>;
1161
1029
  scope: z.ZodObject<{
@@ -1218,28 +1086,6 @@ declare const installationOperationReplySchema: z.ZodDiscriminatedUnion<[z.ZodOb
1218
1086
  disabled: "disabled";
1219
1087
  deleted: "deleted";
1220
1088
  }>;
1221
- visibility: z.ZodEnum<{
1222
- private: "private";
1223
- public: "public";
1224
- }>;
1225
- approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
1226
- name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
1227
- digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
1228
- }, z.core.$strict>>>>;
1229
- anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
1230
- by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
1231
- kind: import("zod/mini").ZodMiniLiteral<"member">;
1232
- memberId: import("zod/mini").ZodMiniString<string>;
1233
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
1234
- kind: import("zod/mini").ZodMiniLiteral<"agent">;
1235
- agentId: import("zod/mini").ZodMiniString<string>;
1236
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
1237
- kind: import("zod/mini").ZodMiniLiteral<"team-key">;
1238
- keyId: import("zod/mini").ZodMiniString<string>;
1239
- }, z.core.$strip>], "kind">;
1240
- at: z.ZodNumber;
1241
- authorityVersion: z.ZodLazy<z.ZodNumber>;
1242
- }, z.core.$strict>>>>;
1243
1089
  }, z.core.$strict>>;
1244
1090
  acceptedAt: z.ZodNumber;
1245
1091
  creation: z.ZodEnum<{
@@ -1301,28 +1147,6 @@ declare const installationOperationReplySchema: z.ZodDiscriminatedUnion<[z.ZodOb
1301
1147
  disabled: "disabled";
1302
1148
  deleted: "deleted";
1303
1149
  }>;
1304
- visibility: z.ZodEnum<{
1305
- private: "private";
1306
- public: "public";
1307
- }>;
1308
- approvedAnonymousMethods: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
1309
- name: z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>;
1310
- digest: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("@fungi.computer/puffball").Sha256Digest, unknown>>;
1311
- }, z.core.$strict>>>>;
1312
- anonymousApproval: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
1313
- by: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
1314
- kind: import("zod/mini").ZodMiniLiteral<"member">;
1315
- memberId: import("zod/mini").ZodMiniString<string>;
1316
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
1317
- kind: import("zod/mini").ZodMiniLiteral<"agent">;
1318
- agentId: import("zod/mini").ZodMiniString<string>;
1319
- }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
1320
- kind: import("zod/mini").ZodMiniLiteral<"team-key">;
1321
- keyId: import("zod/mini").ZodMiniString<string>;
1322
- }, z.core.$strip>], "kind">;
1323
- at: z.ZodNumber;
1324
- authorityVersion: z.ZodLazy<z.ZodNumber>;
1325
- }, z.core.$strict>>>>;
1326
1150
  }, z.core.$strict>;
1327
1151
  hasBackend: z.ZodBoolean;
1328
1152
  prepared: z.ZodBoolean;
@@ -296,15 +296,11 @@ const version = (record) => record?.authority.authorityVersion ?? 0;
296
296
  /** Whether a plan only reuses, or narrows, the approval its baseline holds. */
297
297
  function reusesApproval(baseline, next) {
298
298
  const grants = baseline?.authority.approvedGrants ?? [];
299
- const methods = baseline?.approvedAnonymousMethods ?? [];
300
299
  return (next.authority.approvedGrants.every((grant) => grants.some((held) => held.capability === grant.capability &&
301
300
  same(held.resources, grant.resources) &&
302
301
  grant.operations.every((operation) => held.operations.includes(operation)))) &&
303
302
  (next.authority.approval === null ||
304
- same(next.authority.approval, baseline?.authority.approval ?? null)) &&
305
- next.approvedAnonymousMethods.every((method) => methods.some((held) => held.name === method.name && held.digest === method.digest)) &&
306
- (next.anonymousApproval === null ||
307
- same(next.anonymousApproval, baseline?.anonymousApproval ?? null)));
303
+ same(next.authority.approval, baseline?.authority.approval ?? null)));
308
304
  }
309
305
  /**
310
306
  * One registry-owned command journal joined to maintained Watchdog transactions.
@@ -1,3 +1,6 @@
1
1
  import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
2
2
  /** Own one persisted store incarnation, independent of installation row revisions. */
3
- export declare function createInstallationStoreGeneration(sql: WatchdogSqliteOwner["sql"]): () => string;
3
+ export declare function createInstallationStoreGeneration(owner: WatchdogSqliteOwner): {
4
+ generation: () => string;
5
+ resetAt: () => number | null;
6
+ };
@@ -1,16 +1,69 @@
1
1
  import * as Schema from "effect/Schema";
2
2
  import { defineIdFamily } from "@fungi.computer/cairn/id";
3
+ import { installationsFromOlderOyster } from "./older-oyster-store.js";
3
4
  const installationStoreIds = defineIdFamily({
4
5
  family: "installation-store",
5
6
  version: 1,
6
7
  kinds: { generation: { prefix: "store" } },
7
8
  });
8
9
  const generationRow = Schema.decodeUnknownSync(Schema.Struct({ generation: Schema.NonEmptyString }));
10
+ const resetRow = Schema.decodeUnknownSync(Schema.Struct({ reset_at: Schema.NullOr(Schema.Int) }));
11
+ const columnRow = Schema.decodeUnknownSync(Schema.Struct({ name: Schema.String }));
12
+ const countRow = Schema.decodeUnknownSync(Schema.Struct({ count: Schema.Int }));
13
+ /** Recognize only the retired producer's keys, never general decode failures. */
14
+ function preOwnerOnlyInstallations(sql) {
15
+ if (sql
16
+ .exec("SELECT 1 FROM sqlite_master WHERE type='table' AND name='installations'")
17
+ .toArray().length === 0)
18
+ return false;
19
+ // Missing authority belongs to the existing pre-0.3 refusal, not this reset.
20
+ if (installationsFromOlderOyster(sql))
21
+ return false;
22
+ return (sql
23
+ .exec(`SELECT 1 FROM installations WHERE json_valid(record) AND (
24
+ json_type(record, '$.visibility') IS NOT NULL OR
25
+ json_type(record, '$.approvedAnonymousMethods') IS NOT NULL OR
26
+ json_type(record, '$.anonymousApproval') IS NOT NULL
27
+ ) LIMIT 1`)
28
+ .toArray().length > 0);
29
+ }
9
30
  /** Own one persisted store incarnation, independent of installation row revisions. */
10
- export function createInstallationStoreGeneration(sql) {
11
- sql.exec("CREATE TABLE IF NOT EXISTS installation_store (id INTEGER PRIMARY KEY CHECK (id=1), generation TEXT NOT NULL) STRICT");
12
- sql.exec("INSERT OR IGNORE INTO installation_store (id,generation) VALUES(1,?)", installationStoreIds.generation.generate());
13
- return () => generationRow(sql
14
- .exec("SELECT generation FROM installation_store WHERE id=1")
15
- .toArray()[0]).generation;
31
+ export function createInstallationStoreGeneration(owner) {
32
+ const { sql } = owner;
33
+ const needsReset = preOwnerOnlyInstallations(sql);
34
+ const resetAt = () => resetRow(sql
35
+ .exec("SELECT reset_at FROM installation_store WHERE id=1")
36
+ .toArray()[0]).reset_at;
37
+ owner.transactionSync(() => {
38
+ sql.exec("CREATE TABLE IF NOT EXISTS installation_store (id INTEGER PRIMARY KEY CHECK (id=1), generation TEXT NOT NULL, reset_at INTEGER, reset_rows INTEGER) STRICT");
39
+ // Enroll the existing incarnation singleton in its current audit shape.
40
+ const columns = sql
41
+ .exec("PRAGMA table_info(installation_store)")
42
+ .toArray()
43
+ .map((row) => columnRow(row));
44
+ if (!columns.some(({ name }) => name === "reset_at"))
45
+ sql.exec("ALTER TABLE installation_store ADD COLUMN reset_at INTEGER");
46
+ if (!columns.some(({ name }) => name === "reset_rows"))
47
+ sql.exec("ALTER TABLE installation_store ADD COLUMN reset_rows INTEGER");
48
+ sql.exec("INSERT OR IGNORE INTO installation_store (id,generation) VALUES(1,?)", installationStoreIds.generation.generate());
49
+ if (!needsReset || resetAt() !== null || !preOwnerOnlyInstallations(sql))
50
+ return;
51
+ const rows = countRow(sql.exec("SELECT COUNT(*) AS count FROM installations").toArray()[0]).count;
52
+ // any row keyed by an installationId that is read as authority must be
53
+ // cleared in the same transaction. Other owners and Watchdog job identities
54
+ // remain intact; their existing missing-command handling settles orphan work.
55
+ sql.exec("DELETE FROM installations");
56
+ if (sql
57
+ .exec("SELECT 1 FROM sqlite_master WHERE type='table' AND name='installation_commands'")
58
+ .toArray().length > 0)
59
+ sql.exec("DELETE FROM installation_commands");
60
+ sql.exec("UPDATE installation_store SET generation=? WHERE id=1", installationStoreIds.generation.generate());
61
+ sql.exec("UPDATE installation_store SET reset_at=?, reset_rows=? WHERE id=1", Date.now(), rows);
62
+ });
63
+ return {
64
+ generation: () => generationRow(sql
65
+ .exec("SELECT generation FROM installation_store WHERE id=1")
66
+ .toArray()[0]).generation,
67
+ resetAt,
68
+ };
16
69
  }
@@ -92,7 +92,6 @@ export async function deliverBackground(host, raw) {
92
92
  case "not_declared":
93
93
  case "approval_required":
94
94
  case "invalid_input":
95
- case "visitor_read_only":
96
95
  return { kind: "refused", code: "denied" };
97
96
  }
98
97
  }
@@ -1,8 +1,9 @@
1
1
  /** Release verification and gate facts shared by the HTTP host and its delivery engines. */
2
+ import type { TenantPrincipal } from "../tenant.js";
2
3
  import type { Release } from "@fungi.computer/puffball";
3
4
  import type { GateFacts } from "../apps/gate.js";
4
5
  import type { Installation } from "../apps/installation.js";
5
- import type { OysterHost, OysterPrincipal } from "../host.js";
6
+ import type { OysterHost } from "../host.js";
6
7
  import { type ArtifactStore, type VerifiedRelease } from "../release.js";
7
8
  /**
8
9
  * Verify each Release once per owner (an isolate's HTTP host, or the
@@ -13,4 +14,4 @@ import { type ArtifactStore, type VerifiedRelease } from "../release.js";
13
14
  */
14
15
  export declare function releaseCache(artifacts: ArtifactStore, limit?: number): (release: Release) => Promise<VerifiedRelease>;
15
16
  /** The gate facts one principal presents to one installation. */
16
- export declare function hostGateFacts(host: OysterHost, installation: Installation, release: VerifiedRelease, principal: OysterPrincipal): Promise<GateFacts>;
17
+ export declare function hostGateFacts(host: OysterHost, installation: Installation, release: VerifiedRelease, principal: TenantPrincipal): Promise<GateFacts>;
@@ -1,6 +1,6 @@
1
1
  import { emptyAppDeclaration } from "@fungi.computer/puffball/app";
2
2
  import { deriveDeclaredOperations } from "../apps/operations.js";
3
- import { keyAppAccess } from "../keys.js";
3
+ import { appToolAccess, authorityOf, hostAuthorityPorts, } from "../authorization.js";
4
4
  import { defaultAssetPolicy, verifyRelease, } from "../release.js";
5
5
  /** Verified Releases one cache keeps; the least recently added leaves first. */
6
6
  const releaseCacheLimit = 256;
@@ -31,30 +31,17 @@ export function releaseCache(artifacts, limit = releaseCacheLimit) {
31
31
  return entry;
32
32
  };
33
33
  }
34
- /**
35
- * Only Agents and keys hold tool policy; members and visitors never ask. An
36
- * Agent keeps its one blanket policy; a key's access is its own grants for
37
- * this one installation -- `null` when its live key is unknown, revoked,
38
- * expired or suspended, or simply does not reach this installation, which
39
- * then refuses `not_approved`, the same as an Agent with no approval.
40
- */
41
- async function hostToolPolicy(host, actor, installationId) {
42
- if (actor.kind === "agent")
43
- return (await host.appToolPolicy?.(actor)) ?? null;
44
- if (actor.kind !== "team-key")
45
- return null;
46
- const key = (await host.keys?.live(actor.keyId)) ?? null;
47
- return key === null ? null : keyAppAccess(key, installationId);
48
- }
49
34
  /** The gate facts one principal presents to one installation. */
50
35
  export async function hostGateFacts(host, installation, release, principal) {
51
36
  const { actor } = principal;
52
37
  const declaration = release.declaration ?? emptyAppDeclaration;
53
- const toolPolicy = await hostToolPolicy(host, actor, installation.installationId);
38
+ // Only Agents and keys hold tool policy (`authorization.ts`); `null`
39
+ // refuses `not_approved`, the same as an Agent with no approval.
40
+ const ports = hostAuthorityPorts(host);
41
+ const toolPolicy = await appToolAccess(await authorityOf(principal, ports), installation.installationId, ports);
54
42
  return {
55
43
  installation: {
56
44
  status: installation.status,
57
- visibility: installation.visibility,
58
45
  instanceId: installation.instanceId,
59
46
  authority: installation.authority,
60
47
  artifactDigest: installation.release.artifact.artifactDigest,
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Name where a slow App invocation waited, as the Instance saw it: verifying
3
+ * its backend, waiting for the frame permit, running the guest, and the guest's
4
+ * binding packets inside that run. Durable Object wall time otherwise hides
5
+ * which of these a caller's seconds went to.
6
+ */
7
+ export declare function reportSlowInvocation(name: string, host: Readonly<{
8
+ bindingWaits: {
9
+ count: number;
10
+ ms: number;
11
+ };
12
+ }>, at: Readonly<{
13
+ entered: number;
14
+ verified: number;
15
+ admitted: number;
16
+ }>, now?: () => number): void;
@@ -0,0 +1,23 @@
1
+ /** Stage timing for slow App invocations, logged by the Instance that ran them. */
2
+ /** Invocations slower than this log their stages; faster ones log nothing. */
3
+ const slowInvocationMs = 200;
4
+ /**
5
+ * Name where a slow App invocation waited, as the Instance saw it: verifying
6
+ * its backend, waiting for the frame permit, running the guest, and the guest's
7
+ * binding packets inside that run. Durable Object wall time otherwise hides
8
+ * which of these a caller's seconds went to.
9
+ */
10
+ export function reportSlowInvocation(name, host, at, now = Date.now) {
11
+ const finished = now();
12
+ if (finished - at.entered < slowInvocationMs)
13
+ return;
14
+ console.log("Slow App invocation", {
15
+ name,
16
+ totalMs: finished - at.entered,
17
+ verifyMs: at.verified - at.entered,
18
+ permitMs: at.admitted - at.verified,
19
+ runMs: finished - at.admitted,
20
+ bindingCalls: host.bindingWaits.count,
21
+ bindingMs: host.bindingWaits.ms,
22
+ });
23
+ }
@@ -5,6 +5,7 @@ export function registryInstallations(env, scope) {
5
5
  return {
6
6
  operations: installationOperationsPort((request) => registry().installationOperation(request), { tenantId: scope.tenantId, origin: scope.origin }),
7
7
  generation: async () => registry().generation(),
8
+ resetAt: async () => registry().installationResetAt(),
8
9
  list: async () => registry().list(),
9
10
  get: async (id) => registry().get(id),
10
11
  compareAndPut: async (installation, expectedAuthorityVersion, expectedGeneration) => await registry().compareAndPut(installation, expectedAuthorityVersion, expectedGeneration, scope.tenantId),
@@ -1,4 +1,5 @@
1
- import type { OysterHost, OysterPrincipal } from "./host.js";
1
+ import type { TenantPrincipal } from "./tenant.js";
2
+ import type { OysterHost } from "./host.js";
2
3
  import type { VerifiedRelease } from "./release.js";
3
4
  import type { Installation } from "./apps/installation.js";
4
5
  import { type InvocationGrants } from "./apps/gate.js";
@@ -191,9 +192,9 @@ export type BackgroundFrameState = {
191
192
  export declare function assertBackgroundBinding(frame: InvocationFrame<BackgroundFrameState>, binding: AppBackendBindingName): void;
192
193
  /** Consume this frame's one origin check before awaiting verification, then retain only its decision. */
193
194
  export declare function admitBackgroundFrame(key: CryptoKey, frame: InvocationFrame<BackgroundFrameState>, raw: unknown): Promise<ClaimedJobAdmission>;
194
- /** The host's native guest-abort and authority-close hooks. */
195
+ /** The host's native guest-abort and frame-authority-close hooks. */
195
196
  export type InvocationGatePorts<Host> = Readonly<{
196
- /** Stop the guest when live authority closes early (revocation, expiry). */
197
+ /** Stop the shared guest at cohort drain or revocation, before the last close. */
197
198
  abortGuest: () => Promise<void>;
198
199
  /** Release the host resources belonging to this completed or revoked frame. */
199
200
  close: (frame: InvocationFrame<Host>) => Promise<void>;
@@ -202,7 +203,7 @@ export type InvocationGatePorts<Host> = Readonly<{
202
203
  }>;
203
204
  /** One invocation owner shared by Hub shards and standalone Instances. */
204
205
  export type InvocationGate<Host> = Readonly<{
205
- /** Run work within one immutable frame and close its authority afterward. */
206
+ /** Run one immutable frame; verified foreground reads may share until drain. */
206
207
  run: <A>(facts: InvocationFacts, host: Host, work: (frame: InvocationFrame<Host>) => Promise<A>) => Promise<A>;
207
208
  /** Resolve one live frame by its id, refusing a closed or unknown frame. */
208
209
  active: (frameId: string) => InvocationFrame<Host>;
@@ -216,12 +217,13 @@ export type InvocationGate<Host> = Readonly<{
216
217
  /** Decode only identities minted from the invocation-frame ID family. */
217
218
  export declare const invocationFrameIdSchema: Schema.String;
218
219
  /**
219
- * Admit frames against one guest. Verified foreground reads share it, up to
220
- * eight at once; writes, raw backend requests, Gills and replay run alone. Every guest binding packet names its frame, so a finished
221
- * frame's leftover timers cannot borrow a later frame's authority and a
222
- * normal finish keeps the guest warm. Closing authority early (revocation,
223
- * expiry, a write preempting replay) aborts the guest and so every frame
224
- * sharing it; replay only yields to a verified write.
220
+ * One Instance shares up to eight verified foreground reads; all other work
221
+ * runs alone in first-come order. Every finish fences its frame; the last live
222
+ * frame stops the native guest before closing authority and releasing admission.
223
+ * Binding checks alone cannot stop a timer mutating the facet's own SQLite.
224
+ * Revocation, expiry and a verified write preempting replay stop every live frame.
225
+ * Arrivals wait for a stopping guest. Once the opening deadline passes, new
226
+ * reads queue exclusively so the current cohort cannot be prolonged forever.
225
227
  */
226
228
  export declare function createInvocationGate<Host>(ports: InvocationGatePorts<Host>): InvocationGate<Host>;
227
229
  /** Verified selection, artifact and binding grants before caller attribution. */
@@ -233,11 +235,10 @@ export type InvocationDeadline = Readonly<{
233
235
  }>;
234
236
  /**
235
237
  * Project an admitted principal into App attribution; only the host chooses
236
- * the fixed Computer context. The App caller grammar has no visitor actor, so
237
- * the anonymous public reader is attributed as the reserved `visitor` key
238
- * without a Computer. Host admission reads the principal, never this projection.
238
+ * the fixed Computer context. Host admission reads the principal, never this
239
+ * projection.
239
240
  */
240
- export declare function invocationCallerContext(principal: OysterPrincipal, computerId: string | null): AppMethodCallContext;
241
+ export declare function invocationCallerContext(principal: TenantPrincipal, computerId: string | null): AppMethodCallContext;
241
242
  /** Shared host-held caller/Workflow fact builders, never guest argument decoders. */
242
243
  export declare const invocationFacts: {
243
244
  /** Combine host-admitted caller facts with the invocation and grant deadlines. */