@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.
- package/CHANGELOG.md +206 -0
- package/LICENSE +21 -0
- package/README.md +154 -2
- package/dist/cjs/CapabilitySet.d.ts +6 -0
- package/dist/cjs/CapabilitySet.d.ts.map +1 -0
- package/dist/cjs/CapabilitySet.js +19 -0
- package/dist/cjs/CapabilitySet.js.map +7 -0
- package/dist/cjs/ChildProcessEnvironment.d.ts +60 -0
- package/dist/cjs/ChildProcessEnvironment.d.ts.map +1 -0
- package/dist/cjs/ChildProcessEnvironment.js +61 -0
- package/dist/cjs/ChildProcessEnvironment.js.map +7 -0
- package/dist/cjs/ChildProcessSpawner.d.ts +100 -0
- package/dist/cjs/ChildProcessSpawner.d.ts.map +1 -0
- package/dist/cjs/ChildProcessSpawner.js +291 -0
- package/dist/cjs/ChildProcessSpawner.js.map +7 -0
- package/dist/cjs/CommandLine.d.ts +173 -0
- package/dist/cjs/CommandLine.d.ts.map +1 -0
- package/dist/cjs/CommandLine.js +54 -0
- package/dist/cjs/CommandLine.js.map +7 -0
- package/dist/cjs/ContainedSpawner.d.ts +135 -0
- package/dist/cjs/ContainedSpawner.d.ts.map +1 -0
- package/dist/cjs/ContainedSpawner.js +141 -0
- package/dist/cjs/ContainedSpawner.js.map +7 -0
- package/dist/cjs/FileSystem.d.ts +452 -0
- package/dist/cjs/FileSystem.d.ts.map +1 -0
- package/dist/cjs/FileSystem.js +890 -0
- package/dist/cjs/FileSystem.js.map +7 -0
- package/dist/cjs/FileSystemBatch.d.ts +94 -0
- package/dist/cjs/FileSystemBatch.d.ts.map +1 -0
- package/dist/cjs/FileSystemBatch.js +31 -0
- package/dist/cjs/FileSystemBatch.js.map +7 -0
- package/dist/cjs/GrantEvent.d.ts +240 -0
- package/dist/cjs/GrantEvent.d.ts.map +1 -0
- package/dist/cjs/GrantEvent.js +115 -0
- package/dist/cjs/GrantEvent.js.map +7 -0
- package/dist/cjs/GrantStore.d.ts +270 -0
- package/dist/cjs/GrantStore.d.ts.map +1 -0
- package/dist/cjs/GrantStore.js +857 -0
- package/dist/cjs/GrantStore.js.map +7 -0
- package/dist/cjs/HostError.d.ts +36 -0
- package/dist/cjs/HostError.d.ts.map +1 -0
- package/dist/cjs/HostError.js +17 -0
- package/dist/cjs/HostError.js.map +7 -0
- package/dist/cjs/HostServices.d.ts +101 -0
- package/dist/cjs/HostServices.d.ts.map +1 -0
- package/dist/cjs/HostServices.js +66 -0
- package/dist/cjs/HostServices.js.map +7 -0
- package/dist/cjs/HttpClient.d.ts +177 -0
- package/dist/cjs/HttpClient.d.ts.map +1 -0
- package/dist/cjs/HttpClient.js +217 -0
- package/dist/cjs/HttpClient.js.map +7 -0
- package/dist/cjs/Jj.d.ts +67 -0
- package/dist/cjs/Jj.d.ts.map +1 -0
- package/dist/cjs/Jj.js +118 -0
- package/dist/cjs/Jj.js.map +7 -0
- package/dist/cjs/JournalGrantStore.d.ts +65 -0
- package/dist/cjs/JournalGrantStore.d.ts.map +1 -0
- package/dist/cjs/JournalGrantStore.js +354 -0
- package/dist/cjs/JournalGrantStore.js.map +7 -0
- package/dist/cjs/Path.d.ts +37 -0
- package/dist/cjs/Path.d.ts.map +1 -0
- package/dist/cjs/Path.js +46 -0
- package/dist/cjs/Path.js.map +7 -0
- package/dist/cjs/ProcessConfinement.d.ts +55 -0
- package/dist/cjs/ProcessConfinement.d.ts.map +1 -0
- package/dist/cjs/ProcessConfinement.js +94 -0
- package/dist/cjs/ProcessConfinement.js.map +7 -0
- package/dist/cjs/ProcessLedger.d.ts +260 -0
- package/dist/cjs/ProcessLedger.d.ts.map +1 -0
- package/dist/cjs/ProcessLedger.js +190 -0
- package/dist/cjs/ProcessLedger.js.map +7 -0
- package/dist/cjs/Rooted.d.ts +53 -0
- package/dist/cjs/Rooted.d.ts.map +1 -0
- package/dist/cjs/Rooted.js +101 -0
- package/dist/cjs/Rooted.js.map +7 -0
- package/dist/cjs/Unreachable.d.ts +27 -0
- package/dist/cjs/Unreachable.d.ts.map +1 -0
- package/dist/cjs/Unreachable.js +43 -0
- package/dist/cjs/Unreachable.js.map +7 -0
- package/dist/cjs/Workspace.d.ts +62 -0
- package/dist/cjs/Workspace.d.ts.map +1 -0
- package/dist/cjs/Workspace.js +35 -0
- package/dist/cjs/Workspace.js.map +7 -0
- package/dist/cjs/index.d.ts +161 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +73 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/Containment.d.ts +24 -0
- package/dist/cjs/internal/Containment.d.ts.map +1 -0
- package/dist/cjs/internal/Containment.js +30 -0
- package/dist/cjs/internal/Containment.js.map +7 -0
- package/dist/cjs/internal/Pipeline.d.ts +18 -0
- package/dist/cjs/internal/Pipeline.d.ts.map +1 -0
- package/dist/cjs/internal/Pipeline.js +90 -0
- package/dist/cjs/internal/Pipeline.js.map +7 -0
- package/dist/cjs/internal/makeCapability.d.ts +16 -0
- package/dist/cjs/internal/makeCapability.d.ts.map +1 -0
- package/dist/cjs/internal/makeCapability.js +44 -0
- package/dist/cjs/internal/makeCapability.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/FileSystemBatchContract.d.ts +15 -0
- package/dist/cjs/test/FileSystemBatchContract.d.ts.map +1 -0
- package/dist/cjs/test/FileSystemBatchContract.js +76 -0
- package/dist/cjs/test/FileSystemBatchContract.js.map +7 -0
- package/dist/cjs/test/HostContract.d.ts +225 -0
- package/dist/cjs/test/HostContract.d.ts.map +1 -0
- package/dist/cjs/test/HostContract.js +489 -0
- package/dist/cjs/test/HostContract.js.map +7 -0
- package/dist/cjs/test/TestGrantStore.d.ts +31 -0
- package/dist/cjs/test/TestGrantStore.d.ts.map +1 -0
- package/dist/cjs/test/TestGrantStore.js +65 -0
- package/dist/cjs/test/TestGrantStore.js.map +7 -0
- package/dist/esm/CapabilitySet.d.ts +6 -0
- package/dist/esm/CapabilitySet.d.ts.map +1 -0
- package/dist/esm/CapabilitySet.js +6 -0
- package/dist/esm/CapabilitySet.js.map +1 -0
- package/dist/esm/ChildProcessEnvironment.d.ts +60 -0
- package/dist/esm/ChildProcessEnvironment.d.ts.map +1 -0
- package/dist/esm/ChildProcessEnvironment.js +91 -0
- package/dist/esm/ChildProcessEnvironment.js.map +1 -0
- package/dist/esm/ChildProcessSpawner.d.ts +100 -0
- package/dist/esm/ChildProcessSpawner.d.ts.map +1 -0
- package/dist/esm/ChildProcessSpawner.js +342 -0
- package/dist/esm/ChildProcessSpawner.js.map +1 -0
- package/dist/esm/CommandLine.d.ts +173 -0
- package/dist/esm/CommandLine.d.ts.map +1 -0
- package/dist/esm/CommandLine.js +195 -0
- package/dist/esm/CommandLine.js.map +1 -0
- package/dist/esm/ContainedSpawner.d.ts +135 -0
- package/dist/esm/ContainedSpawner.d.ts.map +1 -0
- package/dist/esm/ContainedSpawner.js +180 -0
- package/dist/esm/ContainedSpawner.js.map +1 -0
- package/dist/esm/FileSystem.d.ts +452 -0
- package/dist/esm/FileSystem.d.ts.map +1 -0
- package/dist/esm/FileSystem.js +989 -0
- package/dist/esm/FileSystem.js.map +1 -0
- package/dist/esm/FileSystemBatch.d.ts +94 -0
- package/dist/esm/FileSystemBatch.d.ts.map +1 -0
- package/dist/esm/FileSystemBatch.js +26 -0
- package/dist/esm/FileSystemBatch.js.map +1 -0
- package/dist/esm/GrantEvent.d.ts +240 -0
- package/dist/esm/GrantEvent.d.ts.map +1 -0
- package/dist/esm/GrantEvent.js +155 -0
- package/dist/esm/GrantEvent.js.map +1 -0
- package/dist/esm/GrantStore.d.ts +270 -0
- package/dist/esm/GrantStore.d.ts.map +1 -0
- package/dist/esm/GrantStore.js +951 -0
- package/dist/esm/GrantStore.js.map +1 -0
- package/dist/esm/HostError.d.ts +36 -0
- package/dist/esm/HostError.d.ts.map +1 -0
- package/dist/esm/HostError.js +20 -0
- package/dist/esm/HostError.js.map +1 -0
- package/dist/esm/HostServices.d.ts +101 -0
- package/dist/esm/HostServices.d.ts.map +1 -0
- package/dist/esm/HostServices.js +76 -0
- package/dist/esm/HostServices.js.map +1 -0
- package/dist/esm/HttpClient.d.ts +177 -0
- package/dist/esm/HttpClient.d.ts.map +1 -0
- package/dist/esm/HttpClient.js +299 -0
- package/dist/esm/HttpClient.js.map +1 -0
- package/dist/esm/Jj.d.ts +67 -0
- package/dist/esm/Jj.d.ts.map +1 -0
- package/dist/esm/Jj.js +111 -0
- package/dist/esm/Jj.js.map +1 -0
- package/dist/esm/JournalGrantStore.d.ts +65 -0
- package/dist/esm/JournalGrantStore.d.ts.map +1 -0
- package/dist/esm/JournalGrantStore.js +350 -0
- package/dist/esm/JournalGrantStore.js.map +1 -0
- package/dist/esm/Path.d.ts +37 -0
- package/dist/esm/Path.d.ts.map +1 -0
- package/dist/esm/Path.js +34 -0
- package/dist/esm/Path.js.map +1 -0
- package/dist/esm/ProcessConfinement.d.ts +55 -0
- package/dist/esm/ProcessConfinement.d.ts.map +1 -0
- package/dist/esm/ProcessConfinement.js +106 -0
- package/dist/esm/ProcessConfinement.js.map +1 -0
- package/dist/esm/ProcessLedger.d.ts +260 -0
- package/dist/esm/ProcessLedger.d.ts.map +1 -0
- package/dist/esm/ProcessLedger.js +283 -0
- package/dist/esm/ProcessLedger.js.map +1 -0
- package/dist/esm/Rooted.d.ts +53 -0
- package/dist/esm/Rooted.d.ts.map +1 -0
- package/dist/esm/Rooted.js +104 -0
- package/dist/esm/Rooted.js.map +1 -0
- package/dist/esm/Unreachable.d.ts +27 -0
- package/dist/esm/Unreachable.d.ts.map +1 -0
- package/dist/esm/Unreachable.js +26 -0
- package/dist/esm/Unreachable.js.map +1 -0
- package/dist/esm/Workspace.d.ts +62 -0
- package/dist/esm/Workspace.d.ts.map +1 -0
- package/dist/esm/Workspace.js +51 -0
- package/dist/esm/Workspace.js.map +1 -0
- package/dist/esm/index.d.ts +161 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +151 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/Containment.d.ts +24 -0
- package/dist/esm/internal/Containment.d.ts.map +1 -0
- package/dist/esm/internal/Containment.js +27 -0
- package/dist/esm/internal/Containment.js.map +1 -0
- package/dist/esm/internal/Pipeline.d.ts +18 -0
- package/dist/esm/internal/Pipeline.d.ts.map +1 -0
- package/dist/esm/internal/Pipeline.js +68 -0
- package/dist/esm/internal/Pipeline.js.map +1 -0
- package/dist/esm/internal/makeCapability.d.ts +16 -0
- package/dist/esm/internal/makeCapability.d.ts.map +1 -0
- package/dist/esm/internal/makeCapability.js +22 -0
- package/dist/esm/internal/makeCapability.js.map +1 -0
- package/dist/esm/test/FileSystemBatchContract.d.ts +15 -0
- package/dist/esm/test/FileSystemBatchContract.d.ts.map +1 -0
- package/dist/esm/test/FileSystemBatchContract.js +57 -0
- package/dist/esm/test/FileSystemBatchContract.js.map +1 -0
- package/dist/esm/test/HostContract.d.ts +225 -0
- package/dist/esm/test/HostContract.d.ts.map +1 -0
- package/dist/esm/test/HostContract.js +460 -0
- package/dist/esm/test/HostContract.js.map +1 -0
- package/dist/esm/test/TestGrantStore.d.ts +31 -0
- package/dist/esm/test/TestGrantStore.d.ts.map +1 -0
- package/dist/esm/test/TestGrantStore.js +57 -0
- package/dist/esm/test/TestGrantStore.js.map +1 -0
- package/docs/README.md +162 -0
- package/docs/api.md +1532 -0
- package/docs/concepts/decoration-in-place.md +117 -0
- package/docs/concepts/filesystem-batches.md +58 -0
- package/docs/concepts/filesystem-confinement.md +168 -0
- package/docs/concepts/grant-decisions.md +181 -0
- package/docs/concepts/process-containment.md +155 -0
- package/docs/contract.md +14 -0
- package/docs/guides/adapt-a-new-host-platform.md +148 -0
- package/docs/guides/answer-permission-requests.md +151 -0
- package/docs/guides/authorize-network-and-model-calls.md +118 -0
- package/docs/guides/contain-spawned-processes.md +118 -0
- package/docs/guides/guard-a-host-bundle.md +109 -0
- package/docs/guides/persist-grants-across-restarts.md +135 -0
- package/docs/guides/write-a-capability-policy.md +159 -0
- package/docs/installation.md +115 -0
- package/docs/quickstart.md +182 -0
- package/docs/testing.md +108 -0
- package/docs/troubleshooting.md +281 -0
- package/package.json +344 -3
- package/src/CapabilitySet.ts +6 -0
- package/src/ChildProcessEnvironment.ts +98 -0
- package/src/ChildProcessSpawner.ts +414 -0
- package/src/CommandLine.ts +250 -0
- package/src/ContainedSpawner.ts +284 -0
- package/src/FileSystem.ts +1655 -0
- package/src/FileSystemBatch.ts +90 -0
- package/src/GrantEvent.ts +174 -0
- package/src/GrantStore.ts +1345 -0
- package/src/HostError.ts +37 -0
- package/src/HostServices.ts +133 -0
- package/src/HttpClient.ts +406 -0
- package/src/Jj.ts +159 -0
- package/src/JournalGrantStore.ts +461 -0
- package/src/Path.ts +47 -0
- package/src/ProcessConfinement.ts +156 -0
- package/src/ProcessLedger.ts +423 -0
- package/src/Rooted.ts +128 -0
- package/src/Unreachable.ts +30 -0
- package/src/Workspace.ts +65 -0
- package/src/index.ts +181 -0
- package/src/internal/Containment.ts +37 -0
- package/src/internal/Pipeline.ts +85 -0
- package/src/internal/makeCapability.ts +26 -0
- package/src/test/FileSystemBatchContract.ts +59 -0
- package/src/test/HostContract.ts +785 -0
- package/src/test/TestGrantStore.ts +73 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic grant-store layers for tests.
|
|
3
|
+
*
|
|
4
|
+
* @since 1.0.0-rc.0
|
|
5
|
+
*/
|
|
6
|
+
import { GrantStoreError, permissionDenied } from "@smthrs/capability/Permission";
|
|
7
|
+
import { Effect, Layer } from "effect";
|
|
8
|
+
import { emptyPolicy as closedPolicy } from "../GrantStore.js";
|
|
9
|
+
import { GrantStore, layerNoop } from "../GrantStore.js";
|
|
10
|
+
/**
|
|
11
|
+
* An allow-all grant store.
|
|
12
|
+
*
|
|
13
|
+
* @category layers
|
|
14
|
+
* @since 1.0.0-rc.0
|
|
15
|
+
*/
|
|
16
|
+
export const layerAllow = layerNoop;
|
|
17
|
+
/**
|
|
18
|
+
* Provides a grant store that denies every capability.
|
|
19
|
+
*
|
|
20
|
+
* @category layers
|
|
21
|
+
* @since 1.0.0-rc.0
|
|
22
|
+
*/
|
|
23
|
+
export const layerDeny = (reason = "denied by test") => {
|
|
24
|
+
const service = GrantStore.of({
|
|
25
|
+
policy: closedPolicy,
|
|
26
|
+
check: Effect.fn("GrantStore.check")((capability) => Effect.fail(permissionDenied(capability, reason))),
|
|
27
|
+
reply: Effect.fn("GrantStore.reply")(() => Effect.fail(new GrantStoreError({ code: "request_not_found" }))),
|
|
28
|
+
list: Effect.fn("GrantStore.list")(() => Effect.succeed([]))(),
|
|
29
|
+
grantEnvelope: Effect.fn("GrantStore.grantEnvelope")(() => Effect.void)
|
|
30
|
+
});
|
|
31
|
+
return Layer.succeed(GrantStore)(service);
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Provides a grant store whose checks consume a deterministic sequence of
|
|
35
|
+
* attended-style replies. `once`, `run`, and `remembered` allow the check;
|
|
36
|
+
* `deny` rejects it. Exhausting the script rejects further checks.
|
|
37
|
+
*
|
|
38
|
+
* @category layers
|
|
39
|
+
* @since 1.0.0-rc.0
|
|
40
|
+
*/
|
|
41
|
+
export const layerScripted = (replies) => Layer.effect(GrantStore, Effect.sync(() => {
|
|
42
|
+
let index = 0;
|
|
43
|
+
const service = GrantStore.of({
|
|
44
|
+
policy: closedPolicy,
|
|
45
|
+
check: Effect.fn("GrantStore.check")((capability) => Effect.suspend(() => {
|
|
46
|
+
const reply = replies[index++];
|
|
47
|
+
return reply !== undefined && reply !== "deny"
|
|
48
|
+
? Effect.void
|
|
49
|
+
: Effect.fail(permissionDenied(capability, reply === undefined ? "permission reply script exhausted" : "permission request denied"));
|
|
50
|
+
})),
|
|
51
|
+
reply: Effect.fn("GrantStore.reply")(() => Effect.fail(new GrantStoreError({ code: "request_not_found" }))),
|
|
52
|
+
list: Effect.fn("GrantStore.list")(() => Effect.succeed([]))(),
|
|
53
|
+
grantEnvelope: Effect.fn("GrantStore.grantEnvelope")(() => Effect.void)
|
|
54
|
+
});
|
|
55
|
+
return service;
|
|
56
|
+
}));
|
|
57
|
+
//# sourceMappingURL=TestGrantStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestGrantStore.js","sourceRoot":"","sources":["../../../src/test/TestGrantStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Deterministic grant-store layers for tests.\n *\n * @since 1.0.0-rc.0\n */\n\nimport { GrantStoreError, permissionDenied } from \"@smthrs/capability/Permission\"\nimport { Effect, Layer } from \"effect\"\nimport { emptyPolicy as closedPolicy } from \"../GrantStore.ts\"\nimport { GrantStore, layerNoop, type Resolution, type Service } from \"../GrantStore.ts\"\n\n/**\n * An allow-all grant store.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerAllow: Layer.Layer<GrantStore> = layerNoop\n\n/**\n * Provides a grant store that denies every capability.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerDeny = (reason = \"denied by test\"): Layer.Layer<GrantStore> => {\n const service = GrantStore.of({\n policy: closedPolicy,\n check: Effect.fn(\"GrantStore.check\")((capability) => Effect.fail(permissionDenied(capability, reason))),\n reply: Effect.fn(\"GrantStore.reply\")(() => Effect.fail(new GrantStoreError({ code: \"request_not_found\" }))),\n list: Effect.fn(\"GrantStore.list\")(() => Effect.succeed([]))(),\n grantEnvelope: Effect.fn(\"GrantStore.grantEnvelope\")(() => Effect.void)\n })\n return Layer.succeed(GrantStore)(service)\n}\n\n/**\n * Provides a grant store whose checks consume a deterministic sequence of\n * attended-style replies. `once`, `run`, and `remembered` allow the check;\n * `deny` rejects it. Exhausting the script rejects further checks.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerScripted = (\n replies: ReadonlyArray<Resolution>\n): Layer.Layer<GrantStore> =>\n Layer.effect(\n GrantStore,\n Effect.sync(() => {\n let index = 0\n const service: Service = GrantStore.of({\n policy: closedPolicy,\n check: Effect.fn(\"GrantStore.check\")((capability) =>\n Effect.suspend(() => {\n const reply = replies[index++]\n return reply !== undefined && reply !== \"deny\"\n ? Effect.void\n : Effect.fail(\n permissionDenied(\n capability,\n reply === undefined ? \"permission reply script exhausted\" : \"permission request denied\"\n )\n )\n })\n ),\n reply: Effect.fn(\"GrantStore.reply\")(() => Effect.fail(new GrantStoreError({ code: \"request_not_found\" }))),\n list: Effect.fn(\"GrantStore.list\")(() => Effect.succeed([]))(),\n grantEnvelope: Effect.fn(\"GrantStore.grantEnvelope\")(() => Effect.void)\n })\n return service\n })\n )\n"]}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "@smthrs/kernel"
|
|
3
|
+
description: "A capability kernel for Effect hosts: it decorates the filesystem, process, network, path, and Jujutsu service tags in place, so every side effect is checked against a grant store before it runs and refused with a typed error when nobody authorized it."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`@smthrs/kernel` puts a permission check in front of every side effect an
|
|
7
|
+
[Effect](https://effect.website) program can reach. It wraps the five service
|
|
8
|
+
tags a host provides, `FileSystem`, `Path`, `ChildProcessSpawner`, `HttpClient`,
|
|
9
|
+
and `Jj`, with middleware that names the operation as a capability, asks a grant
|
|
10
|
+
store whether it may proceed, and refuses it with a typed error when no rule
|
|
11
|
+
allows it and nobody is there to ask. Four of those tags are Effect's own; `Jj`
|
|
12
|
+
is the [Jujutsu](https://jj-vcs.github.io) repository port from
|
|
13
|
+
[`@smthrs/jj`](/api/jj).
|
|
14
|
+
|
|
15
|
+
## What it solves
|
|
16
|
+
|
|
17
|
+
Running code you did not write, an agent's plan, a plugin, a generated build
|
|
18
|
+
script, means every file write and every spawned process needs an answer to one
|
|
19
|
+
question: did anyone authorize this. The usual way to enforce that answer is a
|
|
20
|
+
second, protected service, and to tell every caller to use it. That breaks on
|
|
21
|
+
the first dependency that never heard of your kernel and reaches for the
|
|
22
|
+
ordinary `FileSystem` tag instead.
|
|
23
|
+
|
|
24
|
+
This package decorates the ordinary tag in place. Once the layer is composed,
|
|
25
|
+
the guarded implementation _is_ what the tag resolves to, so there is no
|
|
26
|
+
unguarded one left to reach for. Code that calls `fs.readFileString` is checked
|
|
27
|
+
without ever mentioning permission.
|
|
28
|
+
|
|
29
|
+
A yes or no answer is not enough on its own, so the kernel also closes the gaps
|
|
30
|
+
around it:
|
|
31
|
+
|
|
32
|
+
- **Confinement.** A path is authorized as a canonical resource and the
|
|
33
|
+
operation runs through a pinned directory descriptor, so a symlink or a
|
|
34
|
+
rename between the decision and the call cannot redirect it.
|
|
35
|
+
- **Containment.** A cancelled run signals its children, escalates to
|
|
36
|
+
`SIGKILL` on a deadline, and records each child in a durable ledger, so a
|
|
37
|
+
host that dies leaves orphan records its successor can reap.
|
|
38
|
+
- **A ceiling that only narrows.** `CapabilitySet.attenuate` bounds what a
|
|
39
|
+
fiber may ask for, and no public operation widens it again.
|
|
40
|
+
- **Grants that outlive the process.** A decision is written to a journal
|
|
41
|
+
before it takes effect, so a permission a person chose to remember is still
|
|
42
|
+
in force after a restart.
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
`@smthrs/kernel` is not on npm at 1.0.0-rc.0. Its source lives in the
|
|
47
|
+
[smithers repository](https://github.com/smithersai/smithers), and
|
|
48
|
+
[Installation](./installation.md) covers how to depend on it from a checkout,
|
|
49
|
+
the import forms, and the three test subpaths.
|
|
50
|
+
|
|
51
|
+
The package needs Node.js 26.4.0 or later. It carries no platform
|
|
52
|
+
implementations of its own, so a composition that reaches a real machine also
|
|
53
|
+
adds a bundle such as [`@smthrs/platform-node`](/api/platform-node).
|
|
54
|
+
|
|
55
|
+
## Refuse an operation nobody authorized
|
|
56
|
+
|
|
57
|
+
This program composes the kernel over `@smthrs/testing/TestHost`. Add
|
|
58
|
+
`"@smthrs/testing": "workspace:*"` to the consuming package's devDependencies
|
|
59
|
+
before running it. The policy allows reads under the workspace and says
|
|
60
|
+
nothing about writes:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { Capability, GrantStore, HostServices, Permission, Workspace } from "@smthrs/kernel"
|
|
64
|
+
import * as TestHost from "@smthrs/testing/TestHost"
|
|
65
|
+
import { Effect, FileSystem, Layer } from "effect"
|
|
66
|
+
|
|
67
|
+
const rules = [
|
|
68
|
+
new Permission.Rule({
|
|
69
|
+
effect: "allow",
|
|
70
|
+
pattern: new Capability.CapabilityPattern({ action: "fs:read", resource: "/workspace/**" })
|
|
71
|
+
})
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
const guarded = HostServices.layer.pipe(
|
|
75
|
+
Layer.provide(Layer.orDie(GrantStore.layer({ attended: false, rules }))),
|
|
76
|
+
Layer.provideMerge(TestHost.layer({ files: { "/workspace/README.md": "# hello" } })),
|
|
77
|
+
Layer.provide(Workspace.layer("/workspace"))
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
/** Ordinary Effect code. Nothing here knows a kernel exists. */
|
|
81
|
+
const program = Effect.gen(function*() {
|
|
82
|
+
const fs = yield* FileSystem.FileSystem
|
|
83
|
+
const readme = yield* fs.readFileString("/workspace/README.md")
|
|
84
|
+
yield* fs.writeFileString("/workspace/out.txt", readme)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
Effect.runPromise(program.pipe(Effect.provide(guarded), Effect.scoped))
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The read returns `"# hello"`. The write never reaches the filesystem. Effect
|
|
91
|
+
fixes that method's error channel to `PlatformError`, so the kernel projects
|
|
92
|
+
its refusal into one and keeps the structured original on the cause;
|
|
93
|
+
`Permission.fromPlatformError` reads it back:
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
{
|
|
97
|
+
_tag: '@smthrs/capability/PermissionRequired',
|
|
98
|
+
code: 'permission_required',
|
|
99
|
+
requestId: 'permission-1',
|
|
100
|
+
capability: { action: 'fs:write', resource: '/workspace/out.txt' },
|
|
101
|
+
tier: 'compensable',
|
|
102
|
+
meta: {}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The `tier` says what re-running the refused operation would cost: a write
|
|
107
|
+
inside the workspace is `compensable`, because the run can undo it from its own
|
|
108
|
+
snapshot. See [effect tiers](/pkg/capability/concepts/effect-tiers) for the
|
|
109
|
+
other two.
|
|
110
|
+
|
|
111
|
+
`permission_required`, not `permission_denied`: no rule matched, silence is not
|
|
112
|
+
consent, and this store has nobody to ask. Build the store with
|
|
113
|
+
`attended: true` instead and the same write parks on a request an operator can
|
|
114
|
+
answer, then resumes the operation it was authorized for. The
|
|
115
|
+
[Quickstart](./quickstart.md) runs both halves.
|
|
116
|
+
|
|
117
|
+
## How this fits with @smthrs/flows
|
|
118
|
+
|
|
119
|
+
`@smthrs/kernel` is one package of the Smithers durable flow engine, which
|
|
120
|
+
ships whole as [`@smthrs/flows`](/api/flows). Inside that engine the kernel is
|
|
121
|
+
the layer between a flow's body and the machine:
|
|
122
|
+
[`@smthrs/capability`](/api/capability) supplies the vocabulary it decides with,
|
|
123
|
+
the [`@smthrs/platform-node`](/api/platform-node) family implements the ports it
|
|
124
|
+
decorates, and [`@smthrs/engine`](/api/engine) runs steps over the guarded
|
|
125
|
+
surface it composes. `@smthrs/flows` re-exports all of it as the `Kernel`
|
|
126
|
+
namespace, and the durable Node runtime it builds is this composition at full
|
|
127
|
+
size, so a program that already depends on the barrel does not install this
|
|
128
|
+
package separately.
|
|
129
|
+
|
|
130
|
+
Depend on `@smthrs/kernel` on its own when you are guarding a host of your own
|
|
131
|
+
rather than running Smithers flows. Its only Smithers dependencies are the
|
|
132
|
+
capability vocabulary, the journal, and the Jujutsu port, and its root entry
|
|
133
|
+
point imports no Node built-ins, so the kernel runs wherever its host does.
|
|
134
|
+
|
|
135
|
+
Further up, [`@smthrs/cli`](/api/cli) is the `smthrs` command line that
|
|
136
|
+
everything here sits under. It finds the flows in a project, plans them, takes
|
|
137
|
+
an approval, and runs them, and it builds its host through this kernel with a
|
|
138
|
+
policy scoped to the project root, which is how a step that reaches outside
|
|
139
|
+
that root gets stopped.
|
|
140
|
+
|
|
141
|
+
## Where to go next
|
|
142
|
+
|
|
143
|
+
- [Installation](./installation.md): runtime requirements, the import forms,
|
|
144
|
+
the three test subpaths, and the platform bundle you choose yourself.
|
|
145
|
+
- [Quickstart](./quickstart.md): refuse a read on an unattended store, then
|
|
146
|
+
answer the same read on an attended one.
|
|
147
|
+
- [Guard a host bundle](./guides/guard-a-host-bundle.md): the composition over
|
|
148
|
+
a real platform, in the right order, and the two mistakes that defeat it.
|
|
149
|
+
- [Write a capability policy](./guides/write-a-capability-policy.md): the four
|
|
150
|
+
rulesets, the hard veto, and the resource each action names.
|
|
151
|
+
- [Answer permission requests](./guides/answer-permission-requests.md): the
|
|
152
|
+
code on the other side of a parked request.
|
|
153
|
+
- [Decoration in place](./concepts/decoration-in-place.md): why the kernel
|
|
154
|
+
guards the platform's own tags instead of publishing protected copies.
|
|
155
|
+
- [How a grant decision is made](./concepts/grant-decisions.md): the ceiling,
|
|
156
|
+
the four rulesets, and the order they are consulted in.
|
|
157
|
+
- [API reference](./api.md): every public export, its signature, and its
|
|
158
|
+
bounds.
|
|
159
|
+
- [The kernel contract](./contract.md): what this package guarantees at a host
|
|
160
|
+
boundary, in one paragraph.
|
|
161
|
+
- [Troubleshooting](./troubleshooting.md): each refusal this package raises,
|
|
162
|
+
what it means, and what to change.
|