@smthrs/kernel 0.0.0-stage → 1.0.0-rc.3

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 (267) hide show
  1. package/CHANGELOG.md +206 -0
  2. package/LICENSE +21 -0
  3. package/README.md +154 -2
  4. package/dist/cjs/CapabilitySet.d.ts +6 -0
  5. package/dist/cjs/CapabilitySet.d.ts.map +1 -0
  6. package/dist/cjs/CapabilitySet.js +19 -0
  7. package/dist/cjs/CapabilitySet.js.map +7 -0
  8. package/dist/cjs/ChildProcessEnvironment.d.ts +60 -0
  9. package/dist/cjs/ChildProcessEnvironment.d.ts.map +1 -0
  10. package/dist/cjs/ChildProcessEnvironment.js +61 -0
  11. package/dist/cjs/ChildProcessEnvironment.js.map +7 -0
  12. package/dist/cjs/ChildProcessSpawner.d.ts +100 -0
  13. package/dist/cjs/ChildProcessSpawner.d.ts.map +1 -0
  14. package/dist/cjs/ChildProcessSpawner.js +291 -0
  15. package/dist/cjs/ChildProcessSpawner.js.map +7 -0
  16. package/dist/cjs/CommandLine.d.ts +173 -0
  17. package/dist/cjs/CommandLine.d.ts.map +1 -0
  18. package/dist/cjs/CommandLine.js +54 -0
  19. package/dist/cjs/CommandLine.js.map +7 -0
  20. package/dist/cjs/ContainedSpawner.d.ts +135 -0
  21. package/dist/cjs/ContainedSpawner.d.ts.map +1 -0
  22. package/dist/cjs/ContainedSpawner.js +141 -0
  23. package/dist/cjs/ContainedSpawner.js.map +7 -0
  24. package/dist/cjs/FileSystem.d.ts +452 -0
  25. package/dist/cjs/FileSystem.d.ts.map +1 -0
  26. package/dist/cjs/FileSystem.js +890 -0
  27. package/dist/cjs/FileSystem.js.map +7 -0
  28. package/dist/cjs/FileSystemBatch.d.ts +94 -0
  29. package/dist/cjs/FileSystemBatch.d.ts.map +1 -0
  30. package/dist/cjs/FileSystemBatch.js +31 -0
  31. package/dist/cjs/FileSystemBatch.js.map +7 -0
  32. package/dist/cjs/GrantEvent.d.ts +240 -0
  33. package/dist/cjs/GrantEvent.d.ts.map +1 -0
  34. package/dist/cjs/GrantEvent.js +115 -0
  35. package/dist/cjs/GrantEvent.js.map +7 -0
  36. package/dist/cjs/GrantStore.d.ts +270 -0
  37. package/dist/cjs/GrantStore.d.ts.map +1 -0
  38. package/dist/cjs/GrantStore.js +857 -0
  39. package/dist/cjs/GrantStore.js.map +7 -0
  40. package/dist/cjs/HostError.d.ts +36 -0
  41. package/dist/cjs/HostError.d.ts.map +1 -0
  42. package/dist/cjs/HostError.js +17 -0
  43. package/dist/cjs/HostError.js.map +7 -0
  44. package/dist/cjs/HostServices.d.ts +101 -0
  45. package/dist/cjs/HostServices.d.ts.map +1 -0
  46. package/dist/cjs/HostServices.js +66 -0
  47. package/dist/cjs/HostServices.js.map +7 -0
  48. package/dist/cjs/HttpClient.d.ts +177 -0
  49. package/dist/cjs/HttpClient.d.ts.map +1 -0
  50. package/dist/cjs/HttpClient.js +217 -0
  51. package/dist/cjs/HttpClient.js.map +7 -0
  52. package/dist/cjs/Jj.d.ts +67 -0
  53. package/dist/cjs/Jj.d.ts.map +1 -0
  54. package/dist/cjs/Jj.js +118 -0
  55. package/dist/cjs/Jj.js.map +7 -0
  56. package/dist/cjs/JournalGrantStore.d.ts +65 -0
  57. package/dist/cjs/JournalGrantStore.d.ts.map +1 -0
  58. package/dist/cjs/JournalGrantStore.js +354 -0
  59. package/dist/cjs/JournalGrantStore.js.map +7 -0
  60. package/dist/cjs/Path.d.ts +37 -0
  61. package/dist/cjs/Path.d.ts.map +1 -0
  62. package/dist/cjs/Path.js +46 -0
  63. package/dist/cjs/Path.js.map +7 -0
  64. package/dist/cjs/ProcessConfinement.d.ts +55 -0
  65. package/dist/cjs/ProcessConfinement.d.ts.map +1 -0
  66. package/dist/cjs/ProcessConfinement.js +94 -0
  67. package/dist/cjs/ProcessConfinement.js.map +7 -0
  68. package/dist/cjs/ProcessLedger.d.ts +260 -0
  69. package/dist/cjs/ProcessLedger.d.ts.map +1 -0
  70. package/dist/cjs/ProcessLedger.js +190 -0
  71. package/dist/cjs/ProcessLedger.js.map +7 -0
  72. package/dist/cjs/Rooted.d.ts +53 -0
  73. package/dist/cjs/Rooted.d.ts.map +1 -0
  74. package/dist/cjs/Rooted.js +101 -0
  75. package/dist/cjs/Rooted.js.map +7 -0
  76. package/dist/cjs/Unreachable.d.ts +27 -0
  77. package/dist/cjs/Unreachable.d.ts.map +1 -0
  78. package/dist/cjs/Unreachable.js +43 -0
  79. package/dist/cjs/Unreachable.js.map +7 -0
  80. package/dist/cjs/Workspace.d.ts +62 -0
  81. package/dist/cjs/Workspace.d.ts.map +1 -0
  82. package/dist/cjs/Workspace.js +35 -0
  83. package/dist/cjs/Workspace.js.map +7 -0
  84. package/dist/cjs/index.d.ts +161 -0
  85. package/dist/cjs/index.d.ts.map +1 -0
  86. package/dist/cjs/index.js +73 -0
  87. package/dist/cjs/index.js.map +7 -0
  88. package/dist/cjs/internal/Containment.d.ts +24 -0
  89. package/dist/cjs/internal/Containment.d.ts.map +1 -0
  90. package/dist/cjs/internal/Containment.js +30 -0
  91. package/dist/cjs/internal/Containment.js.map +7 -0
  92. package/dist/cjs/internal/Pipeline.d.ts +18 -0
  93. package/dist/cjs/internal/Pipeline.d.ts.map +1 -0
  94. package/dist/cjs/internal/Pipeline.js +90 -0
  95. package/dist/cjs/internal/Pipeline.js.map +7 -0
  96. package/dist/cjs/internal/makeCapability.d.ts +16 -0
  97. package/dist/cjs/internal/makeCapability.d.ts.map +1 -0
  98. package/dist/cjs/internal/makeCapability.js +44 -0
  99. package/dist/cjs/internal/makeCapability.js.map +7 -0
  100. package/dist/cjs/package.json +1 -0
  101. package/dist/cjs/test/FileSystemBatchContract.d.ts +15 -0
  102. package/dist/cjs/test/FileSystemBatchContract.d.ts.map +1 -0
  103. package/dist/cjs/test/FileSystemBatchContract.js +76 -0
  104. package/dist/cjs/test/FileSystemBatchContract.js.map +7 -0
  105. package/dist/cjs/test/HostContract.d.ts +225 -0
  106. package/dist/cjs/test/HostContract.d.ts.map +1 -0
  107. package/dist/cjs/test/HostContract.js +489 -0
  108. package/dist/cjs/test/HostContract.js.map +7 -0
  109. package/dist/cjs/test/TestGrantStore.d.ts +31 -0
  110. package/dist/cjs/test/TestGrantStore.d.ts.map +1 -0
  111. package/dist/cjs/test/TestGrantStore.js +65 -0
  112. package/dist/cjs/test/TestGrantStore.js.map +7 -0
  113. package/dist/esm/CapabilitySet.d.ts +6 -0
  114. package/dist/esm/CapabilitySet.d.ts.map +1 -0
  115. package/dist/esm/CapabilitySet.js +6 -0
  116. package/dist/esm/CapabilitySet.js.map +1 -0
  117. package/dist/esm/ChildProcessEnvironment.d.ts +60 -0
  118. package/dist/esm/ChildProcessEnvironment.d.ts.map +1 -0
  119. package/dist/esm/ChildProcessEnvironment.js +91 -0
  120. package/dist/esm/ChildProcessEnvironment.js.map +1 -0
  121. package/dist/esm/ChildProcessSpawner.d.ts +100 -0
  122. package/dist/esm/ChildProcessSpawner.d.ts.map +1 -0
  123. package/dist/esm/ChildProcessSpawner.js +342 -0
  124. package/dist/esm/ChildProcessSpawner.js.map +1 -0
  125. package/dist/esm/CommandLine.d.ts +173 -0
  126. package/dist/esm/CommandLine.d.ts.map +1 -0
  127. package/dist/esm/CommandLine.js +195 -0
  128. package/dist/esm/CommandLine.js.map +1 -0
  129. package/dist/esm/ContainedSpawner.d.ts +135 -0
  130. package/dist/esm/ContainedSpawner.d.ts.map +1 -0
  131. package/dist/esm/ContainedSpawner.js +180 -0
  132. package/dist/esm/ContainedSpawner.js.map +1 -0
  133. package/dist/esm/FileSystem.d.ts +452 -0
  134. package/dist/esm/FileSystem.d.ts.map +1 -0
  135. package/dist/esm/FileSystem.js +989 -0
  136. package/dist/esm/FileSystem.js.map +1 -0
  137. package/dist/esm/FileSystemBatch.d.ts +94 -0
  138. package/dist/esm/FileSystemBatch.d.ts.map +1 -0
  139. package/dist/esm/FileSystemBatch.js +26 -0
  140. package/dist/esm/FileSystemBatch.js.map +1 -0
  141. package/dist/esm/GrantEvent.d.ts +240 -0
  142. package/dist/esm/GrantEvent.d.ts.map +1 -0
  143. package/dist/esm/GrantEvent.js +155 -0
  144. package/dist/esm/GrantEvent.js.map +1 -0
  145. package/dist/esm/GrantStore.d.ts +270 -0
  146. package/dist/esm/GrantStore.d.ts.map +1 -0
  147. package/dist/esm/GrantStore.js +951 -0
  148. package/dist/esm/GrantStore.js.map +1 -0
  149. package/dist/esm/HostError.d.ts +36 -0
  150. package/dist/esm/HostError.d.ts.map +1 -0
  151. package/dist/esm/HostError.js +20 -0
  152. package/dist/esm/HostError.js.map +1 -0
  153. package/dist/esm/HostServices.d.ts +101 -0
  154. package/dist/esm/HostServices.d.ts.map +1 -0
  155. package/dist/esm/HostServices.js +76 -0
  156. package/dist/esm/HostServices.js.map +1 -0
  157. package/dist/esm/HttpClient.d.ts +177 -0
  158. package/dist/esm/HttpClient.d.ts.map +1 -0
  159. package/dist/esm/HttpClient.js +299 -0
  160. package/dist/esm/HttpClient.js.map +1 -0
  161. package/dist/esm/Jj.d.ts +67 -0
  162. package/dist/esm/Jj.d.ts.map +1 -0
  163. package/dist/esm/Jj.js +111 -0
  164. package/dist/esm/Jj.js.map +1 -0
  165. package/dist/esm/JournalGrantStore.d.ts +65 -0
  166. package/dist/esm/JournalGrantStore.d.ts.map +1 -0
  167. package/dist/esm/JournalGrantStore.js +350 -0
  168. package/dist/esm/JournalGrantStore.js.map +1 -0
  169. package/dist/esm/Path.d.ts +37 -0
  170. package/dist/esm/Path.d.ts.map +1 -0
  171. package/dist/esm/Path.js +34 -0
  172. package/dist/esm/Path.js.map +1 -0
  173. package/dist/esm/ProcessConfinement.d.ts +55 -0
  174. package/dist/esm/ProcessConfinement.d.ts.map +1 -0
  175. package/dist/esm/ProcessConfinement.js +106 -0
  176. package/dist/esm/ProcessConfinement.js.map +1 -0
  177. package/dist/esm/ProcessLedger.d.ts +260 -0
  178. package/dist/esm/ProcessLedger.d.ts.map +1 -0
  179. package/dist/esm/ProcessLedger.js +283 -0
  180. package/dist/esm/ProcessLedger.js.map +1 -0
  181. package/dist/esm/Rooted.d.ts +53 -0
  182. package/dist/esm/Rooted.d.ts.map +1 -0
  183. package/dist/esm/Rooted.js +104 -0
  184. package/dist/esm/Rooted.js.map +1 -0
  185. package/dist/esm/Unreachable.d.ts +27 -0
  186. package/dist/esm/Unreachable.d.ts.map +1 -0
  187. package/dist/esm/Unreachable.js +26 -0
  188. package/dist/esm/Unreachable.js.map +1 -0
  189. package/dist/esm/Workspace.d.ts +62 -0
  190. package/dist/esm/Workspace.d.ts.map +1 -0
  191. package/dist/esm/Workspace.js +51 -0
  192. package/dist/esm/Workspace.js.map +1 -0
  193. package/dist/esm/index.d.ts +161 -0
  194. package/dist/esm/index.d.ts.map +1 -0
  195. package/dist/esm/index.js +151 -0
  196. package/dist/esm/index.js.map +1 -0
  197. package/dist/esm/internal/Containment.d.ts +24 -0
  198. package/dist/esm/internal/Containment.d.ts.map +1 -0
  199. package/dist/esm/internal/Containment.js +27 -0
  200. package/dist/esm/internal/Containment.js.map +1 -0
  201. package/dist/esm/internal/Pipeline.d.ts +18 -0
  202. package/dist/esm/internal/Pipeline.d.ts.map +1 -0
  203. package/dist/esm/internal/Pipeline.js +68 -0
  204. package/dist/esm/internal/Pipeline.js.map +1 -0
  205. package/dist/esm/internal/makeCapability.d.ts +16 -0
  206. package/dist/esm/internal/makeCapability.d.ts.map +1 -0
  207. package/dist/esm/internal/makeCapability.js +22 -0
  208. package/dist/esm/internal/makeCapability.js.map +1 -0
  209. package/dist/esm/test/FileSystemBatchContract.d.ts +15 -0
  210. package/dist/esm/test/FileSystemBatchContract.d.ts.map +1 -0
  211. package/dist/esm/test/FileSystemBatchContract.js +57 -0
  212. package/dist/esm/test/FileSystemBatchContract.js.map +1 -0
  213. package/dist/esm/test/HostContract.d.ts +225 -0
  214. package/dist/esm/test/HostContract.d.ts.map +1 -0
  215. package/dist/esm/test/HostContract.js +460 -0
  216. package/dist/esm/test/HostContract.js.map +1 -0
  217. package/dist/esm/test/TestGrantStore.d.ts +31 -0
  218. package/dist/esm/test/TestGrantStore.d.ts.map +1 -0
  219. package/dist/esm/test/TestGrantStore.js +57 -0
  220. package/dist/esm/test/TestGrantStore.js.map +1 -0
  221. package/docs/README.md +162 -0
  222. package/docs/api.md +1532 -0
  223. package/docs/concepts/decoration-in-place.md +117 -0
  224. package/docs/concepts/filesystem-batches.md +58 -0
  225. package/docs/concepts/filesystem-confinement.md +168 -0
  226. package/docs/concepts/grant-decisions.md +181 -0
  227. package/docs/concepts/process-containment.md +155 -0
  228. package/docs/contract.md +14 -0
  229. package/docs/guides/adapt-a-new-host-platform.md +148 -0
  230. package/docs/guides/answer-permission-requests.md +151 -0
  231. package/docs/guides/authorize-network-and-model-calls.md +118 -0
  232. package/docs/guides/contain-spawned-processes.md +118 -0
  233. package/docs/guides/guard-a-host-bundle.md +109 -0
  234. package/docs/guides/persist-grants-across-restarts.md +135 -0
  235. package/docs/guides/write-a-capability-policy.md +159 -0
  236. package/docs/installation.md +115 -0
  237. package/docs/quickstart.md +182 -0
  238. package/docs/testing.md +108 -0
  239. package/docs/troubleshooting.md +281 -0
  240. package/package.json +344 -3
  241. package/src/CapabilitySet.ts +6 -0
  242. package/src/ChildProcessEnvironment.ts +98 -0
  243. package/src/ChildProcessSpawner.ts +414 -0
  244. package/src/CommandLine.ts +250 -0
  245. package/src/ContainedSpawner.ts +284 -0
  246. package/src/FileSystem.ts +1655 -0
  247. package/src/FileSystemBatch.ts +90 -0
  248. package/src/GrantEvent.ts +174 -0
  249. package/src/GrantStore.ts +1345 -0
  250. package/src/HostError.ts +37 -0
  251. package/src/HostServices.ts +133 -0
  252. package/src/HttpClient.ts +406 -0
  253. package/src/Jj.ts +159 -0
  254. package/src/JournalGrantStore.ts +461 -0
  255. package/src/Path.ts +47 -0
  256. package/src/ProcessConfinement.ts +156 -0
  257. package/src/ProcessLedger.ts +423 -0
  258. package/src/Rooted.ts +128 -0
  259. package/src/Unreachable.ts +30 -0
  260. package/src/Workspace.ts +65 -0
  261. package/src/index.ts +181 -0
  262. package/src/internal/Containment.ts +37 -0
  263. package/src/internal/Pipeline.ts +85 -0
  264. package/src/internal/makeCapability.ts +26 -0
  265. package/src/test/FileSystemBatchContract.ts +59 -0
  266. package/src/test/HostContract.ts +785 -0
  267. package/src/test/TestGrantStore.ts +73 -0
@@ -0,0 +1,857 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var GrantStore_exports = {};
20
+ __export(GrantStore_exports, {
21
+ GrantStore: () => GrantStore,
22
+ canonicalEnvelopePatterns: () => canonicalEnvelopePatterns,
23
+ emptyPolicy: () => emptyPolicy,
24
+ envelopeSignature: () => envelopeSignature,
25
+ isValidEnvelopePattern: () => isValidEnvelopePattern,
26
+ isValidGrantPattern: () => isValidGrantPattern,
27
+ layer: () => layer,
28
+ layerNoop: () => layerNoop,
29
+ make: () => make,
30
+ makeNoop: () => makeNoop,
31
+ maximumCapabilityResourceLength: () => maximumCapabilityResourceLength,
32
+ maximumEnvelopePatterns: () => maximumEnvelopePatterns,
33
+ maximumEventBytes: () => maximumEventBytes,
34
+ maximumIdentityLength: () => maximumIdentityLength,
35
+ maximumMetadataBytes: () => maximumMetadataBytes,
36
+ maximumMetadataDepth: () => maximumMetadataDepth,
37
+ maximumMetadataMembers: () => maximumMetadataMembers,
38
+ maximumPendingRequests: () => maximumPendingRequests,
39
+ maximumPersistMillis: () => maximumPersistMillis,
40
+ maximumRules: () => maximumRules
41
+ });
42
+ module.exports = __toCommonJS(GrantStore_exports);
43
+ var import_Capability = require("@smthrs/capability/Capability");
44
+ var import_Permission = require("@smthrs/capability/Permission");
45
+ var import_Sha256 = require("@smthrs/crypto/Sha256");
46
+ var import_effect = require("effect");
47
+ var import_CapabilitySet = require("./CapabilitySet.js");
48
+ var import_GrantEvent = require("./GrantEvent.js");
49
+ var import_Workspace = require("./Workspace.js");
50
+ const emptyPolicy = import_CapabilitySet.current.pipe(
51
+ import_effect.Effect.map((ceiling) => Object.freeze({ groups: Object.freeze([]), ceiling }))
52
+ );
53
+ class GrantStore extends import_effect.Context.Service()("@smthrs/kernel/GrantStore") {
54
+ }
55
+ const maximumRules = 1024;
56
+ const maximumEnvelopePatterns = 256;
57
+ const maximumPendingRequests = 1024;
58
+ const maximumMetadataDepth = 16;
59
+ const maximumMetadataMembers = 1024;
60
+ const maximumMetadataBytes = 64 * 1024;
61
+ const maximumEventBytes = 256 * 1024;
62
+ const maximumPersistMillis = 3e4;
63
+ const maximumIdentityLength = 4096;
64
+ const maximumCapabilityResourceLength = import_Capability.maxResourceLength;
65
+ const encoder = new TextEncoder();
66
+ const isWellFormed = (value) => {
67
+ for (let index = 0; index < value.length; index += 1) {
68
+ const unit = value.charCodeAt(index);
69
+ if (unit >= 55296 && unit <= 56319) {
70
+ const next = value.charCodeAt(index + 1);
71
+ if (!(next >= 56320 && next <= 57343)) return false;
72
+ index += 1;
73
+ } else if (unit >= 56320 && unit <= 57343) {
74
+ return false;
75
+ }
76
+ }
77
+ return true;
78
+ };
79
+ const invalid = (message) => new import_Permission.GrantStoreError({ code: "invalid_resolution", message });
80
+ const immutableFields = (value, fields) => {
81
+ for (const field of fields) {
82
+ Object.defineProperty(value, field, {
83
+ value: value[field],
84
+ enumerable: true,
85
+ writable: false,
86
+ configurable: false
87
+ });
88
+ }
89
+ return value;
90
+ };
91
+ const snapshotCapability = (capability) => immutableFields(
92
+ new import_Capability.Capability({ action: capability.action, resource: capability.resource }),
93
+ ["action", "resource"]
94
+ );
95
+ const snapshotPattern = (pattern) => immutableFields(
96
+ new import_Capability.CapabilityPattern({ action: pattern.action, resource: pattern.resource }),
97
+ ["action", "resource"]
98
+ );
99
+ const snapshotRule = (rule) => immutableFields(
100
+ new import_Permission.Rule({ effect: rule.effect, pattern: snapshotPattern(rule.pattern) }),
101
+ ["effect", "pattern"]
102
+ );
103
+ const snapshotFailure = (what, cause) => cause instanceof import_Permission.GrantStoreError ? cause : invalid(`${what} is invalid`);
104
+ const attemptSnapshot = (what, evaluate2) => import_effect.Effect.try({
105
+ try: evaluate2,
106
+ catch: (cause) => snapshotFailure(what, cause)
107
+ });
108
+ const metadataSnapshot = (input) => {
109
+ let members = 0;
110
+ const active = /* @__PURE__ */ new WeakSet();
111
+ const visit = (value, depth) => {
112
+ if (depth > maximumMetadataDepth) throw invalid(`metadata exceeds depth ${maximumMetadataDepth}`);
113
+ if (value === null || typeof value === "boolean") return value;
114
+ if (typeof value === "number") {
115
+ if (!Number.isFinite(value)) throw invalid("metadata contains a non-finite number");
116
+ return value;
117
+ }
118
+ if (typeof value === "string") {
119
+ if (!isWellFormed(value)) throw invalid("metadata contains ill-formed text");
120
+ if (value.length > maximumMetadataBytes) throw invalid(`metadata exceeds ${maximumMetadataBytes} bytes`);
121
+ return value;
122
+ }
123
+ if (typeof value !== "object" || value === null) throw invalid("metadata must be JSON data");
124
+ if (active.has(value)) throw invalid("metadata must not contain cycles");
125
+ active.add(value);
126
+ try {
127
+ if (Array.isArray(value)) {
128
+ const descriptors = Object.getOwnPropertyDescriptors(value);
129
+ const snapshot3 = [];
130
+ for (let index = 0; index < value.length; index += 1) {
131
+ members += 1;
132
+ if (members > maximumMetadataMembers) {
133
+ throw invalid(`metadata exceeds ${maximumMetadataMembers} members`);
134
+ }
135
+ const descriptor = descriptors[String(index)];
136
+ if (descriptor === void 0 || !("value" in descriptor)) {
137
+ throw invalid("metadata arrays must be dense data");
138
+ }
139
+ snapshot3.push(visit(descriptor.value, depth + 1));
140
+ }
141
+ return Object.freeze(snapshot3);
142
+ }
143
+ const prototype = Object.getPrototypeOf(value);
144
+ if (prototype !== Object.prototype && prototype !== null) {
145
+ throw invalid("metadata objects must be plain records");
146
+ }
147
+ const snapshot2 = /* @__PURE__ */ Object.create(null);
148
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
149
+ if (!descriptor.enumerable) continue;
150
+ members += 1;
151
+ if (members > maximumMetadataMembers) {
152
+ throw invalid(`metadata exceeds ${maximumMetadataMembers} members`);
153
+ }
154
+ if (!isWellFormed(key) || !("value" in descriptor)) {
155
+ throw invalid("metadata members must be well-formed data properties");
156
+ }
157
+ if (descriptor.value !== void 0) snapshot2[key] = visit(descriptor.value, depth + 1);
158
+ }
159
+ return Object.freeze(snapshot2);
160
+ } finally {
161
+ active.delete(value);
162
+ }
163
+ };
164
+ const snapshot = visit(input, 0);
165
+ if (Array.isArray(snapshot) || snapshot === null || typeof snapshot !== "object") {
166
+ throw invalid("metadata must be a record");
167
+ }
168
+ const encoded = JSON.stringify(snapshot);
169
+ if (encoder.encode(encoded).byteLength > maximumMetadataBytes) {
170
+ throw invalid(`metadata exceeds ${maximumMetadataBytes} bytes`);
171
+ }
172
+ return snapshot;
173
+ };
174
+ const validIdentity = (value) => value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maximumIdentityLength && isWellFormed(value) && !value.includes("\0");
175
+ const prepareEnvelope = (input, expectedPlanDigest, workspaceRoot) => attemptSnapshot("grant envelope", () => {
176
+ if (!Array.isArray(input.patterns)) throw invalid("patterns must be an array");
177
+ if (input.patterns.length > maximumEnvelopePatterns) {
178
+ throw invalid(`patterns exceed ${maximumEnvelopePatterns} entries`);
179
+ }
180
+ const patterns = canonicalEnvelopePatterns(input.patterns);
181
+ if (patterns.length === 0) {
182
+ return { planDigest: expectedPlanDigest ?? "", scope: "run", patterns };
183
+ }
184
+ const scope = input.scope ?? "run";
185
+ if (!isEnvelopeScope(scope)) throw invalid("scope must be run or remembered");
186
+ if (expectedPlanDigest === void 0) throw invalid("grant envelope requires a configured planDigest");
187
+ if (!validIdentity(input.planDigest)) throw invalid("planDigest is empty, malformed, or too long");
188
+ if (input.planDigest !== expectedPlanDigest) throw invalid("planDigest mismatch");
189
+ for (let index = 0; index < patterns.length; index += 1) {
190
+ if (!isValidEnvelopePattern(patterns[index], workspaceRoot)) {
191
+ throw invalid(`patterns[${index}] is outside the workspace envelope`);
192
+ }
193
+ }
194
+ return { planDigest: input.planDigest, scope, patterns };
195
+ });
196
+ const exactPattern = (capability) => new import_Capability.CapabilityPattern({
197
+ action: capability.action,
198
+ resource: capability.resource
199
+ });
200
+ const hasResourceWildcard = (resource) => resource.includes("*") || resource.includes("?");
201
+ const isResolution = (value) => value === "once" || value === "run" || value === "remembered" || value === "deny";
202
+ const isEnvelopeScope = (value) => value === "run" || value === "remembered";
203
+ const canonicalEnvelopePatterns = (patterns) => {
204
+ const byIdentity = /* @__PURE__ */ new Map();
205
+ for (const input of patterns) {
206
+ const pattern = snapshotPattern(input);
207
+ const identity = (0, import_Capability.format)(pattern);
208
+ if (!byIdentity.has(identity)) {
209
+ byIdentity.set(identity, pattern);
210
+ }
211
+ }
212
+ return Object.freeze([...byIdentity.keys()].sort().map((identity) => byIdentity.get(identity)));
213
+ };
214
+ const envelopeEncoding = (planDigest, scope, patterns) => JSON.stringify({ planDigest, scope, patterns: canonicalEnvelopePatterns(patterns).map(import_Capability.format) });
215
+ const envelopeSignature = (planDigest, scope, patterns) => `sha256:${(0, import_Sha256.digestSync)(envelopeEncoding(planDigest, scope, patterns))}`;
216
+ const decodeLegacySignature = import_effect.Schema.decodeUnknownResult(
217
+ import_effect.Schema.fromJsonString(import_effect.Schema.Struct({
218
+ planDigest: import_effect.Schema.String,
219
+ scope: import_effect.Schema.Literals(["run", "remembered"]),
220
+ patterns: import_effect.Schema.Array(import_effect.Schema.String).check(import_effect.Schema.isMaxLength(maximumEnvelopePatterns))
221
+ })),
222
+ { onExcessProperty: "error" }
223
+ );
224
+ const normalizeEnvelopeSignature = (value) => {
225
+ if (!value.startsWith("{")) return value;
226
+ if (value.length > maximumEventBytes || encoder.encode(value).byteLength > maximumEventBytes) return value;
227
+ const decoded = decodeLegacySignature(value);
228
+ if (decoded._tag === "Failure" || !validIdentity(decoded.success.planDigest)) return value;
229
+ const { planDigest, scope, patterns } = decoded.success;
230
+ const parsed = import_effect.Option.all(patterns.map(import_Capability.parsePattern));
231
+ if (import_effect.Option.isNone(parsed) || envelopeEncoding(planDigest, scope, parsed.value) !== value) return value;
232
+ return envelopeSignature(planDigest, scope, parsed.value);
233
+ };
234
+ const isValidGrantPattern = (pattern, capability, tier, workspaceRoot) => {
235
+ if (pattern.action !== capability.action || !(0, import_Capability.matches)(pattern, capability)) {
236
+ return false;
237
+ }
238
+ if (hasResourceWildcard(pattern.resource) && pattern.resource === capability.resource) {
239
+ return false;
240
+ }
241
+ if ((0, import_Capability.tierOf)(capability, { workspaceRoot }) !== tier) {
242
+ return false;
243
+ }
244
+ if (capability.action !== "fs:write") {
245
+ return true;
246
+ }
247
+ if (tier === "irreversible") {
248
+ return !hasResourceWildcard(pattern.resource);
249
+ }
250
+ const workspacePattern = new import_Capability.CapabilityPattern({
251
+ action: "fs:write",
252
+ resource: `${workspaceRoot.replace(/[\\/]+$/, "")}/**`
253
+ });
254
+ return pattern.resource === workspaceRoot || (0, import_Capability.subsumes)(workspacePattern, pattern);
255
+ };
256
+ const isValidEnvelopePattern = (pattern, workspaceRoot) => {
257
+ if (pattern.action.includes("*")) {
258
+ return false;
259
+ }
260
+ if (pattern.action !== "fs:write" || !hasResourceWildcard(pattern.resource)) {
261
+ return true;
262
+ }
263
+ const workspacePattern = new import_Capability.CapabilityPattern({
264
+ action: "fs:write",
265
+ resource: `${workspaceRoot.replace(/[\\/]+$/, "")}/**`
266
+ });
267
+ return (0, import_Capability.subsumes)(workspacePattern, pattern);
268
+ };
269
+ const normalizeRules = (rules) => {
270
+ if (rules === void 0 || rules.length === 0 || rules[0] === void 0) {
271
+ return { configured: [], remembered: [] };
272
+ }
273
+ if (rules.length > maximumRules) throw invalid(`rules exceed ${maximumRules} entries`);
274
+ if (Array.isArray(rules[0])) {
275
+ const rulesets = rules;
276
+ let count = 0;
277
+ for (const ruleset of rulesets) {
278
+ count += ruleset.length;
279
+ if (count > maximumRules) throw invalid(`rules exceed ${maximumRules} entries`);
280
+ }
281
+ return {
282
+ configured: Object.freeze(rulesets[0].map(snapshotRule)),
283
+ remembered: Object.freeze(rulesets.slice(1).flatMap((ruleset) => ruleset.map(snapshotRule)))
284
+ };
285
+ }
286
+ return {
287
+ configured: Object.freeze(rules.map(snapshotRule)),
288
+ remembered: []
289
+ };
290
+ };
291
+ const make = (options = {}) => import_effect.Effect.gen(function* () {
292
+ const attended = options.attended ?? true;
293
+ const runId = options.runId;
294
+ const planDigest = options.planDigest;
295
+ if (!validIdentity(runId)) {
296
+ return yield* import_effect.Effect.fail(invalid("runId is empty, malformed, or too long"));
297
+ }
298
+ if (!validIdentity(planDigest)) {
299
+ return yield* import_effect.Effect.fail(invalid("planDigest is empty, malformed, or too long"));
300
+ }
301
+ const workspace = yield* import_Workspace.Workspace;
302
+ const initialCeiling = yield* import_CapabilitySet.current;
303
+ const workspaceRoot = workspace.root;
304
+ const initial = yield* attemptSnapshot("rules", () => normalizeRules(options.rules));
305
+ const initialRunRules = yield* attemptSnapshot("runRules", () => {
306
+ const values = options.runRules ?? [];
307
+ if (values.length > maximumRules) throw invalid(`runRules exceed ${maximumRules} entries`);
308
+ return values.map(
309
+ (value) => "rule" in value ? {
310
+ rule: snapshotRule(value.rule),
311
+ ceiling: value.ceiling.reduce(
312
+ (ceiling, group) => (0, import_CapabilitySet.intersect)(ceiling, (0, import_CapabilitySet.fromPatterns)(group)),
313
+ initialCeiling
314
+ )
315
+ } : { rule: snapshotRule(value), ceiling: initialCeiling }
316
+ );
317
+ });
318
+ if (initial.configured.length + initial.remembered.length + initialRunRules.length > maximumRules) {
319
+ return yield* import_effect.Effect.fail(invalid(`rules exceed ${maximumRules} entries`));
320
+ }
321
+ const configuredRules = initial.configured;
322
+ const envelopeRules = [];
323
+ const runRules = initialRunRules;
324
+ const rememberedRules = [...initial.remembered];
325
+ const pending = /* @__PURE__ */ new Map();
326
+ const persist = options.persist ?? (() => import_effect.Effect.void);
327
+ const signatures = yield* attemptSnapshot("envelopeSignatures", () => {
328
+ const values = options.envelopeSignatures ?? [];
329
+ if (!Array.isArray(values) || values.length > maximumRules) {
330
+ throw invalid(`envelopeSignatures exceed ${maximumRules} entries`);
331
+ }
332
+ return values.map((value, index) => {
333
+ const signature = typeof value === "string" ? normalizeEnvelopeSignature(value) : value;
334
+ if (typeof signature !== "string" || !validIdentity(signature)) {
335
+ throw invalid(`envelopeSignatures[${index}] is malformed or too long`);
336
+ }
337
+ return signature;
338
+ });
339
+ });
340
+ const grantedEnvelopes = new Set(signatures);
341
+ let nextRequestId = 1;
342
+ let closed = false;
343
+ const mutation = yield* import_effect.Semaphore.make(1);
344
+ const persistEvent = (event) => {
345
+ const bytes = encoder.encode(JSON.stringify(event)).byteLength;
346
+ return bytes > maximumEventBytes ? import_effect.Effect.fail(invalid(`grant event exceeds ${maximumEventBytes} bytes`)) : import_effect.Effect.timeoutOrElse(persist(event), {
347
+ duration: maximumPersistMillis,
348
+ orElse: () => import_effect.Effect.fail(
349
+ new import_Permission.GrantStoreError({
350
+ code: "journal_failed",
351
+ message: `grant journal write exceeded ${maximumPersistMillis}ms`
352
+ })
353
+ )
354
+ });
355
+ };
356
+ const invalidRemembered = rememberedRules.findIndex(
357
+ (rule) => rule.effect === "allow" && !isValidEnvelopePattern(rule.pattern, workspaceRoot)
358
+ );
359
+ if (invalidRemembered !== -1) {
360
+ return yield* import_effect.Effect.fail(invalid(`rememberedRules[${invalidRemembered}] is outside the workspace envelope`));
361
+ }
362
+ const invalidRun = runRules.findIndex(
363
+ ({ rule }) => rule.effect === "allow" && !isValidEnvelopePattern(rule.pattern, workspaceRoot)
364
+ );
365
+ if (invalidRun !== -1) {
366
+ return yield* import_effect.Effect.fail(invalid(`runRules[${invalidRun}] is outside the workspace envelope`));
367
+ }
368
+ const admitEnvelope = (envelope) => import_effect.Effect.gen(function* () {
369
+ const { patterns, scope } = envelope;
370
+ if (patterns.length === 0) return false;
371
+ const signature = envelopeSignature(envelope.planDigest, scope, patterns);
372
+ const durable = grantedEnvelopes.has(signature);
373
+ const destination = scope === "remembered" ? rememberedRules : envelopeRules;
374
+ const replayed = scope === "remembered" ? rememberedRules : runRules.filter(({ ceiling }) => ceiling === initialCeiling).map(({ rule }) => rule);
375
+ const existing = /* @__PURE__ */ new Set();
376
+ for (const rule of replayed) {
377
+ if (rule.effect === "allow") existing.add((0, import_Capability.format)(rule.pattern));
378
+ else existing.clear();
379
+ }
380
+ const incoming = durable ? patterns.filter((pattern) => !existing.has((0, import_Capability.format)(pattern))) : patterns;
381
+ if (configuredRules.length + envelopeRules.length + runRules.length + rememberedRules.length + incoming.length > maximumRules) {
382
+ return yield* import_effect.Effect.fail(invalid(`rules exceed ${maximumRules} entries`));
383
+ }
384
+ if (!durable && grantedEnvelopes.size >= maximumRules) {
385
+ return yield* import_effect.Effect.fail(invalid(`grant envelopes exceed ${maximumRules} entries`));
386
+ }
387
+ if (!durable) {
388
+ yield* persistEvent(
389
+ new import_GrantEvent.EnvelopeGrant({
390
+ eventType: "flows.kernel.grant.envelope.v1",
391
+ runId: runId ?? "",
392
+ planDigest: envelope.planDigest,
393
+ patterns,
394
+ scope
395
+ })
396
+ );
397
+ grantedEnvelopes.add(signature);
398
+ }
399
+ for (const pattern of incoming) {
400
+ destination.push(snapshotRule(new import_Permission.Rule({ effect: "allow", pattern })));
401
+ }
402
+ return true;
403
+ });
404
+ if (options.envelope !== void 0) {
405
+ const envelope = yield* prepareEnvelope(options.envelope, planDigest, workspaceRoot);
406
+ yield* admitEnvelope(envelope);
407
+ }
408
+ const rulesets = (capability) => [
409
+ configuredRules,
410
+ envelopeRules,
411
+ runRules.filter(({ ceiling }) => (0, import_CapabilitySet.allows)(ceiling, capability)).map(({ rule }) => rule),
412
+ rememberedRules
413
+ ];
414
+ yield* import_effect.Effect.addFinalizer(
415
+ () => mutation.withPermit(
416
+ import_effect.Effect.gen(function* () {
417
+ closed = true;
418
+ const entries = [...pending.values()];
419
+ pending.clear();
420
+ yield* import_effect.Effect.forEach(
421
+ entries,
422
+ (entry) => import_effect.Deferred.fail(
423
+ entry.deferred,
424
+ (0, import_Permission.permissionDenied)(entry.capability, "grant store closed")
425
+ ),
426
+ { discard: true }
427
+ );
428
+ })
429
+ )
430
+ );
431
+ const allocateRequest = (capability, tier, meta, ceiling) => import_effect.Effect.gen(function* () {
432
+ if (pending.size >= maximumPendingRequests) {
433
+ return yield* import_effect.Effect.fail(invalid(`pending requests exceed ${maximumPendingRequests} entries`));
434
+ }
435
+ const requestId = `permission-${nextRequestId++}`;
436
+ const deferred = yield* import_effect.Deferred.make();
437
+ const entry = {
438
+ requestId,
439
+ capability,
440
+ tier,
441
+ meta,
442
+ ceiling,
443
+ deferred
444
+ };
445
+ pending.set(requestId, entry);
446
+ return entry;
447
+ });
448
+ const nextUnattendedRequestId = import_effect.Effect.sync(() => `permission-${nextRequestId++}`);
449
+ const check = import_effect.Effect.fn("GrantStore.check")(
450
+ (capability, meta = {}) => import_effect.Effect.uninterruptibleMask(
451
+ (restore) => import_effect.Effect.gen(function* () {
452
+ const request = yield* attemptSnapshot("permission request", () => ({
453
+ capability: snapshotCapability(capability),
454
+ meta: metadataSnapshot(meta)
455
+ }));
456
+ const entry = yield* restore(mutation.take(1)).pipe(
457
+ import_effect.Effect.andThen(
458
+ import_effect.Effect.gen(function* () {
459
+ if (closed) {
460
+ return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "store_closed" }));
461
+ }
462
+ const ceiling = yield* import_CapabilitySet.current;
463
+ if (!(0, import_CapabilitySet.allows)(ceiling, request.capability)) {
464
+ return yield* import_effect.Effect.fail((0, import_Permission.permissionDenied)(request.capability, "outside capability ceiling"));
465
+ }
466
+ const decision = (0, import_Permission.evaluate)(rulesets(request.capability), request.capability);
467
+ if (decision === "allow") {
468
+ return void 0;
469
+ }
470
+ if (decision === "deny") {
471
+ return yield* import_effect.Effect.fail((0, import_Permission.permissionDenied)(request.capability, "denied by permission policy"));
472
+ }
473
+ const tier = (0, import_Capability.tierOf)(request.capability, { workspaceRoot });
474
+ if (!attended) {
475
+ const requestId = yield* nextUnattendedRequestId;
476
+ return yield* import_effect.Effect.fail(
477
+ (0, import_Permission.permissionRequired)({
478
+ requestId,
479
+ runId,
480
+ capability: request.capability,
481
+ tier,
482
+ meta: request.meta
483
+ })
484
+ );
485
+ }
486
+ return yield* allocateRequest(
487
+ request.capability,
488
+ tier,
489
+ request.meta,
490
+ ceiling
491
+ );
492
+ }).pipe(import_effect.Effect.ensuring(mutation.release(1)))
493
+ )
494
+ );
495
+ if (entry === void 0) {
496
+ return;
497
+ }
498
+ return yield* restore(import_effect.Deferred.await(entry.deferred)).pipe(
499
+ import_effect.Effect.ensuring(
500
+ mutation.withPermit(
501
+ import_effect.Effect.sync(() => {
502
+ pending.delete(entry.requestId);
503
+ })
504
+ )
505
+ )
506
+ );
507
+ })
508
+ )
509
+ );
510
+ const resolveCovered = import_effect.Effect.suspend(
511
+ () => import_effect.Effect.forEach(
512
+ [...pending.entries()],
513
+ ([requestId, entry]) => {
514
+ if (!(0, import_CapabilitySet.allows)(entry.ceiling, entry.capability) || (0, import_Permission.evaluate)(rulesets(entry.capability), entry.capability) !== "allow") {
515
+ return import_effect.Effect.void;
516
+ }
517
+ return import_effect.Deferred.succeed(entry.deferred, void 0).pipe(
518
+ import_effect.Effect.andThen(
519
+ import_effect.Effect.sync(() => {
520
+ pending.delete(requestId);
521
+ })
522
+ )
523
+ );
524
+ },
525
+ { discard: true }
526
+ ).pipe(import_effect.Effect.asVoid)
527
+ );
528
+ const resolving = /* @__PURE__ */ new Set();
529
+ const granting = /* @__PURE__ */ new Map();
530
+ let reservedRules = 0;
531
+ const planReply = (requestId, resolution, suppliedPattern) => import_effect.Effect.gen(function* () {
532
+ if (closed) {
533
+ return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "store_closed" }));
534
+ }
535
+ if (!isResolution(resolution)) {
536
+ return yield* import_effect.Effect.fail(
537
+ new import_Permission.GrantStoreError({
538
+ code: "invalid_resolution",
539
+ message: "unknown grant resolution"
540
+ })
541
+ );
542
+ }
543
+ const entry = pending.get(requestId);
544
+ if (entry === void 0 || resolving.has(requestId)) {
545
+ return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "request_not_found" }));
546
+ }
547
+ const pattern = yield* attemptSnapshot("grant pattern", () => {
548
+ if (resolution === "run" || resolution === "remembered") {
549
+ if (suppliedPattern !== void 0) return snapshotPattern(suppliedPattern);
550
+ const derived = (0, import_Capability.patternFromCapability)(entry.capability);
551
+ if (import_effect.Option.isNone(derived)) {
552
+ throw invalid(
553
+ "the requested resource contains glob metacharacters; supply an explicit grant pattern or resolve once"
554
+ );
555
+ }
556
+ return snapshotPattern(derived.value);
557
+ }
558
+ return snapshotPattern(exactPattern(entry.capability));
559
+ });
560
+ switch (resolution) {
561
+ case "once": {
562
+ resolving.add(requestId);
563
+ return {
564
+ resolution,
565
+ entry,
566
+ event: new import_GrantEvent.OnceGrant({
567
+ eventType: "flows.kernel.grant.once.v1",
568
+ requestId,
569
+ runId: runId ?? "",
570
+ ...planDigest === void 0 ? {} : { planDigest },
571
+ capability: entry.capability,
572
+ pattern,
573
+ scope: "once",
574
+ tier: entry.tier
575
+ })
576
+ };
577
+ }
578
+ case "run": {
579
+ if (planDigest === void 0) {
580
+ return yield* import_effect.Effect.fail(
581
+ new import_Permission.GrantStoreError({
582
+ code: "invalid_resolution",
583
+ message: "run grants require a plan digest"
584
+ })
585
+ );
586
+ }
587
+ if (!isValidGrantPattern(pattern, entry.capability, entry.tier, workspaceRoot)) {
588
+ return yield* import_effect.Effect.fail(invalid("grant pattern exceeds the requested authority"));
589
+ }
590
+ if (configuredRules.length + envelopeRules.length + runRules.length + rememberedRules.length + reservedRules >= maximumRules) {
591
+ return yield* import_effect.Effect.fail(invalid(`rules exceed ${maximumRules} entries`));
592
+ }
593
+ resolving.add(requestId);
594
+ reservedRules += 1;
595
+ return {
596
+ resolution,
597
+ entry,
598
+ rule: snapshotRule(new import_Permission.Rule({ effect: "allow", pattern })),
599
+ event: new import_GrantEvent.RunGrant({
600
+ eventType: "flows.kernel.grant.run.v2",
601
+ requestId,
602
+ runId: runId ?? "",
603
+ planDigest,
604
+ capability: entry.capability,
605
+ pattern,
606
+ ceiling: entry.ceiling.groups,
607
+ scope: "run",
608
+ tier: entry.tier
609
+ })
610
+ };
611
+ }
612
+ case "remembered": {
613
+ if (!isValidGrantPattern(pattern, entry.capability, entry.tier, workspaceRoot)) {
614
+ return yield* import_effect.Effect.fail(invalid("grant pattern exceeds the requested authority"));
615
+ }
616
+ if (configuredRules.length + envelopeRules.length + runRules.length + rememberedRules.length + reservedRules >= maximumRules) {
617
+ return yield* import_effect.Effect.fail(invalid(`rules exceed ${maximumRules} entries`));
618
+ }
619
+ resolving.add(requestId);
620
+ reservedRules += 1;
621
+ return {
622
+ resolution,
623
+ entry,
624
+ rule: snapshotRule(new import_Permission.Rule({ effect: "allow", pattern })),
625
+ event: new import_GrantEvent.RememberedGrant({
626
+ eventType: "flows.kernel.grant.remembered.v1",
627
+ requestId,
628
+ runId: runId ?? "",
629
+ ...planDigest === void 0 ? {} : { planDigest },
630
+ capability: entry.capability,
631
+ pattern,
632
+ scope: "remembered",
633
+ tier: entry.tier
634
+ })
635
+ };
636
+ }
637
+ case "deny": {
638
+ resolving.add(requestId);
639
+ return {
640
+ resolution,
641
+ entry,
642
+ event: new import_GrantEvent.DeniedGrant({
643
+ eventType: "flows.kernel.grant.denied.v1",
644
+ requestId,
645
+ runId: runId ?? "",
646
+ ...planDigest === void 0 ? {} : { planDigest },
647
+ capability: entry.capability,
648
+ pattern,
649
+ scope: "once",
650
+ tier: entry.tier
651
+ })
652
+ };
653
+ }
654
+ }
655
+ });
656
+ const activateReply = (requestId, planned) => import_effect.Effect.gen(function* () {
657
+ resolving.delete(requestId);
658
+ if (planned.resolution === "run" || planned.resolution === "remembered") reservedRules -= 1;
659
+ if (closed) {
660
+ return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "store_closed" }));
661
+ }
662
+ const entry = pending.get(requestId);
663
+ switch (planned.resolution) {
664
+ case "once": {
665
+ if (entry === void 0) {
666
+ return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "request_not_found" }));
667
+ }
668
+ yield* import_effect.Deferred.succeed(entry.deferred, void 0);
669
+ pending.delete(requestId);
670
+ return;
671
+ }
672
+ case "deny": {
673
+ if (entry === void 0) {
674
+ return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "request_not_found" }));
675
+ }
676
+ yield* import_effect.Deferred.fail(
677
+ entry.deferred,
678
+ (0, import_Permission.permissionDenied)(entry.capability, "permission request denied")
679
+ );
680
+ pending.delete(requestId);
681
+ return;
682
+ }
683
+ case "run": {
684
+ runRules.push({ rule: planned.rule, ceiling: planned.entry.ceiling });
685
+ yield* resolveCovered;
686
+ return;
687
+ }
688
+ case "remembered": {
689
+ rememberedRules.push(planned.rule);
690
+ yield* resolveCovered;
691
+ return;
692
+ }
693
+ }
694
+ });
695
+ const reply = import_effect.Effect.fn("GrantStore.reply")(
696
+ (requestId, resolution, suppliedPattern) => import_effect.Effect.uninterruptibleMask(
697
+ (restore) => import_effect.Effect.gen(function* () {
698
+ const planned = yield* mutation.withPermit(planReply(requestId, resolution, suppliedPattern));
699
+ yield* restore(persistEvent(planned.event)).pipe(
700
+ import_effect.Effect.onError(
701
+ () => mutation.withPermit(
702
+ import_effect.Effect.sync(() => {
703
+ resolving.delete(requestId);
704
+ if (planned.resolution === "run" || planned.resolution === "remembered") reservedRules -= 1;
705
+ })
706
+ )
707
+ )
708
+ );
709
+ yield* mutation.withPermit(activateReply(requestId, planned));
710
+ })
711
+ )
712
+ );
713
+ const list = import_effect.Effect.fn("GrantStore.list")(
714
+ () => mutation.withPermit(
715
+ import_effect.Effect.sync(
716
+ () => (
717
+ // Every entry's meta is already the frozen snapshot taken at check
718
+ // time; list copies the array and re-wraps identity without
719
+ // re-walking or re-serializing the metadata graph.
720
+ Object.freeze(Array.from(
721
+ pending.values(),
722
+ ({ requestId, capability, tier, meta }) => Object.freeze({
723
+ requestId,
724
+ capability: snapshotCapability(capability),
725
+ tier,
726
+ meta
727
+ })
728
+ ))
729
+ )
730
+ )
731
+ )
732
+ )();
733
+ const planEnvelope = (envelope) => import_effect.Effect.gen(function* () {
734
+ if (closed) {
735
+ return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "store_closed" }));
736
+ }
737
+ const { patterns, scope } = envelope;
738
+ const signature = envelopeSignature(envelope.planDigest, scope, patterns);
739
+ if (grantedEnvelopes.has(signature)) return { _tag: "Durable" };
740
+ const inFlight = granting.get(signature);
741
+ if (inFlight !== void 0) return { _tag: "Wait", completion: inFlight };
742
+ if (configuredRules.length + envelopeRules.length + runRules.length + rememberedRules.length + reservedRules + patterns.length > maximumRules) {
743
+ return yield* import_effect.Effect.fail(invalid(`rules exceed ${maximumRules} entries`));
744
+ }
745
+ if (grantedEnvelopes.size + granting.size >= maximumRules) {
746
+ return yield* import_effect.Effect.fail(invalid(`grant envelopes exceed ${maximumRules} entries`));
747
+ }
748
+ const completion = yield* import_effect.Deferred.make();
749
+ granting.set(signature, completion);
750
+ reservedRules += patterns.length;
751
+ return {
752
+ _tag: "Fresh",
753
+ signature,
754
+ scope,
755
+ patterns,
756
+ completion,
757
+ event: new import_GrantEvent.EnvelopeGrant({
758
+ eventType: "flows.kernel.grant.envelope.v1",
759
+ runId: runId ?? "",
760
+ planDigest: envelope.planDigest,
761
+ patterns,
762
+ scope
763
+ })
764
+ };
765
+ });
766
+ const activateEnvelope = (planned) => import_effect.Effect.gen(function* () {
767
+ granting.delete(planned.signature);
768
+ reservedRules -= planned.patterns.length;
769
+ if (closed) {
770
+ return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "store_closed" }));
771
+ }
772
+ grantedEnvelopes.add(planned.signature);
773
+ const destination = planned.scope === "remembered" ? rememberedRules : envelopeRules;
774
+ for (const pattern of planned.patterns) {
775
+ destination.push(snapshotRule(new import_Permission.Rule({ effect: "allow", pattern })));
776
+ }
777
+ yield* resolveCovered;
778
+ });
779
+ const grantEnvelope = import_effect.Effect.fn("GrantStore.grantEnvelope")(
780
+ (input) => import_effect.Effect.uninterruptibleMask((restore) => {
781
+ const admit = (prepared) => import_effect.Effect.gen(function* () {
782
+ const planned = yield* mutation.withPermit(planEnvelope(prepared));
783
+ switch (planned._tag) {
784
+ case "Durable":
785
+ return;
786
+ case "Wait": {
787
+ return yield* restore(import_effect.Deferred.await(planned.completion)).pipe(
788
+ import_effect.Effect.catchCause(
789
+ (cause) => import_effect.Cause.hasInterruptsOnly(cause) ? restore(import_effect.Effect.void).pipe(import_effect.Effect.andThen(admit(prepared))) : import_effect.Effect.failCause(cause)
790
+ )
791
+ );
792
+ }
793
+ case "Fresh": {
794
+ yield* restore(persistEvent(planned.event)).pipe(
795
+ import_effect.Effect.onError(
796
+ (cause) => mutation.withPermit(
797
+ import_effect.Effect.gen(function* () {
798
+ granting.delete(planned.signature);
799
+ reservedRules -= planned.patterns.length;
800
+ yield* import_effect.Deferred.failCause(planned.completion, cause);
801
+ })
802
+ )
803
+ )
804
+ );
805
+ yield* mutation.withPermit(activateEnvelope(planned)).pipe(
806
+ import_effect.Effect.tap(() => import_effect.Deferred.succeed(planned.completion, void 0)),
807
+ import_effect.Effect.onError((cause) => import_effect.Deferred.failCause(planned.completion, cause))
808
+ );
809
+ return;
810
+ }
811
+ }
812
+ });
813
+ return import_effect.Effect.gen(function* () {
814
+ const prepared = yield* prepareEnvelope(input, planDigest, workspaceRoot);
815
+ if (prepared.patterns.length === 0) return;
816
+ yield* admit(prepared);
817
+ });
818
+ })
819
+ );
820
+ const policy = mutation.withPermit(import_effect.Effect.gen(function* () {
821
+ if (closed) return yield* import_effect.Effect.fail(new import_Permission.GrantStoreError({ code: "store_closed" }));
822
+ const ceiling = yield* import_CapabilitySet.current;
823
+ const unbounded = (rules) => rules.map(
824
+ (rule) => Object.freeze({ rule: snapshotRule(rule), ceiling: Object.freeze({ groups: Object.freeze([]) }) })
825
+ );
826
+ return Object.freeze({
827
+ ceiling,
828
+ groups: Object.freeze([
829
+ unbounded(configuredRules),
830
+ unbounded(envelopeRules),
831
+ runRules.map(({ rule, ceiling: ceiling2 }) => Object.freeze({ rule: snapshotRule(rule), ceiling: ceiling2 })),
832
+ unbounded(rememberedRules)
833
+ ].map((group) => Object.freeze(group)))
834
+ });
835
+ }));
836
+ return GrantStore.of({ check, reply, list, grantEnvelope, policy });
837
+ });
838
+ const layer = (options = {}) => import_effect.Layer.effect(GrantStore, make(options));
839
+ const makeNoop = GrantStore.of({
840
+ policy: import_CapabilitySet.current.pipe(import_effect.Effect.map(
841
+ (ceiling) => Object.freeze({
842
+ ceiling,
843
+ groups: Object.freeze([Object.freeze(["*", "net:private"].map(
844
+ (action) => Object.freeze({
845
+ rule: snapshotRule(new import_Permission.Rule({ effect: "allow", pattern: new import_Capability.CapabilityPattern({ action, resource: "**" }) })),
846
+ ceiling: Object.freeze({ groups: Object.freeze([]) })
847
+ })
848
+ ))])
849
+ })
850
+ )),
851
+ check: import_effect.Effect.fn("GrantStore.check")(() => import_effect.Effect.void),
852
+ reply: import_effect.Effect.fn("GrantStore.reply")(() => import_effect.Effect.void),
853
+ list: import_effect.Effect.fn("GrantStore.list")(() => import_effect.Effect.succeed([]))(),
854
+ grantEnvelope: import_effect.Effect.fn("GrantStore.grantEnvelope")(() => import_effect.Effect.void)
855
+ });
856
+ const layerNoop = import_effect.Layer.succeed(GrantStore)(makeNoop);
857
+ //# sourceMappingURL=GrantStore.js.map