@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
package/dist/esm/Jj.js ADDED
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Permission-aware Jujutsu version-control operations.
3
+ *
4
+ * There is no kernel `Jj` interface and no kernel `Jj` tag. `@smthrs/jj` owns
5
+ * both, and its error channel already names the failures the kernel adds, so
6
+ * this module is only the middleware: a `Layer` over the same tag that reads
7
+ * the raw service out of context and returns a guarded one in its place.
8
+ *
9
+ * Governing design:
10
+ * `docs/specs/Concepts/Permission Kernel.md`,
11
+ * `docs/specs/Concepts/Effect Taxonomy.md`, and
12
+ * `docs/specs/Concepts/Host Adapters.md`.
13
+ *
14
+ * @since 1.0.0-rc.0
15
+ */
16
+ import { permissionDenied } from "@smthrs/capability/Permission";
17
+ import { Jj } from "@smthrs/jj";
18
+ import { Effect, FileSystem as EffectFileSystem, Layer, Path as EffectPath } from "effect";
19
+ import { canonicalResource } from "./FileSystem.js";
20
+ import { GrantStore } from "./GrantStore.js";
21
+ import { makeCapability } from "./internal/makeCapability.js";
22
+ import { Workspace } from "./Workspace.js";
23
+ /**
24
+ * `@smthrs/jj`'s own Jujutsu tag, unchanged. Re-exported so the kernel
25
+ * namespace stays one-stop; it is the *same* tag, never a second one.
26
+ *
27
+ * @category services
28
+ * @since 1.0.0-rc.0
29
+ */
30
+ export { Jj } from "@smthrs/jj";
31
+ /**
32
+ * Provides an unavailable Jujutsu stub.
33
+ *
34
+ * @category layers
35
+ * @since 1.0.0-rc.0
36
+ */
37
+ export { layerNoop } from "@smthrs/jj";
38
+ /**
39
+ * Constructs a Jujutsu service from an implementation.
40
+ *
41
+ * @category constructors
42
+ * @since 1.0.0-rc.0
43
+ */
44
+ export { make } from "@smthrs/jj";
45
+ /**
46
+ * Constructs an unavailable Jujutsu stub.
47
+ *
48
+ * @category constructors
49
+ * @since 1.0.0-rc.0
50
+ */
51
+ export { makeNoop } from "@smthrs/jj";
52
+ /**
53
+ * Decorates the Jujutsu service in place with operation-specific capability
54
+ * checks.
55
+ *
56
+ * The layer provides the tag it also requires: compose it over a host jj layer
57
+ * with `Layer.provide` and every consumer of `Jj` — including one that never
58
+ * heard of the kernel — resolves the guarded implementation.
59
+ *
60
+ * `workspaceAdd` canonicalizes its destination and `root` canonicalizes its
61
+ * starting directory through the *raw* filesystem before asking for
62
+ * `jj:workspace-add` and `fs:write`, or for `jj:root`, so an existing symlink
63
+ * cannot turn an inside-workspace grant into outside authority or separate the
64
+ * authorized path from the directory jj is run in.
65
+ *
66
+ * @category layers
67
+ * @since 1.0.0-rc.0
68
+ */
69
+ export const layer = Layer.effect(Jj, Effect.gen(function* () {
70
+ const jj = yield* Jj;
71
+ const jjRoot = jj.root;
72
+ const jjRevert = jj.revert;
73
+ const jjOpRestore = jj.opRestore;
74
+ const fileSystem = yield* EffectFileSystem.FileSystem;
75
+ const path = yield* EffectPath.Path;
76
+ const workspace = yield* Workspace;
77
+ const grants = yield* GrantStore;
78
+ const check = (action, resource) => makeCapability(action, resource).pipe(Effect.flatMap((capability) => grants.check(capability)));
79
+ return Jj.of({
80
+ status: Effect.fn("Jj.status")(() => check("jj:status", ".").pipe(Effect.andThen(jj.status()))),
81
+ diff: Effect.fn("Jj.diff")((from, to) => check("jj:diff", `${from}:${to}`).pipe(Effect.andThen(jj.diff(from, to)))),
82
+ snapshot: Effect.fn("Jj.snapshot")((message) => check("jj:snapshot", message ?? "").pipe(Effect.andThen(jj.snapshot(message)))),
83
+ restore: Effect.fn("Jj.restore")((revision) => check("jj:restore", revision).pipe(Effect.andThen(jj.restore(revision)))),
84
+ workspaceAdd: Effect.fn("Jj.workspaceAdd")((name, destination, revision) => canonicalResource(fileSystem, path, workspace.root, destination).pipe(Effect.flatMap((resource) => check("jj:workspace-add", resource).pipe(Effect.andThen(check("fs:write", resource)), Effect.andThen(canonicalResource(fileSystem, path, workspace.root, destination)), Effect.flatMap((settled) => settled === resource
85
+ ? jj.workspaceAdd(name, resource, revision)
86
+ : makeCapability("jj:workspace-add", resource).pipe(Effect.flatMap((capability) => Effect.fail(permissionDenied(capability, "workspace destination no longer names the resource that was authorized"))))))))),
87
+ workspaceForget: Effect.fn("Jj.workspaceForget")((name) => check("jj:workspace-forget", name).pipe(Effect.andThen(jj.workspaceForget(name)))),
88
+ // `root`, `revert`, and `opRestore` are optional on the service, so the decorator
89
+ // forwards the ABSENCE too. A backend that cannot revert must keep
90
+ // reading as a backend that cannot revert: replacing it with a guarded
91
+ // method that fails on call would turn "this host has no revert" into
92
+ // "your revert was refused", which is a different answer to a caller
93
+ // deciding what it can offer.
94
+ ...jjRoot === undefined ? {} : {
95
+ root: Effect.fn("Jj.root")((from) =>
96
+ // Canonicalized BEFORE the check, and the canonical path is what jj
97
+ // is then run in. Checking the caller's spelling and inspecting a
98
+ // different directory is the whole symlink-alias problem: an
99
+ // authorized name would decide the answer for a repository the grant
100
+ // never mentioned.
101
+ canonicalResource(fileSystem, path, workspace.root, from).pipe(Effect.flatMap((resource) => check("jj:root", resource).pipe(Effect.andThen(jjRoot(resource))))))
102
+ },
103
+ ...jjRevert === undefined ? {} : {
104
+ revert: Effect.fn("Jj.revert")((revision) => check("jj:revert", revision).pipe(Effect.andThen(jjRevert(revision))))
105
+ },
106
+ ...jjOpRestore === undefined ? {} : {
107
+ opRestore: Effect.fn("Jj.opRestore")((operationId) => check("jj:op-restore", operationId).pipe(Effect.andThen(jjOpRestore(operationId))))
108
+ }
109
+ });
110
+ }));
111
+ //# sourceMappingURL=Jj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Jj.js","sourceRoot":"","sources":["../../src/Jj.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Permission-aware Jujutsu version-control operations.\n *\n * There is no kernel `Jj` interface and no kernel `Jj` tag. `@smthrs/jj` owns\n * both, and its error channel already names the failures the kernel adds, so\n * this module is only the middleware: a `Layer` over the same tag that reads\n * the raw service out of context and returns a guarded one in its place.\n *\n * Governing design:\n * `docs/specs/Concepts/Permission Kernel.md`,\n * `docs/specs/Concepts/Effect Taxonomy.md`, and\n * `docs/specs/Concepts/Host Adapters.md`.\n *\n * @since 1.0.0-rc.0\n */\n\nimport type { Action } from \"@smthrs/capability/Capability\"\nimport { permissionDenied } from \"@smthrs/capability/Permission\"\nimport { Jj, type OperationId, type Revision } from \"@smthrs/jj\"\nimport { Effect, FileSystem as EffectFileSystem, Layer, Path as EffectPath } from \"effect\"\nimport { canonicalResource } from \"./FileSystem.ts\"\nimport { GrantStore } from \"./GrantStore.ts\"\nimport { makeCapability } from \"./internal/makeCapability.ts\"\nimport { Workspace } from \"./Workspace.ts\"\n\n/**\n * `@smthrs/jj`'s own Jujutsu tag, unchanged. Re-exported so the kernel\n * namespace stays one-stop; it is the *same* tag, never a second one.\n *\n * @category services\n * @since 1.0.0-rc.0\n */\nexport { Jj } from \"@smthrs/jj\"\n\n/**\n * Provides an unavailable Jujutsu stub.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport { layerNoop } from \"@smthrs/jj\"\n\n/**\n * Constructs a Jujutsu service from an implementation.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport { make } from \"@smthrs/jj\"\n\n/**\n * Constructs an unavailable Jujutsu stub.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport { makeNoop } from \"@smthrs/jj\"\n\n/**\n * Decorates the Jujutsu service in place with operation-specific capability\n * checks.\n *\n * The layer provides the tag it also requires: compose it over a host jj layer\n * with `Layer.provide` and every consumer of `Jj` — including one that never\n * heard of the kernel — resolves the guarded implementation.\n *\n * `workspaceAdd` canonicalizes its destination and `root` canonicalizes its\n * starting directory through the *raw* filesystem before asking for\n * `jj:workspace-add` and `fs:write`, or for `jj:root`, so an existing symlink\n * cannot turn an inside-workspace grant into outside authority or separate the\n * authorized path from the directory jj is run in.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layer: Layer.Layer<\n Jj,\n never,\n Jj | EffectFileSystem.FileSystem | EffectPath.Path | Workspace | GrantStore\n> = Layer.effect(\n Jj,\n Effect.gen(function*() {\n const jj = yield* Jj\n const jjRoot = jj.root\n const jjRevert = jj.revert\n const jjOpRestore = jj.opRestore\n const fileSystem = yield* EffectFileSystem.FileSystem\n const path = yield* EffectPath.Path\n const workspace = yield* Workspace\n const grants = yield* GrantStore\n const check = (action: Action, resource: string) =>\n makeCapability(action, resource).pipe(Effect.flatMap((capability) => grants.check(capability)))\n return Jj.of({\n status: Effect.fn(\"Jj.status\")(() => check(\"jj:status\", \".\").pipe(Effect.andThen(jj.status()))),\n diff: Effect.fn(\"Jj.diff\")((from, to) =>\n check(\"jj:diff\", `${from}:${to}`).pipe(Effect.andThen(jj.diff(from, to)))\n ),\n snapshot: Effect.fn(\"Jj.snapshot\")((message) =>\n check(\"jj:snapshot\", message ?? \"\").pipe(Effect.andThen(jj.snapshot(message)))\n ),\n restore: Effect.fn(\"Jj.restore\")((revision) =>\n check(\"jj:restore\", revision).pipe(Effect.andThen(jj.restore(revision)))\n ),\n workspaceAdd: Effect.fn(\"Jj.workspaceAdd\")((name, destination, revision) =>\n canonicalResource(fileSystem, path, workspace.root, destination).pipe(\n Effect.flatMap((resource) =>\n check(\"jj:workspace-add\", resource).pipe(\n Effect.andThen(check(\"fs:write\", resource)),\n Effect.andThen(canonicalResource(fileSystem, path, workspace.root, destination)),\n Effect.flatMap((settled) =>\n settled === resource\n ? jj.workspaceAdd(name, resource, revision)\n : makeCapability(\"jj:workspace-add\", resource).pipe(\n Effect.flatMap((capability) =>\n Effect.fail(permissionDenied(\n capability,\n \"workspace destination no longer names the resource that was authorized\"\n ))\n )\n )\n )\n )\n )\n )\n ),\n workspaceForget: Effect.fn(\"Jj.workspaceForget\")((name) =>\n check(\"jj:workspace-forget\", name).pipe(Effect.andThen(jj.workspaceForget(name)))\n ),\n // `root`, `revert`, and `opRestore` are optional on the service, so the decorator\n // forwards the ABSENCE too. A backend that cannot revert must keep\n // reading as a backend that cannot revert: replacing it with a guarded\n // method that fails on call would turn \"this host has no revert\" into\n // \"your revert was refused\", which is a different answer to a caller\n // deciding what it can offer.\n ...jjRoot === undefined ? {} : {\n root: Effect.fn(\"Jj.root\")((from: string) =>\n // Canonicalized BEFORE the check, and the canonical path is what jj\n // is then run in. Checking the caller's spelling and inspecting a\n // different directory is the whole symlink-alias problem: an\n // authorized name would decide the answer for a repository the grant\n // never mentioned.\n canonicalResource(fileSystem, path, workspace.root, from).pipe(\n Effect.flatMap((resource) => check(\"jj:root\", resource).pipe(Effect.andThen(jjRoot(resource))))\n )\n )\n },\n ...jjRevert === undefined ? {} : {\n revert: Effect.fn(\"Jj.revert\")((revision: Revision) =>\n check(\"jj:revert\", revision).pipe(Effect.andThen(jjRevert(revision)))\n )\n },\n ...jjOpRestore === undefined ? {} : {\n opRestore: Effect.fn(\"Jj.opRestore\")((operationId: OperationId) =>\n check(\"jj:op-restore\", operationId).pipe(Effect.andThen(jjOpRestore(operationId)))\n )\n }\n })\n })\n)\n"]}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Journal-backed persistence and replay for capability decisions.
3
+ *
4
+ * Governing design:
5
+ * `docs/specs/Concepts/Permission Kernel.md` and
6
+ * `docs/specs/Concepts/Journal Queue.md`.
7
+ *
8
+ * @since 1.0.0-rc.0
9
+ */
10
+ import * as Capability from "@smthrs/capability/Capability";
11
+ import { GrantStoreError, Rule } from "@smthrs/capability/Permission";
12
+ import * as JournalModule from "@smthrs/journal/Journal";
13
+ import * as Effect from "effect/Effect";
14
+ import * as Layer from "effect/Layer";
15
+ import * as GrantStore from "./GrantStore.ts";
16
+ import { Workspace } from "./Workspace.ts";
17
+ /**
18
+ * Configuration for a journal-backed grant store.
19
+ *
20
+ * `policyRunId` is a deliberately dedicated run containing remembered-policy
21
+ * events. The journal has no global grant projection, so callers that want
22
+ * remembered grants to span operational runs must keep this id stable.
23
+ * `planDigest` binds run grants and envelopes to the exact active plan.
24
+ * `sourceId` is also checked during replay; events from other producers cannot
25
+ * activate kernel authority.
26
+ *
27
+ * The journal is authoritative permission storage: SqlJournal must use the
28
+ * `reject` overflow policy. Drop-capable overflow policies are unsupported,
29
+ * because a dropped grant decision cannot safely be treated as persisted.
30
+ *
31
+ * @category models
32
+ * @since 1.0.0-rc.0
33
+ */
34
+ export interface JournalGrantStoreOptions {
35
+ readonly runId: string;
36
+ readonly policyRunId: string;
37
+ readonly sourceId: string;
38
+ readonly planDigest: string;
39
+ readonly attended?: boolean;
40
+ readonly rules?: ReadonlyArray<ReadonlyArray<Rule>>;
41
+ readonly envelope?: {
42
+ readonly patterns: ReadonlyArray<Capability.CapabilityPattern>;
43
+ readonly scope?: "run" | "remembered" | undefined;
44
+ };
45
+ }
46
+ /**
47
+ * Makes a grant store that replays remembered policy from, and writes decisions
48
+ * to, the supplied journal.
49
+ *
50
+ * `emitDurable` commits before `GrantStore` activates a remembered or
51
+ * run-scoped rule (or resolves a denial). Any journal failure is mapped to
52
+ * `journal_failed`, so permission decisions fail closed.
53
+ *
54
+ * @category constructors
55
+ * @since 1.0.0-rc.0
56
+ */
57
+ export declare const make: (options: JournalGrantStoreOptions) => Effect.Effect<GrantStore.Service, GrantStoreError, import("effect/Scope").Scope | Workspace | JournalModule.Journal>;
58
+ /**
59
+ * Provides a journal-backed `GrantStore`.
60
+ *
61
+ * @category layers
62
+ * @since 1.0.0-rc.0
63
+ */
64
+ export declare const layer: (options: JournalGrantStoreOptions) => Layer.Layer<GrantStore.GrantStore, GrantStoreError, Workspace | JournalModule.Journal>;
65
+ //# sourceMappingURL=JournalGrantStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JournalGrantStore.d.ts","sourceRoot":"","sources":["../../src/JournalGrantStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,UAAU,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,KAAK,aAAa,MAAM,yBAAyB,CAAA;AAExD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAIrC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;IACnD,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAA;QAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,YAAY,GAAG,SAAS,CAAA;KAClD,CAAA;CACF;AAoPD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,GAAI,SAAS,wBAAwB,yHAmJlD,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,SAAS,wBAAwB,2FAAuD,CAAA"}
@@ -0,0 +1,350 @@
1
+ /**
2
+ * Journal-backed persistence and replay for capability decisions.
3
+ *
4
+ * Governing design:
5
+ * `docs/specs/Concepts/Permission Kernel.md` and
6
+ * `docs/specs/Concepts/Journal Queue.md`.
7
+ *
8
+ * @since 1.0.0-rc.0
9
+ */
10
+ import * as Capability from "@smthrs/capability/Capability";
11
+ import { GrantStoreError, Rule } from "@smthrs/capability/Permission";
12
+ import * as JournalModule from "@smthrs/journal/Journal";
13
+ import * as JournalEvent from "@smthrs/journal/JournalEvent";
14
+ import * as Effect from "effect/Effect";
15
+ import * as Layer from "effect/Layer";
16
+ import * as Schema from "effect/Schema";
17
+ import * as Semaphore from "effect/Semaphore";
18
+ import { decode, GrantEventSchema } from "./GrantEvent.js";
19
+ import * as GrantStore from "./GrantStore.js";
20
+ import { Workspace } from "./Workspace.js";
21
+ const journalFailed = (message, cause) => new GrantStoreError({
22
+ code: "journal_failed",
23
+ message,
24
+ cause
25
+ });
26
+ const knownEventTypes = new Set([
27
+ "flows.kernel.grant.once.v1",
28
+ "flows.kernel.grant.run.v1",
29
+ "flows.kernel.grant.run.v2",
30
+ "flows.kernel.grant.remembered.v1",
31
+ "flows.kernel.grant.denied.v1",
32
+ "flows.kernel.grant.envelope.v1"
33
+ ]);
34
+ const invalidReplay = (message) => new GrantStoreError({ code: "invalid_resolution", message });
35
+ const appendReplayedRule = (rules, seen, pattern) => {
36
+ const rule = new Rule({ effect: "allow", pattern });
37
+ const key = Capability.format(rule.pattern);
38
+ if (seen.has(key))
39
+ return;
40
+ seen.add(key);
41
+ rules.push(rule);
42
+ };
43
+ const validId = (value) => {
44
+ if (typeof value !== "string" || value.length === 0 || value.length > GrantStore.maximumIdentityLength)
45
+ return false;
46
+ for (let index = 0; index < value.length; index += 1) {
47
+ const unit = value.charCodeAt(index);
48
+ if (unit === 0)
49
+ return false;
50
+ if (unit >= 0xd800 && unit <= 0xdbff) {
51
+ const next = value.charCodeAt(index + 1);
52
+ if (!(next >= 0xdc00 && next <= 0xdfff))
53
+ return false;
54
+ index += 1;
55
+ }
56
+ else if (unit >= 0xdc00 && unit <= 0xdfff) {
57
+ return false;
58
+ }
59
+ }
60
+ return true;
61
+ };
62
+ const encodeGrantEvent = Schema.encodeSync(GrantEventSchema);
63
+ // Replay reads two runs (`policyRunId` and `runId`) and journal sequences are
64
+ // per run, so every refusal names the run it was replaying: the operator then
65
+ // knows which journal run to inspect. Replay always reads a run from its start
66
+ // and never consults a checkpoint, so compacting a run it replays makes every
67
+ // later construction fail `compacted`.
68
+ const decodeTrustedEntry = (entry, sourceId, runId) => {
69
+ if (entry.sourceId !== sourceId || !knownEventTypes.has(entry.eventType)) {
70
+ return Effect.succeed(undefined);
71
+ }
72
+ if (entry.eventType === "flows.kernel.grant.run.v1") {
73
+ return Effect.fail(invalidReplay(`legacy run grant has no captured ceiling in run ${runId} at journal sequence ${entry.seq}`));
74
+ }
75
+ const event = decode(entry.payload);
76
+ if (event._tag === "Failure") {
77
+ return Effect.fail(invalidReplay(`invalid grant payload in run ${runId} at journal sequence ${entry.seq}`));
78
+ }
79
+ if (event.success.eventType !== entry.eventType) {
80
+ return Effect.fail(invalidReplay(`grant envelope/payload type mismatch in run ${runId} at journal sequence ${entry.seq}`));
81
+ }
82
+ return Effect.succeed(event.success);
83
+ };
84
+ // One construction-envelope critical section per Journal service instance.
85
+ // Two concurrent constructors sharing one service object can both replay the
86
+ // envelope's absence before either persists it; the lock serializes the
87
+ // re-check-then-append so exactly one construction envelope lands.
88
+ //
89
+ // The guarantee stops at the service object. The lock is a WeakMap keyed by
90
+ // the in-memory `Journal.Service`, and the persisted Input carries no
91
+ // `sourceSeq` or `dedupe` key, so two service objects over one backing journal
92
+ // (two processes, or two connections in one process) each hold their own
93
+ // permit and can both append the envelope. Replay tolerates the duplicate:
94
+ // the signature set collapses it and the rules are identical.
95
+ const constructionLocks = new WeakMap();
96
+ const constructionLock = (journal) => {
97
+ const existing = constructionLocks.get(journal);
98
+ if (existing !== undefined) {
99
+ return existing;
100
+ }
101
+ const created = Semaphore.makeUnsafe(1);
102
+ constructionLocks.set(journal, created);
103
+ return created;
104
+ };
105
+ const replayRememberedRules = (policyRunId, sourceId, workspaceRoot) => Effect.gen(function* () {
106
+ const journal = yield* JournalModule.Journal;
107
+ const rules = [];
108
+ const seenRules = new Set();
109
+ const envelopeSignatures = new Set();
110
+ let after;
111
+ do {
112
+ const page = yield* journal.entries({
113
+ runId: policyRunId,
114
+ ...(after === undefined ? {} : { after }),
115
+ limit: 256
116
+ });
117
+ const last = page.entries.at(-1);
118
+ if (last !== undefined && after !== undefined && last.seq <= after) {
119
+ // A page that does not advance past the cursor it was asked for is
120
+ // corrupt journal output. Following it would replay the same events
121
+ // forever; accepting it would double-apply them. Refuse construction.
122
+ return yield* Effect.fail(invalidReplay(`non-advancing journal page in run ${policyRunId} at sequence ${last.seq}`));
123
+ }
124
+ for (const entry of page.entries) {
125
+ const event = yield* decodeTrustedEntry(entry, sourceId, policyRunId);
126
+ if (event === undefined) {
127
+ continue;
128
+ }
129
+ if (event.eventType === "flows.kernel.grant.remembered.v1") {
130
+ if (!GrantStore.isValidGrantPattern(event.pattern, event.capability, event.tier, workspaceRoot)) {
131
+ return yield* Effect.fail(invalidReplay(`unsafe remembered grant event in run ${policyRunId} at sequence ${entry.seq}`));
132
+ }
133
+ appendReplayedRule(rules, seenRules, event.pattern);
134
+ continue;
135
+ }
136
+ if (event.eventType === "flows.kernel.grant.envelope.v1" && event.scope === "remembered") {
137
+ if (event.patterns.some((pattern) => !GrantStore.isValidEnvelopePattern(pattern, workspaceRoot))) {
138
+ return yield* Effect.fail(invalidReplay(`unsafe remembered envelope event in run ${policyRunId} at sequence ${entry.seq}`));
139
+ }
140
+ for (const pattern of event.patterns)
141
+ appendReplayedRule(rules, seenRules, pattern);
142
+ envelopeSignatures.add(GrantStore.envelopeSignature(event.planDigest, event.scope, event.patterns));
143
+ continue;
144
+ }
145
+ return yield* Effect.fail(invalidReplay(`run-scoped event found in policy journal run ${policyRunId} at sequence ${entry.seq}`));
146
+ }
147
+ after = last?.seq;
148
+ if (!page.hasMore) {
149
+ return { rules, envelopeSignatures };
150
+ }
151
+ } while (after !== undefined);
152
+ return { rules, envelopeSignatures };
153
+ });
154
+ const replayRunRules = (runId, sourceId, planDigest, workspaceRoot) => Effect.gen(function* () {
155
+ const journal = yield* JournalModule.Journal;
156
+ const rules = [];
157
+ const seenRules = new Set();
158
+ const envelopeSignatures = new Set();
159
+ let after;
160
+ do {
161
+ const page = yield* journal.entries({
162
+ runId: runId,
163
+ ...(after === undefined ? {} : { after }),
164
+ limit: 256
165
+ });
166
+ const last = page.entries.at(-1);
167
+ if (last !== undefined && after !== undefined && last.seq <= after) {
168
+ // See the identical guard in `replayRememberedRules`.
169
+ return yield* Effect.fail(invalidReplay(`non-advancing journal page in run ${runId} at sequence ${last.seq}`));
170
+ }
171
+ for (const entry of page.entries) {
172
+ const event = yield* decodeTrustedEntry(entry, sourceId, runId);
173
+ if (event === undefined) {
174
+ continue;
175
+ }
176
+ if (event.runId !== runId) {
177
+ return yield* Effect.fail(invalidReplay(`grant payload run mismatch in run ${runId} at journal sequence ${entry.seq}`));
178
+ }
179
+ if (event.eventType === "flows.kernel.grant.once.v1" || event.eventType === "flows.kernel.grant.denied.v1") {
180
+ continue;
181
+ }
182
+ if (event.eventType === "flows.kernel.grant.run.v2") {
183
+ if (event.planDigest !== planDigest) {
184
+ continue;
185
+ }
186
+ if (!GrantStore.isValidGrantPattern(event.pattern, event.capability, event.tier, workspaceRoot)) {
187
+ return yield* Effect.fail(invalidReplay(`unsafe run grant event in run ${runId} at sequence ${entry.seq}`));
188
+ }
189
+ const key = `run:${JSON.stringify([event.pattern, event.ceiling])}`;
190
+ if (!seenRules.has(key)) {
191
+ seenRules.add(key);
192
+ rules.push({ rule: new Rule({ effect: "allow", pattern: event.pattern }), ceiling: event.ceiling });
193
+ }
194
+ continue;
195
+ }
196
+ if (event.eventType === "flows.kernel.grant.envelope.v1" && event.scope === "run") {
197
+ if (event.planDigest !== planDigest) {
198
+ continue;
199
+ }
200
+ if (event.patterns.some((pattern) => !GrantStore.isValidEnvelopePattern(pattern, workspaceRoot))) {
201
+ return yield* Effect.fail(invalidReplay(`unsafe run envelope event in run ${runId} at sequence ${entry.seq}`));
202
+ }
203
+ for (const pattern of event.patterns)
204
+ appendReplayedRule(rules, seenRules, pattern);
205
+ envelopeSignatures.add(GrantStore.envelopeSignature(event.planDigest, event.scope, event.patterns));
206
+ continue;
207
+ }
208
+ return yield* Effect.fail(invalidReplay(`remembered event found in run journal ${runId} at sequence ${entry.seq}`));
209
+ }
210
+ after = last?.seq;
211
+ if (!page.hasMore) {
212
+ return { rules, envelopeSignatures };
213
+ }
214
+ } while (after !== undefined);
215
+ return { rules, envelopeSignatures };
216
+ });
217
+ /**
218
+ * Makes a grant store that replays remembered policy from, and writes decisions
219
+ * to, the supplied journal.
220
+ *
221
+ * `emitDurable` commits before `GrantStore` activates a remembered or
222
+ * run-scoped rule (or resolves a denial). Any journal failure is mapped to
223
+ * `journal_failed`, so permission decisions fail closed.
224
+ *
225
+ * @category constructors
226
+ * @since 1.0.0-rc.0
227
+ */
228
+ export const make = (options) => Effect.gen(function* () {
229
+ const runId = options.runId;
230
+ const policyRunId = options.policyRunId;
231
+ const sourceId = options.sourceId;
232
+ const planDigest = options.planDigest;
233
+ if (!validId(runId))
234
+ return yield* Effect.fail(invalidReplay("runId must be non-empty, bounded, well-formed text"));
235
+ if (!validId(policyRunId)) {
236
+ return yield* Effect.fail(invalidReplay("policyRunId must be non-empty, bounded, well-formed text"));
237
+ }
238
+ if (!validId(sourceId)) {
239
+ return yield* Effect.fail(invalidReplay("sourceId must be non-empty, bounded, well-formed text"));
240
+ }
241
+ if (!validId(planDigest)) {
242
+ return yield* Effect.fail(invalidReplay("planDigest must be non-empty, bounded, well-formed text"));
243
+ }
244
+ if (runId === policyRunId) {
245
+ return yield* Effect.fail(invalidReplay("runId and policyRunId must be distinct"));
246
+ }
247
+ const journal = yield* JournalModule.Journal;
248
+ const workspace = yield* Workspace;
249
+ const replayPolicy = replayRememberedRules(policyRunId, sourceId, workspace.root).pipe(Effect.mapError((cause) => cause instanceof GrantStoreError ? cause : journalFailed("could not replay remembered grants", cause)));
250
+ const replayRun = replayRunRules(runId, sourceId, planDigest, workspace.root).pipe(Effect.mapError((cause) => cause instanceof GrantStoreError ? cause : journalFailed("could not replay run grants", cause)));
251
+ let replayedPolicy = yield* replayPolicy;
252
+ let replayedRun = yield* replayRun;
253
+ const persist = (event) => {
254
+ const payload = encodeGrantEvent(event);
255
+ return Effect.gen(function* () {
256
+ // Unfenced: the grant store is the kernel's own ledger, not a run's
257
+ // lifecycle — it owns no run, and grant admissions are
258
+ // first-writer-wins records replayed by every later process.
259
+ yield* journal.emitDurableUnfenced(new JournalEvent.Input({
260
+ runId: (event.eventType === "flows.kernel.grant.remembered.v1"
261
+ || (event.eventType === "flows.kernel.grant.envelope.v1" && event.scope === "remembered")
262
+ ? policyRunId
263
+ : runId),
264
+ sourceId: sourceId,
265
+ eventType: event.eventType,
266
+ payload
267
+ }));
268
+ }).pipe(Effect.mapError((cause) => journalFailed("could not persist grant event", cause)), Effect.asVoid);
269
+ };
270
+ const envelope = options.envelope;
271
+ const build = () => {
272
+ const configuredCount = options.rules?.reduce((count, rules) => count + rules.length, 0) ?? 0;
273
+ const rememberedCount = replayedPolicy.rules.length;
274
+ const runCount = replayedRun.rules.length;
275
+ const total = configuredCount + rememberedCount + runCount;
276
+ if (total > GrantStore.maximumRules) {
277
+ return Effect.fail(invalidReplay(`policy run ${policyRunId} replayed ${rememberedCount} remembered rules; configured rules (${configuredCount}) and replayed run rules (${runCount}) bring the total to ${total}, which exceeds the ${GrantStore.maximumRules}-rule ceiling; remove configured rules or start a new policyRunId and grant again`));
278
+ }
279
+ // Envelope signatures accumulate in the policy journal exactly the way
280
+ // remembered rules do, and `GrantStore.make` refuses more than
281
+ // `maximumRules` of them with a message that names neither the journal
282
+ // nor the counts. Diagnose it here instead, so an operator reading the
283
+ // failure knows which policy run filled rather than which field overflowed.
284
+ const policySignatureCount = replayedPolicy.envelopeSignatures.size;
285
+ const runSignatureCount = replayedRun.envelopeSignatures.size;
286
+ const signatureTotal = policySignatureCount + runSignatureCount;
287
+ if (signatureTotal > GrantStore.maximumRules) {
288
+ return Effect.fail(invalidReplay(`policy run ${policyRunId} replayed ${policySignatureCount} remembered envelope signatures; replayed run envelope signatures (${runSignatureCount}) bring the total to ${signatureTotal}, which exceeds the ${GrantStore.maximumRules}-envelope-signature ceiling; start a new policyRunId and grant again`));
289
+ }
290
+ // GrantStore's shared envelope admission adds only patterns not already
291
+ // replayed for a seeded envelope, and checks the combined rule and
292
+ // signature counts before any construction event is persisted.
293
+ return GrantStore.make({
294
+ runId,
295
+ planDigest,
296
+ ...(options.attended === undefined ? {} : { attended: options.attended }),
297
+ rules: options.rules === undefined || options.rules.length === 0
298
+ ? [[], replayedPolicy.rules]
299
+ : [...options.rules, replayedPolicy.rules],
300
+ runRules: replayedRun.rules,
301
+ envelopeSignatures: [
302
+ ...replayedPolicy.envelopeSignatures,
303
+ ...replayedRun.envelopeSignatures
304
+ ],
305
+ ...(envelope === undefined ? {} : {
306
+ envelope: {
307
+ planDigest,
308
+ patterns: envelope.patterns,
309
+ ...(envelope.scope === undefined ? {} : { scope: envelope.scope })
310
+ }
311
+ }),
312
+ persist
313
+ });
314
+ };
315
+ if (envelope === undefined || envelope.patterns.length === 0) {
316
+ return yield* build();
317
+ }
318
+ const scope = envelope.scope ?? "run";
319
+ const signature = GrantStore.envelopeSignature(planDigest, scope, envelope.patterns);
320
+ const replayedSignatures = scope === "remembered"
321
+ ? replayedPolicy.envelopeSignatures
322
+ : replayedRun.envelopeSignatures;
323
+ if (replayedSignatures.has(signature)) {
324
+ // Already durable: the seeded signature makes the construction envelope
325
+ // activate without persisting again.
326
+ return yield* build();
327
+ }
328
+ // The envelope was absent when this constructor replayed, but a concurrent
329
+ // constructor sharing this Journal service may persist it before we do.
330
+ // Re-replay the target run inside the per-service critical section, so
331
+ // exactly one such constructor appends the envelope and every other one
332
+ // replays it instead.
333
+ return yield* constructionLock(journal).withPermit(Effect.gen(function* () {
334
+ if (scope === "remembered") {
335
+ replayedPolicy = yield* replayPolicy;
336
+ }
337
+ else {
338
+ replayedRun = yield* replayRun;
339
+ }
340
+ return yield* build();
341
+ }));
342
+ });
343
+ /**
344
+ * Provides a journal-backed `GrantStore`.
345
+ *
346
+ * @category layers
347
+ * @since 1.0.0-rc.0
348
+ */
349
+ export const layer = (options) => Layer.effect(GrantStore.GrantStore)(make(options));
350
+ //# sourceMappingURL=JournalGrantStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JournalGrantStore.js","sourceRoot":"","sources":["../../src/JournalGrantStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Journal-backed persistence and replay for capability decisions.\n *\n * Governing design:\n * `docs/specs/Concepts/Permission Kernel.md` and\n * `docs/specs/Concepts/Journal Queue.md`.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Capability from \"@smthrs/capability/Capability\"\nimport { GrantStoreError, Rule } from \"@smthrs/capability/Permission\"\nimport * as JournalModule from \"@smthrs/journal/Journal\"\nimport * as JournalEvent from \"@smthrs/journal/JournalEvent\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as Semaphore from \"effect/Semaphore\"\nimport { decode, type GrantEvent, GrantEventSchema } from \"./GrantEvent.ts\"\nimport * as GrantStore from \"./GrantStore.ts\"\nimport { Workspace } from \"./Workspace.ts\"\n\n/**\n * Configuration for a journal-backed grant store.\n *\n * `policyRunId` is a deliberately dedicated run containing remembered-policy\n * events. The journal has no global grant projection, so callers that want\n * remembered grants to span operational runs must keep this id stable.\n * `planDigest` binds run grants and envelopes to the exact active plan.\n * `sourceId` is also checked during replay; events from other producers cannot\n * activate kernel authority.\n *\n * The journal is authoritative permission storage: SqlJournal must use the\n * `reject` overflow policy. Drop-capable overflow policies are unsupported,\n * because a dropped grant decision cannot safely be treated as persisted.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface JournalGrantStoreOptions {\n readonly runId: string\n readonly policyRunId: string\n readonly sourceId: string\n readonly planDigest: string\n readonly attended?: boolean\n readonly rules?: ReadonlyArray<ReadonlyArray<Rule>>\n readonly envelope?: {\n readonly patterns: ReadonlyArray<Capability.CapabilityPattern>\n readonly scope?: \"run\" | \"remembered\" | undefined\n }\n}\n\nconst journalFailed = (message: string, cause: unknown): GrantStoreError =>\n new GrantStoreError({\n code: \"journal_failed\",\n message,\n cause\n })\n\nconst knownEventTypes: ReadonlySet<string> = new Set([\n \"flows.kernel.grant.once.v1\",\n \"flows.kernel.grant.run.v1\",\n \"flows.kernel.grant.run.v2\",\n \"flows.kernel.grant.remembered.v1\",\n \"flows.kernel.grant.denied.v1\",\n \"flows.kernel.grant.envelope.v1\"\n])\n\nconst invalidReplay = (message: string): GrantStoreError => new GrantStoreError({ code: \"invalid_resolution\", message })\n\nconst appendReplayedRule = (\n rules: Array<NonNullable<GrantStore.MakeOptions[\"runRules\"]>[number]>,\n seen: Set<string>,\n pattern: Capability.CapabilityPattern\n): void => {\n const rule = new Rule({ effect: \"allow\", pattern })\n const key = Capability.format(rule.pattern)\n if (seen.has(key)) return\n seen.add(key)\n rules.push(rule)\n}\n\nconst validId = (value: unknown): value is string => {\n if (typeof value !== \"string\" || value.length === 0 || value.length > GrantStore.maximumIdentityLength) return false\n for (let index = 0; index < value.length; index += 1) {\n const unit = value.charCodeAt(index)\n if (unit === 0) return false\n if (unit >= 0xd800 && unit <= 0xdbff) {\n const next = value.charCodeAt(index + 1)\n if (!(next >= 0xdc00 && next <= 0xdfff)) return false\n index += 1\n } else if (unit >= 0xdc00 && unit <= 0xdfff) {\n return false\n }\n }\n return true\n}\n\nconst encodeGrantEvent = Schema.encodeSync(GrantEventSchema)\n\n// Replay reads two runs (`policyRunId` and `runId`) and journal sequences are\n// per run, so every refusal names the run it was replaying: the operator then\n// knows which journal run to inspect. Replay always reads a run from its start\n// and never consults a checkpoint, so compacting a run it replays makes every\n// later construction fail `compacted`.\nconst decodeTrustedEntry = (\n entry: JournalEvent.Entry,\n sourceId: string,\n runId: string\n): Effect.Effect<GrantEvent | undefined, GrantStoreError> => {\n if (entry.sourceId !== sourceId || !knownEventTypes.has(entry.eventType)) {\n return Effect.succeed(undefined)\n }\n if (entry.eventType === \"flows.kernel.grant.run.v1\") {\n return Effect.fail(\n invalidReplay(`legacy run grant has no captured ceiling in run ${runId} at journal sequence ${entry.seq}`)\n )\n }\n const event = decode(entry.payload)\n if (event._tag === \"Failure\") {\n return Effect.fail(invalidReplay(`invalid grant payload in run ${runId} at journal sequence ${entry.seq}`))\n }\n if (event.success.eventType !== entry.eventType) {\n return Effect.fail(\n invalidReplay(`grant envelope/payload type mismatch in run ${runId} at journal sequence ${entry.seq}`)\n )\n }\n return Effect.succeed(event.success)\n}\n\n// One construction-envelope critical section per Journal service instance.\n// Two concurrent constructors sharing one service object can both replay the\n// envelope's absence before either persists it; the lock serializes the\n// re-check-then-append so exactly one construction envelope lands.\n//\n// The guarantee stops at the service object. The lock is a WeakMap keyed by\n// the in-memory `Journal.Service`, and the persisted Input carries no\n// `sourceSeq` or `dedupe` key, so two service objects over one backing journal\n// (two processes, or two connections in one process) each hold their own\n// permit and can both append the envelope. Replay tolerates the duplicate:\n// the signature set collapses it and the rules are identical.\nconst constructionLocks = new WeakMap<JournalModule.Service, Semaphore.Semaphore>()\n\nconst constructionLock = (journal: JournalModule.Service): Semaphore.Semaphore => {\n const existing = constructionLocks.get(journal)\n if (existing !== undefined) {\n return existing\n }\n const created = Semaphore.makeUnsafe(1)\n constructionLocks.set(journal, created)\n return created\n}\n\nconst replayRememberedRules = (\n policyRunId: string,\n sourceId: string,\n workspaceRoot: string\n) =>\n Effect.gen(function*() {\n const journal = yield* JournalModule.Journal\n const rules: Array<Rule> = []\n const seenRules = new Set<string>()\n const envelopeSignatures = new Set<string>()\n let after: JournalEvent.Seq | undefined\n\n do {\n const page = yield* journal.entries({\n runId: policyRunId as JournalEvent.RunId,\n ...(after === undefined ? {} : { after }),\n limit: 256\n })\n const last = page.entries.at(-1)\n if (last !== undefined && after !== undefined && last.seq <= after) {\n // A page that does not advance past the cursor it was asked for is\n // corrupt journal output. Following it would replay the same events\n // forever; accepting it would double-apply them. Refuse construction.\n return yield* Effect.fail(\n invalidReplay(`non-advancing journal page in run ${policyRunId} at sequence ${last.seq}`)\n )\n }\n for (const entry of page.entries) {\n const event = yield* decodeTrustedEntry(entry, sourceId, policyRunId)\n if (event === undefined) {\n continue\n }\n if (event.eventType === \"flows.kernel.grant.remembered.v1\") {\n if (!GrantStore.isValidGrantPattern(event.pattern, event.capability, event.tier, workspaceRoot)) {\n return yield* Effect.fail(\n invalidReplay(`unsafe remembered grant event in run ${policyRunId} at sequence ${entry.seq}`)\n )\n }\n appendReplayedRule(rules, seenRules, event.pattern)\n continue\n }\n if (event.eventType === \"flows.kernel.grant.envelope.v1\" && event.scope === \"remembered\") {\n if (event.patterns.some((pattern) => !GrantStore.isValidEnvelopePattern(pattern, workspaceRoot))) {\n return yield* Effect.fail(\n invalidReplay(`unsafe remembered envelope event in run ${policyRunId} at sequence ${entry.seq}`)\n )\n }\n for (const pattern of event.patterns) appendReplayedRule(rules, seenRules, pattern)\n envelopeSignatures.add(GrantStore.envelopeSignature(event.planDigest, event.scope, event.patterns))\n continue\n }\n return yield* Effect.fail(\n invalidReplay(`run-scoped event found in policy journal run ${policyRunId} at sequence ${entry.seq}`)\n )\n }\n after = last?.seq\n if (!page.hasMore) {\n return { rules, envelopeSignatures }\n }\n } while (after !== undefined)\n\n return { rules, envelopeSignatures }\n })\n\nconst replayRunRules = (\n runId: string,\n sourceId: string,\n planDigest: string,\n workspaceRoot: string\n) =>\n Effect.gen(function*() {\n const journal = yield* JournalModule.Journal\n const rules: Array<NonNullable<GrantStore.MakeOptions[\"runRules\"]>[number]> = []\n const seenRules = new Set<string>()\n const envelopeSignatures = new Set<string>()\n let after: JournalEvent.Seq | undefined\n\n do {\n const page = yield* journal.entries({\n runId: runId as JournalEvent.RunId,\n ...(after === undefined ? {} : { after }),\n limit: 256\n })\n const last = page.entries.at(-1)\n if (last !== undefined && after !== undefined && last.seq <= after) {\n // See the identical guard in `replayRememberedRules`.\n return yield* Effect.fail(invalidReplay(`non-advancing journal page in run ${runId} at sequence ${last.seq}`))\n }\n for (const entry of page.entries) {\n const event = yield* decodeTrustedEntry(entry, sourceId, runId)\n if (event === undefined) {\n continue\n }\n if (event.runId !== runId) {\n return yield* Effect.fail(\n invalidReplay(`grant payload run mismatch in run ${runId} at journal sequence ${entry.seq}`)\n )\n }\n if (event.eventType === \"flows.kernel.grant.once.v1\" || event.eventType === \"flows.kernel.grant.denied.v1\") {\n continue\n }\n if (event.eventType === \"flows.kernel.grant.run.v2\") {\n if (event.planDigest !== planDigest) {\n continue\n }\n if (!GrantStore.isValidGrantPattern(event.pattern, event.capability, event.tier, workspaceRoot)) {\n return yield* Effect.fail(invalidReplay(`unsafe run grant event in run ${runId} at sequence ${entry.seq}`))\n }\n const key = `run:${JSON.stringify([event.pattern, event.ceiling])}`\n if (!seenRules.has(key)) {\n seenRules.add(key)\n rules.push({ rule: new Rule({ effect: \"allow\", pattern: event.pattern }), ceiling: event.ceiling })\n }\n continue\n }\n if (event.eventType === \"flows.kernel.grant.envelope.v1\" && event.scope === \"run\") {\n if (event.planDigest !== planDigest) {\n continue\n }\n if (event.patterns.some((pattern) => !GrantStore.isValidEnvelopePattern(pattern, workspaceRoot))) {\n return yield* Effect.fail(\n invalidReplay(`unsafe run envelope event in run ${runId} at sequence ${entry.seq}`)\n )\n }\n for (const pattern of event.patterns) appendReplayedRule(rules, seenRules, pattern)\n envelopeSignatures.add(GrantStore.envelopeSignature(event.planDigest, event.scope, event.patterns))\n continue\n }\n return yield* Effect.fail(\n invalidReplay(`remembered event found in run journal ${runId} at sequence ${entry.seq}`)\n )\n }\n after = last?.seq\n if (!page.hasMore) {\n return { rules, envelopeSignatures }\n }\n } while (after !== undefined)\n\n return { rules, envelopeSignatures }\n })\n\n/**\n * Makes a grant store that replays remembered policy from, and writes decisions\n * to, the supplied journal.\n *\n * `emitDurable` commits before `GrantStore` activates a remembered or\n * run-scoped rule (or resolves a denial). Any journal failure is mapped to\n * `journal_failed`, so permission decisions fail closed.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const make = (options: JournalGrantStoreOptions) =>\n Effect.gen(function*() {\n const runId = options.runId\n const policyRunId = options.policyRunId\n const sourceId = options.sourceId\n const planDigest = options.planDigest\n if (!validId(runId)) return yield* Effect.fail(invalidReplay(\"runId must be non-empty, bounded, well-formed text\"))\n if (!validId(policyRunId)) {\n return yield* Effect.fail(invalidReplay(\"policyRunId must be non-empty, bounded, well-formed text\"))\n }\n if (!validId(sourceId)) {\n return yield* Effect.fail(invalidReplay(\"sourceId must be non-empty, bounded, well-formed text\"))\n }\n if (!validId(planDigest)) {\n return yield* Effect.fail(invalidReplay(\"planDigest must be non-empty, bounded, well-formed text\"))\n }\n if (runId === policyRunId) {\n return yield* Effect.fail(invalidReplay(\"runId and policyRunId must be distinct\"))\n }\n const journal = yield* JournalModule.Journal\n const workspace = yield* Workspace\n const replayPolicy = replayRememberedRules(\n policyRunId,\n sourceId,\n workspace.root\n ).pipe(\n Effect.mapError((cause) =>\n cause instanceof GrantStoreError ? cause : journalFailed(\"could not replay remembered grants\", cause)\n )\n )\n const replayRun = replayRunRules(\n runId,\n sourceId,\n planDigest,\n workspace.root\n ).pipe(\n Effect.mapError((cause) =>\n cause instanceof GrantStoreError ? cause : journalFailed(\"could not replay run grants\", cause)\n )\n )\n let replayedPolicy = yield* replayPolicy\n let replayedRun = yield* replayRun\n const persist = (event: GrantEvent): Effect.Effect<void, GrantStoreError> => {\n const payload = encodeGrantEvent(event)\n return Effect.gen(function*() {\n // Unfenced: the grant store is the kernel's own ledger, not a run's\n // lifecycle — it owns no run, and grant admissions are\n // first-writer-wins records replayed by every later process.\n yield* journal.emitDurableUnfenced(\n new JournalEvent.Input({\n runId: (\n event.eventType === \"flows.kernel.grant.remembered.v1\"\n || (event.eventType === \"flows.kernel.grant.envelope.v1\" && event.scope === \"remembered\")\n ? policyRunId\n : runId\n ) as JournalEvent.RunId,\n sourceId: sourceId as JournalEvent.SourceId,\n eventType: event.eventType,\n payload\n })\n )\n }).pipe(\n Effect.mapError((cause) => journalFailed(\"could not persist grant event\", cause)),\n Effect.asVoid\n )\n }\n const envelope = options.envelope\n const build = () => {\n const configuredCount = options.rules?.reduce((count, rules) => count + rules.length, 0) ?? 0\n const rememberedCount = replayedPolicy.rules.length\n const runCount = replayedRun.rules.length\n const total = configuredCount + rememberedCount + runCount\n if (total > GrantStore.maximumRules) {\n return Effect.fail(\n invalidReplay(\n `policy run ${policyRunId} replayed ${rememberedCount} remembered rules; configured rules (${configuredCount}) and replayed run rules (${runCount}) bring the total to ${total}, which exceeds the ${GrantStore.maximumRules}-rule ceiling; remove configured rules or start a new policyRunId and grant again`\n )\n )\n }\n // Envelope signatures accumulate in the policy journal exactly the way\n // remembered rules do, and `GrantStore.make` refuses more than\n // `maximumRules` of them with a message that names neither the journal\n // nor the counts. Diagnose it here instead, so an operator reading the\n // failure knows which policy run filled rather than which field overflowed.\n const policySignatureCount = replayedPolicy.envelopeSignatures.size\n const runSignatureCount = replayedRun.envelopeSignatures.size\n const signatureTotal = policySignatureCount + runSignatureCount\n if (signatureTotal > GrantStore.maximumRules) {\n return Effect.fail(\n invalidReplay(\n `policy run ${policyRunId} replayed ${policySignatureCount} remembered envelope signatures; replayed run envelope signatures (${runSignatureCount}) bring the total to ${signatureTotal}, which exceeds the ${GrantStore.maximumRules}-envelope-signature ceiling; start a new policyRunId and grant again`\n )\n )\n }\n // GrantStore's shared envelope admission adds only patterns not already\n // replayed for a seeded envelope, and checks the combined rule and\n // signature counts before any construction event is persisted.\n return GrantStore.make({\n runId,\n planDigest,\n ...(options.attended === undefined ? {} : { attended: options.attended }),\n rules: options.rules === undefined || options.rules.length === 0\n ? [[], replayedPolicy.rules]\n : [...options.rules, replayedPolicy.rules],\n runRules: replayedRun.rules,\n envelopeSignatures: [\n ...replayedPolicy.envelopeSignatures,\n ...replayedRun.envelopeSignatures\n ],\n ...(envelope === undefined ? {} : {\n envelope: {\n planDigest,\n patterns: envelope.patterns,\n ...(envelope.scope === undefined ? {} : { scope: envelope.scope })\n }\n }),\n persist\n })\n }\n if (envelope === undefined || envelope.patterns.length === 0) {\n return yield* build()\n }\n const scope = envelope.scope ?? \"run\"\n const signature = GrantStore.envelopeSignature(planDigest, scope, envelope.patterns)\n const replayedSignatures = scope === \"remembered\"\n ? replayedPolicy.envelopeSignatures\n : replayedRun.envelopeSignatures\n if (replayedSignatures.has(signature)) {\n // Already durable: the seeded signature makes the construction envelope\n // activate without persisting again.\n return yield* build()\n }\n // The envelope was absent when this constructor replayed, but a concurrent\n // constructor sharing this Journal service may persist it before we do.\n // Re-replay the target run inside the per-service critical section, so\n // exactly one such constructor appends the envelope and every other one\n // replays it instead.\n return yield* constructionLock(journal).withPermit(\n Effect.gen(function*() {\n if (scope === \"remembered\") {\n replayedPolicy = yield* replayPolicy\n } else {\n replayedRun = yield* replayRun\n }\n return yield* build()\n })\n )\n })\n\n/**\n * Provides a journal-backed `GrantStore`.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layer = (options: JournalGrantStoreOptions) => Layer.effect(GrantStore.GrantStore)(make(options))\n"]}