@intentic/machine 1.366.0 → 1.367.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +47 -3
  2. package/dist/commands.d.ts.map +1 -1
  3. package/dist/commands.js +2 -0
  4. package/dist/commands.js.map +1 -1
  5. package/dist/device/egress/follower.d.ts +20 -0
  6. package/dist/device/egress/follower.d.ts.map +1 -0
  7. package/dist/device/egress/follower.js +110 -0
  8. package/dist/device/egress/follower.js.map +1 -0
  9. package/dist/device/egress/pins.d.ts +37 -0
  10. package/dist/device/egress/pins.d.ts.map +1 -0
  11. package/dist/device/egress/pins.js +58 -0
  12. package/dist/device/egress/pins.js.map +1 -0
  13. package/dist/device/egress/proof.d.ts +25 -0
  14. package/dist/device/egress/proof.d.ts.map +1 -0
  15. package/dist/device/egress/proof.js +86 -0
  16. package/dist/device/egress/proof.js.map +1 -0
  17. package/dist/device/egress/set-egress.d.ts +30 -0
  18. package/dist/device/egress/set-egress.d.ts.map +1 -0
  19. package/dist/device/egress/set-egress.js +136 -0
  20. package/dist/device/egress/set-egress.js.map +1 -0
  21. package/dist/device/egress/trust-command.d.ts +3 -0
  22. package/dist/device/egress/trust-command.d.ts.map +1 -0
  23. package/dist/device/egress/trust-command.js +45 -0
  24. package/dist/device/egress/trust-command.js.map +1 -0
  25. package/dist/device/grant.d.ts +43 -0
  26. package/dist/device/grant.d.ts.map +1 -1
  27. package/dist/device/router.d.ts +43 -0
  28. package/dist/device/router.d.ts.map +1 -1
  29. package/dist/device/router.js +20 -5
  30. package/dist/device/router.js.map +1 -1
  31. package/dist/keyring/commands.d.ts +4 -2
  32. package/dist/keyring/commands.d.ts.map +1 -1
  33. package/dist/keyring/commands.js +3 -31
  34. package/dist/keyring/commands.js.map +1 -1
  35. package/dist/keyring/keyring-status.d.ts +2 -0
  36. package/dist/keyring/keyring-status.d.ts.map +1 -1
  37. package/dist/keyring/keyring-status.js +18 -3
  38. package/dist/keyring/keyring-status.js.map +1 -1
  39. package/dist/keyring/lifecycle-commands.d.ts +16 -0
  40. package/dist/keyring/lifecycle-commands.d.ts.map +1 -0
  41. package/dist/keyring/lifecycle-commands.js +55 -0
  42. package/dist/keyring/lifecycle-commands.js.map +1 -0
  43. package/dist/keyring/service.d.ts +12 -0
  44. package/dist/keyring/service.d.ts.map +1 -1
  45. package/dist/keyring/service.js +44 -6
  46. package/dist/keyring/service.js.map +1 -1
  47. package/dist/resident/resident.d.ts.map +1 -1
  48. package/dist/resident/resident.js +10 -1
  49. package/dist/resident/resident.js.map +1 -1
  50. package/package.json +9 -9
package/README.md CHANGED
@@ -910,7 +910,8 @@ it reaches the pipe.
910
910
  | Command | Success output |
911
911
  |---|---|
912
912
  | `keyring setup [--no-hello]` | `{ "ok": true, "path", "askpass": { "installed", "path"? }, "autostart", "setUp": true, "alreadySetUp", "recoveryKey"?, "state"? }` (`recoveryKey` only when this run made the vault) |
913
- | `keyring status` | `{ "ok": true, "installed", "running", "setUp", "state"?, "askpass": { "installed", "path"? }, "support": { "canSetUp", "why"?, "hello", "platform" } }` |
913
+ | `keyring status` | `{ "ok": true, "installed", "running", "setUp", "state"?, "serviceDown"?, "serviceInstalled"?, "askpass": { "installed", "path"? }, "support": { "canSetUp", "why"?, "hello", "platform" } }` (`serviceInstalled`: the protected service is there and its session helper did not answer, so set up or not is unknown) |
914
+ | `keyring start` | `{ "ok": true, "autostart", "setUp", "state" }`: the session helper started where the service is installed, the service repaired if silent |
914
915
  | `keyring unlock [--minutes N]` / `keyring lock` | `{ "ok": true, "unlockedUntil" }` / `{ "ok": true }` |
915
916
  | `keyring list` | `{ "ok": true, "entries": [EntryInfo], "backends" }` |
916
917
  | `keyring add NAME [--stdin] [rules] [--backend B --ref R [--field F] [--attribute A]] [--replace]` | `{ "ok": true, "name" }` |
@@ -921,7 +922,8 @@ it reaches the pipe.
921
922
  | `keyring backend 1password\|bitwarden\|keepassxc [--cli] [--account] [--token-entry] [--password-entry] [--database] [--key-file]` | `{ "ok": true, "backend" }` |
922
923
  | `keyring askpass install\|uninstall\|status` | `{ "ok": true, "installed", "path"?, "variables", "file"?, "profile"? }` |
923
924
  | `keyring sync status` | `{ "ok": true, "sync": { "key", "peers", "revision" } }` |
924
- | `keyring upgrade` | `{ "ok": true, "upgraded", "from", "to" }` |
925
+ | `keyring upgrade` | `{ "ok": true, "upgraded", "from", "to" }`. Asks the running keyring first; where it has nobody to ask (no session helper let in), the administrator's yes stands for its own |
926
+ | `keyring reset` | `{ "ok": true, "setAside" }`: Passwords started over for a lost recovery key, after an administrator's yes. The service's vault files move into a `set-aside-<time>` folder beside them (never deleted; the old key still opens them) and `keyring setup` makes a new vault and key |
925
927
  | `keyring uninstall` | `{ "ok": true, "removed", "vault", "service" }` |
926
928
 
927
929
  The rule flags are `--kind password|text`, `--program P`, `--package P`, `--host H`, `--askpass M` (each repeatable;
@@ -933,6 +935,47 @@ user: through `[Environment]::SetEnvironmentVariable` on Windows, and on Linux a
933
935
  uninstall` removes the service (an administrator's yes) and puts the vault back in `~/.intentic/keyring`, where it
934
936
  stays; set up again, the same recovery key opens it.
935
937
 
938
+ ### The egress fence
939
+
940
+ (2026-10-27) A sandbox's traffic to private networks (the LAN, the machine itself, VPN-reachable nets, link-local
941
+ metadata) can be fenced, per sandbox and off by default. `ic` enforces it on the host, never inside the container,
942
+ where the agent is root. This agent decides whether a change asked from the editor may be carried out
943
+ ([`device/egress/`](src/device/egress)).
944
+
945
+ - **The op.** `set-egress` carries a sandbox's whole policy, behind "Manage sandboxes" like every verb that changes one.
946
+ The agent reads what ic holds (`ic sandbox egress <slug> --json`) and compares. A change that only tightens
947
+ (`egressOnlyTightens` in the contract) is carried out as it comes. Anything else (an exception added or widened, the
948
+ fence turned off) needs the owner's passkey over exactly that change. The editor's only way to ic runs through the
949
+ sandbox's own daemon, which is inside the fence, so the daemon's word cannot be what decides a loosening.
950
+ - **The pin.** The first tightening that carries the owner's passkeys (the daemon fills them in) pins them on this
951
+ machine: trust on first use, written to the audit log. Pins live in `~/.intentic/machine/egress-pins.json` (0600), one
952
+ per sandbox, keyed by slug and the sandbox's id from its connect token. A sandbox removed and made again under the
953
+ same name starts unpinned. A tightening never moves a pin. A loosening signed by a pinned key may only NARROW it to the
954
+ passkeys the request carries (a passkey the owner deleted drops off); it never adds one. A passkey the owner adds
955
+ later is trusted here after `intentic-machine egress trust <slug> --reset` on this machine and one save of the fence
956
+ from the editor, which pins again. `intentic-machine egress trust <slug>` shows a pin. (2026-10-27: a loosening used
957
+ to move the pin to whatever key list it signed over, but that list is the daemon's word, so a daemon could slip in a
958
+ key of its own beside the change the owner meant to sign, and hold the pin from then on.)
959
+ - **What a loosening must prove.** The signed text reads as an `EgressChangeDocument` for this machine's id, this
960
+ environment's key (the one the agent enrolls with), this slug, the revision ic holds now, and the policy asked for. It
961
+ expires within five minutes. Its `keys` digest matches the passkeys the daemon handed over. The WebAuthn assertion is
962
+ from a pinned credential, of type `webauthn.get`. Its challenge is the sha256 of the document text, and it ran on a
963
+ pinned origin, not cross-origin. Its rpIdHash is the pinned rpId, with user presence and user verification. The
964
+ signature verifies with the pinned key, and a key that counts must count up (the last counter is kept with the pin,
965
+ spent before ic runs). Every refusal says what to do: sign it, sign again, or change it on the machine with
966
+ `ic sandbox egress <slug>`. The verifier is the daemon's own, moved to `@intentic/base/webauthn` so both read the
967
+ same bytes.
968
+ - **The follower.** While this environment's ic advertises `egress` (`ic features --json`, nothing else), the agent
969
+ keeps `ic sandbox egress --follow` running as a child. ic fences a container the moment docker starts it, so a
970
+ sandbox Docker Compose recreates is fenced within a second. A follower that exits is started again after 1 s,
971
+ doubling to a minute, and back to 1 s after a run that held a minute. The minute's `ic sandbox watch` is the
972
+ backstop, and ic puts the rules back inside it.
973
+ - **What it does not cover.** A sandbox granted "Run commands" (`shell`) on this machine runs commands outside every
974
+ fence, as this agent. The daemon marks such a sandbox (`hostShell`) so the editor can say so. Until a sandbox's first
975
+ `set-egress` with passkeys, whoever drives its daemon can pin keys of their own. Any sandbox linked with "Manage
976
+ sandboxes" can send the first one for a slug on this machine. A pin that looks wrong is reset here and pinned again
977
+ from the editor.
978
+
936
979
  ### Uninstall
937
980
 
938
981
  `intentic-machine uninstall [--purge] [--delete-data]` ([`src/uninstall/`](src/uninstall/)). `ic uninstall` is the one
@@ -976,7 +1019,7 @@ person who runs it alone, and either way it never downloads anything.
976
1019
 
977
1020
  ## Key files
978
1021
 
979
- - [src/commands.ts](src/commands.ts) — the CLI: `device`, `sync`, `keyring`, `sandbox`, `run`, `status`, `doctor`, `upgrade`, `updates`, `uninstall`.
1022
+ - [src/commands.ts](src/commands.ts) — the CLI: `device`, `sync`, `keyring`, `sandbox`, `egress`, `run`, `status`, `doctor`, `upgrade`, `updates`, `uninstall`.
980
1023
  - [src/uninstall/purge.ts](src/uninstall/purge.ts) — `uninstall --purge`: the steps in `steps.ts`, the distros in `distros.ts`, the files in `sweep.ts`, every decision pure in `plan.ts`.
981
1024
  - [src/resident/resident.ts](src/resident/resident.ts) — the resident process that holds links, pairings and WSL distros; `supervision.ts` beside it starts and stops it through whatever restarts this environment's agent.
982
1025
  - [src/device/mcp.ts](src/device/mcp.ts) — every tool a sandbox can call on this device.
@@ -988,6 +1031,7 @@ person who runs it alone, and either way it never downloads anything.
988
1031
  - [src/upkeep/manifest.ts](src/upkeep/manifest.ts) — what the device upkeep finds and puts right; `reconcile.ts` runs it.
989
1032
  - [src/resident/watchdog.ts](src/resident/watchdog.ts) — the agent's own hang watchdog.
990
1033
  - [src/release/release-verify.ts](src/release/release-verify.ts) — a downloaded release binary checked against the release's signed `SHA256SUMS`.
1034
+ - [src/device/egress/set-egress.ts](src/device/egress/set-egress.ts) — the `set-egress` op: a tightening as it comes, a loosening under the owner's passkey (`proof.ts`), checked against the pins in `pins.ts`; `follower.ts` keeps `ic sandbox egress --follow` running, `trust-command.ts` is `intentic-machine egress trust`.
991
1035
  - [src/keyring/commands.ts](src/keyring/commands.ts) — `intentic-machine keyring …`; `service.ts` installs, starts, upgrades and removes intentic-keyring, `relay.ts` carries the sandbox's `keyring*` calls to its pipe.
992
1036
 
993
1037
  ## Commands
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAsInE,eAAO,MAAM,QAAQ,kDA4BnB,CAAC"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAuInE,eAAO,MAAM,QAAQ,kDA8BnB,CAAC"}
package/dist/commands.js CHANGED
@@ -4,6 +4,7 @@ import { beforeSignInCommand } from "./autostart/before-sign-in.js";
4
4
  import { buildCommand, machineUi } from "./cli-ui.js";
5
5
  import { runLogPath } from "./config.js";
6
6
  import { deviceCommands } from "./device/commands.js";
7
+ import { egressRoutes } from "./device/egress/trust-command.js";
7
8
  import { sandboxRoutes } from "./device/sandbox-cli.js";
8
9
  import { environmentRoutes, updates } from "./environments/commands.js";
9
10
  import { keyringCommands } from "./keyring/commands.js";
@@ -110,6 +111,7 @@ export const commands = buildRouteMap({
110
111
  docs: { brief: "The password vault on this computer, which types and hands over secrets for your sandboxes at your word" },
111
112
  }),
112
113
  sandbox: sandboxRoutes,
114
+ egress: egressRoutes,
113
115
  environment: environmentRoutes,
114
116
  run,
115
117
  "before-sign-in": beforeSignInCommand,
@@ -1 +1 @@
1
- {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type Log } from "@intentic/local-agent";
2
+ export interface FollowerChild {
3
+ readonly exited: Promise<string>;
4
+ readonly stop: () => void;
5
+ }
6
+ export interface FollowerSeams {
7
+ readonly advertised: () => Promise<boolean>;
8
+ readonly spawn: (onLine: (line: string) => void) => FollowerChild;
9
+ readonly now: () => number;
10
+ }
11
+ export interface Backoff {
12
+ readonly wait: number;
13
+ readonly next: number;
14
+ }
15
+ export declare const nextBackoff: (previous: number, startedAt: number, endedAt: number) => Backoff;
16
+ export interface EgressFollower {
17
+ readonly stop: () => void;
18
+ }
19
+ export declare const startEgressFollower: (log: Log, seams?: FollowerSeams) => EgressFollower;
20
+ //# sourceMappingURL=follower.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follower.d.ts","sourceRoot":"","sources":["../../../src/device/egress/follower.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,KAAK,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAqB1D,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,aAAa,CAAC;IAClE,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;CAC9B;AAGD,MAAM,WAAW,OAAO;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AACD,eAAO,MAAM,WAAW,aAAc,MAAM,aAAa,MAAM,WAAW,MAAM,KAAG,OAGlF,CAAC;AAEF,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;CAC7B;AACD,eAAO,MAAM,mBAAmB,QAAS,GAAG,UAAS,aAAa,KAAyB,cAqD1F,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { spawn } from "node:child_process";
2
+ import { homeDir } from "@intentic/local-agent";
3
+ import { DEVICE_FEATURE_EGRESS } from "@intentic/sandbox-contract/schemas/hosts";
4
+ import { icEgressFollowArgs } from "@intentic/sandbox-contract/schemas/egress";
5
+ import { deviceFeatures, icCandidates } from "../tools/ic-binary.js";
6
+ import { lineSplitter } from "../tools/sandboxes.js";
7
+ const FIRST_BACKOFF_MS = 1_000;
8
+ const MAX_BACKOFF_MS = 60_000;
9
+ const STEADY_MS = 60_000;
10
+ const RECHECK_MS = 10 * 60_000;
11
+ export const nextBackoff = (previous, startedAt, endedAt) => {
12
+ const wait = endedAt - startedAt >= STEADY_MS ? FIRST_BACKOFF_MS : previous;
13
+ return { wait, next: Math.min(wait * 2, MAX_BACKOFF_MS) };
14
+ };
15
+ export const startEgressFollower = (log, seams = realFollowerSeams()) => {
16
+ let stopped = false;
17
+ let child;
18
+ let wake;
19
+ let timer;
20
+ const pause = async (ms) => await new Promise((resolve) => {
21
+ wake = resolve;
22
+ timer = setTimeout(resolve, ms);
23
+ timer.unref();
24
+ });
25
+ const loop = async () => {
26
+ let backoff = FIRST_BACKOFF_MS;
27
+ let said = false;
28
+ for (;;) {
29
+ if (stopped) {
30
+ return;
31
+ }
32
+ if (!(await seams.advertised().catch(() => false))) {
33
+ await pause(RECHECK_MS);
34
+ continue;
35
+ }
36
+ if (!said) {
37
+ log("egress follow: fencing sandboxes as docker starts them (ic sandbox egress --follow)");
38
+ said = true;
39
+ }
40
+ const startedAt = seams.now();
41
+ child = seams.spawn((line) => log(`egress follow: ${line}`));
42
+ const ended = await child.exited;
43
+ child = undefined;
44
+ if (stopped) {
45
+ return;
46
+ }
47
+ const { wait, next } = nextBackoff(backoff, startedAt, seams.now());
48
+ backoff = next;
49
+ log(`egress follow: ${ended}; starting it again in ${Math.round(wait / 1_000)}s (the minute's ic sandbox watch fences sandboxes meanwhile)`);
50
+ await pause(wait);
51
+ }
52
+ };
53
+ void loop();
54
+ return {
55
+ stop: () => {
56
+ stopped = true;
57
+ clearTimeout(timer);
58
+ wake?.();
59
+ child?.stop();
60
+ },
61
+ };
62
+ };
63
+ const spawnFollower = (onLine) => {
64
+ const candidates = icCandidates(process.platform, homeDir());
65
+ let current;
66
+ let stopping = false;
67
+ const attempt = (index) => new Promise((resolve) => {
68
+ const binary = candidates[index];
69
+ if (binary === undefined) {
70
+ resolve("no ic was found where this agent installs it");
71
+ return;
72
+ }
73
+ const out = lineSplitter(onLine);
74
+ const err = lineSplitter(onLine);
75
+ const proc = spawn(binary, icEgressFollowArgs(), { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
76
+ current = proc;
77
+ proc.stdin.on("error", () => undefined);
78
+ proc.stdout.setEncoding("utf8").on("data", out.push);
79
+ proc.stderr.setEncoding("utf8").on("data", err.push);
80
+ let missing = false;
81
+ proc.on("error", (error) => {
82
+ missing = error.code === "ENOENT" && !stopping;
83
+ if (!missing) {
84
+ resolve(`it could not run (${error.message})`);
85
+ }
86
+ });
87
+ proc.on("close", (code, signal) => {
88
+ out.end();
89
+ err.end();
90
+ if (missing) {
91
+ void attempt(index + 1).then(resolve);
92
+ return;
93
+ }
94
+ resolve(signal === null ? `it exited with code ${code ?? 1}` : `it was ended by ${signal}`);
95
+ });
96
+ });
97
+ return {
98
+ exited: attempt(0),
99
+ stop: () => {
100
+ stopping = true;
101
+ current?.kill();
102
+ },
103
+ };
104
+ };
105
+ const realFollowerSeams = () => ({
106
+ advertised: async () => (await deviceFeatures()).includes(DEVICE_FEATURE_EGRESS),
107
+ spawn: spawnFollower,
108
+ now: Date.now,
109
+ });
110
+ //# sourceMappingURL=follower.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follower.js","sourceRoot":"","sources":["../../../src/device/egress/follower.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { type EgressOwnerKeys } from "@intentic/sandbox-contract/schemas/egress";
2
+ import { z } from "zod";
3
+ export declare const egressPinsPath: string;
4
+ declare const EgressPinSchema: z.ZodObject<{
5
+ slug: z.ZodString;
6
+ sandboxId: z.ZodNullable<z.ZodString>;
7
+ owner: z.ZodObject<{
8
+ rpId: z.ZodString;
9
+ origins: z.ZodArray<z.ZodString>;
10
+ keys: z.ZodArray<z.ZodObject<{
11
+ id: z.ZodString;
12
+ alg: z.ZodUnion<readonly [z.ZodLiteral<-7>, z.ZodLiteral<-257>, z.ZodLiteral<-8>]>;
13
+ jwk: z.ZodRecord<z.ZodString, z.ZodString>;
14
+ name: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$strict>>;
16
+ }, z.core.$strict>;
17
+ counters: z.ZodRecord<z.ZodString, z.ZodInt>;
18
+ pinnedAt: z.ZodNumber;
19
+ pinnedBy: z.ZodString;
20
+ replacedAt: z.ZodOptional<z.ZodNumber>;
21
+ }, z.core.$strip>;
22
+ export type EgressPin = z.infer<typeof EgressPinSchema>;
23
+ export interface PinnedSandbox {
24
+ readonly slug: string;
25
+ readonly sandboxId: string | null;
26
+ }
27
+ export interface EgressPinStore {
28
+ readonly read: (sandbox: PinnedSandbox) => Promise<EgressPin | undefined>;
29
+ readonly list: (slug?: string) => Promise<readonly EgressPin[]>;
30
+ readonly write: (pin: EgressPin) => Promise<void>;
31
+ readonly reset: (slug: string) => Promise<number>;
32
+ }
33
+ export declare const sameOwnerKeys: (a: EgressOwnerKeys, b: EgressOwnerKeys) => boolean;
34
+ export declare const narrowsOwnerKeys: (next: EgressOwnerKeys, pinned: EgressOwnerKeys) => boolean;
35
+ export declare const egressPinStore: (path?: string, dir?: string) => EgressPinStore;
36
+ export {};
37
+ //# sourceMappingURL=pins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pins.d.ts","sourceRoot":"","sources":["../../../src/device/egress/pins.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgC,KAAK,eAAe,EAAyB,MAAM,2CAA2C,CAAC;AACtI,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,eAAO,MAAM,cAAc,QAAoC,CAAC;AAEhE,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;iBAWnB,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAMxD,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAE3B,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IAE1E,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAAC;IAEhE,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrD;AAGD,eAAO,MAAM,aAAa,MAAO,eAAe,KAAK,eAAe,KAAG,OAA8E,CAAC;AAMtJ,eAAO,MAAM,gBAAgB,SAAU,eAAe,UAAU,eAAe,KAAG,OAIjF,CAAC;AAsBF,eAAO,MAAM,cAAc,UAAU,MAAM,QAAwB,MAAM,KAAa,cAkBrF,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { queueOnFile } from "@intentic/base/fs";
4
+ import { parseJsonWith } from "@intentic/base/json";
5
+ import { writeSecretFile } from "@intentic/local-agent";
6
+ import { canonicalEgressOwnerKeysJson, EgressOwnerKeysSchema } from "@intentic/sandbox-contract/schemas/egress";
7
+ import { z } from "zod";
8
+ import { baseDir } from "../../config.js";
9
+ export const egressPinsPath = join(baseDir, "egress-pins.json");
10
+ const EgressPinSchema = z.object({
11
+ slug: z.string().min(1),
12
+ sandboxId: z.string().nullable(),
13
+ owner: EgressOwnerKeysSchema,
14
+ counters: z.record(z.string(), z.int().nonnegative()),
15
+ pinnedAt: z.number(),
16
+ pinnedBy: z.string(),
17
+ replacedAt: z.number().optional(),
18
+ });
19
+ const EgressPinsFileSchema = z.object({ v: z.literal(1), pins: z.array(EgressPinSchema) });
20
+ export const sameOwnerKeys = (a, b) => canonicalEgressOwnerKeysJson(a) === canonicalEgressOwnerKeysJson(b);
21
+ export const narrowsOwnerKeys = (next, pinned) => {
22
+ const keyText = (key) => canonicalEgressOwnerKeysJson({ rpId: "", origins: [""], keys: [{ ...key, name: undefined }] });
23
+ const held = new Set(pinned.keys.map(keyText));
24
+ return next.rpId === pinned.rpId && next.origins.every((origin) => pinned.origins.includes(origin)) && next.keys.every((key) => held.has(keyText(key)));
25
+ };
26
+ const readFileOf = async (path) => {
27
+ const raw = await readFile(path, "utf8").catch((error) => {
28
+ if (error.code === "ENOENT") {
29
+ return undefined;
30
+ }
31
+ throw error;
32
+ });
33
+ if (raw === undefined) {
34
+ return { v: 1, pins: [] };
35
+ }
36
+ const parsed = parseJsonWith(raw, EgressPinsFileSchema);
37
+ if (parsed === undefined) {
38
+ throw new Error(`${path} does not read as this agent's passkey pins. Move it aside and pin again, or restore it from a backup.`);
39
+ }
40
+ return parsed;
41
+ };
42
+ export const egressPinStore = (path = egressPinsPath, dir = baseDir) => {
43
+ const update = async (change) => await queueOnFile(path, async () => {
44
+ const { next, answer } = change(await readFileOf(path));
45
+ await writeSecretFile(path, dir, `${JSON.stringify(next, undefined, 2)}\n`);
46
+ return answer;
47
+ });
48
+ return {
49
+ read: async ({ slug, sandboxId }) => (await readFileOf(path)).pins.find((pin) => pin.slug === slug && pin.sandboxId === sandboxId),
50
+ list: async (slug) => (await readFileOf(path)).pins.filter((pin) => slug === undefined || pin.slug === slug),
51
+ write: async (pin) => await update((file) => ({ next: { v: 1, pins: [...file.pins.filter((held) => held.slug !== pin.slug), pin] }, answer: undefined })),
52
+ reset: async (slug) => await update((file) => ({
53
+ next: { v: 1, pins: file.pins.filter((held) => held.slug !== slug) },
54
+ answer: file.pins.filter((held) => held.slug === slug).length,
55
+ })),
56
+ };
57
+ };
58
+ //# sourceMappingURL=pins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pins.js","sourceRoot":"","sources":["../../../src/device/egress/pins.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { type EgressOwnerKeys, type EgressPolicy, type EgressProof } from "@intentic/sandbox-contract/schemas/egress";
2
+ import type { EgressPin } from "./pins.js";
3
+ export declare class EgressRefusal extends Error {
4
+ }
5
+ export declare const egressChallengeOf: (documentText: string) => string;
6
+ export declare const ownerKeysDigest: (owner: EgressOwnerKeys) => string;
7
+ export declare const looseningText: (slug: string, from: EgressPolicy, to: EgressPolicy) => string;
8
+ export interface ProofCheck {
9
+ readonly proof: EgressProof;
10
+ readonly slug: string;
11
+ readonly asked: EgressPolicy;
12
+ readonly revision: string;
13
+ readonly machineId: string;
14
+ readonly environment: string;
15
+ readonly ownerKeys: EgressOwnerKeys | undefined;
16
+ readonly pin: EgressPin;
17
+ readonly now: number;
18
+ }
19
+ export interface VerifiedProof {
20
+ readonly keyId: string;
21
+ readonly counter: number;
22
+ readonly ownerKeysSigned: boolean;
23
+ }
24
+ export declare const verifyEgressProof: (check: ProofCheck) => VerifiedProof;
25
+ //# sourceMappingURL=proof.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/device/egress/proof.ts"],"names":[],"mappings":"AAGA,OAAO,EAQH,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EACnB,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAM3C,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAM3C,eAAO,MAAM,iBAAiB,iBAAkB,MAAM,KAAG,MAAqE,CAAC;AAG/H,eAAO,MAAM,eAAe,UAAW,eAAe,KAAG,MAAwD,CAAC;AAWlH,eAAO,MAAM,aAAa,SAAU,MAAM,QAAQ,YAAY,MAAM,YAAY,KAAG,MAUlF,CAAC;AAEF,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,QAAQ,CAAC,SAAS,EAAE,eAAe,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAE1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACrC;AAyCD,eAAO,MAAM,iBAAiB,UAAW,UAAU,KAAG,aA0BrD,CAAC"}
@@ -0,0 +1,86 @@
1
+ import { createHash } from "node:crypto";
2
+ import { parseJsonWith } from "@intentic/base/json";
3
+ import { base64url, verifyAuthentication, WebAuthnError } from "@intentic/base/webauthn";
4
+ import { canonicalEgressOwnerKeysJson, canonicalEgressPolicy, canonicalEgressPolicyJson, EGRESS_HOST, EGRESS_PROOF_MAX_AGE_MS, EgressChangeDocumentSchema, } from "@intentic/sandbox-contract/schemas/egress";
5
+ export class EgressRefusal extends Error {
6
+ }
7
+ const sha256 = (bytes) => createHash("sha256").update(bytes).digest();
8
+ const sha256Hex = (text) => sha256(Buffer.from(text, "utf8")).toString("hex");
9
+ export const egressChallengeOf = (documentText) => base64url.encode(sha256(Buffer.from(documentText, "utf8")));
10
+ export const ownerKeysDigest = (owner) => sha256Hex(canonicalEgressOwnerKeysJson(owner));
11
+ const reachText = (entry) => {
12
+ const where = entry.to === EGRESS_HOST ? "the machine itself" : entry.to;
13
+ const ports = entry.ports === undefined ? "" : ` port${entry.ports.length === 1 ? "" : "s"} ${entry.ports.join(", ")}`;
14
+ const protocol = entry.protocol === undefined ? "" : ports === "" ? ` (${entry.protocol} only)` : `/${entry.protocol}`;
15
+ return `${where}${ports}${protocol}`;
16
+ };
17
+ export const looseningText = (slug, from, to) => {
18
+ if (to.profile === "open") {
19
+ return `this change turns the egress fence of "${slug}" off, so it reaches your network and this machine again`;
20
+ }
21
+ const reach = (entry) => JSON.stringify([entry.to, entry.ports ?? null, entry.protocol ?? null]);
22
+ const before = new Set(canonicalEgressPolicy(from).allow.map(reach));
23
+ const opened = canonicalEgressPolicy(to).allow.filter((entry) => !before.has(reach(entry)));
24
+ return opened.length === 0
25
+ ? `this change loosens the egress fence of "${slug}"`
26
+ : `this change opens ${opened.map(reachText).join(", ")} to "${slug}"`;
27
+ };
28
+ const minutes = (ms) => `${Math.round(ms / 60_000)} minutes`;
29
+ const checkDocument = (check) => {
30
+ const document = parseJsonWith(check.proof.document, EgressChangeDocumentSchema);
31
+ if (document === undefined) {
32
+ throw new EgressRefusal("What your passkey signed is not an intentic egress change this agent can read (v1, intentic-egress-change). Sign the change again.");
33
+ }
34
+ if (document.machineId !== check.machineId) {
35
+ throw new EgressRefusal(`The change you signed is for another machine (${document.machineId}), not this one (${check.machineId}). Sign it again for this machine.`);
36
+ }
37
+ if (document.environment !== check.environment) {
38
+ throw new EgressRefusal(`The change you signed is for another side of this computer (${document.environment}); this agent is ${check.environment}. Sign it again from the sandbox's own row.`);
39
+ }
40
+ if (document.slug !== check.slug) {
41
+ throw new EgressRefusal(`The change you signed is for sandbox "${document.slug}", not "${check.slug}". Sign the change for "${check.slug}".`);
42
+ }
43
+ if (document.basedOn !== check.revision) {
44
+ throw new EgressRefusal(`The egress policy of "${check.slug}" changed since you signed. Look at it again and sign again.`);
45
+ }
46
+ if (canonicalEgressPolicyJson(document.policy) !== canonicalEgressPolicyJson(check.asked)) {
47
+ throw new EgressRefusal(`The policy you signed is not the one this request asks for on "${check.slug}". Sign the change again.`);
48
+ }
49
+ if (document.expiresAt <= check.now) {
50
+ throw new EgressRefusal(`Your signature over this change expired (a signed change holds for ${minutes(EGRESS_PROOF_MAX_AGE_MS)}). Sign again.`);
51
+ }
52
+ if (document.expiresAt > check.now + EGRESS_PROOF_MAX_AGE_MS) {
53
+ throw new EgressRefusal(`The signed change says it holds until ${new Date(document.expiresAt).toISOString()}, more than ${minutes(EGRESS_PROOF_MAX_AGE_MS)} ahead of this machine's clock. Check the clocks of this machine and the device you signed on, then sign again.`);
54
+ }
55
+ if (check.ownerKeys !== undefined && document.keys !== ownerKeysDigest(check.ownerKeys)) {
56
+ throw new EgressRefusal("The passkeys this request carries are not the ones you signed over. Reload the page and sign again.");
57
+ }
58
+ return document.keys;
59
+ };
60
+ export const verifyEgressProof = (check) => {
61
+ const signedKeys = checkDocument(check);
62
+ const { assertion } = check.proof;
63
+ const key = check.pin.owner.keys.find((pinned) => pinned.id === assertion.id);
64
+ if (key === undefined || assertion.rawId !== assertion.id) {
65
+ const names = check.pin.owner.keys.map((pinned) => pinned.name ?? pinned.id).join(", ");
66
+ throw new EgressRefusal(`The passkey you signed with is not one this machine pinned for "${check.slug}"${names === "" ? "" : ` (it trusts ${names})`}. Sign with one of those, or change it on the machine itself with \`ic sandbox egress ${check.slug}\`.`);
67
+ }
68
+ const counter = check.pin.counters[key.id] ?? 0;
69
+ try {
70
+ const verified = verifyAuthentication(assertion, {
71
+ challenge: egressChallengeOf(check.proof.document),
72
+ origin: check.pin.owner.origins,
73
+ rpId: check.pin.owner.rpId,
74
+ publicKey: { alg: key.alg, jwk: key.jwk },
75
+ counter,
76
+ });
77
+ return { keyId: key.id, counter: verified.counter, ownerKeysSigned: check.ownerKeys !== undefined && signedKeys === ownerKeysDigest(check.ownerKeys) };
78
+ }
79
+ catch (error) {
80
+ if (error instanceof WebAuthnError) {
81
+ throw new EgressRefusal(`Your passkey's signature over this change did not check out on this machine: ${error.message}. Sign again.`);
82
+ }
83
+ throw error;
84
+ }
85
+ };
86
+ //# sourceMappingURL=proof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proof.js","sourceRoot":"","sources":["../../../src/device/egress/proof.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { DeviceScopes } from "@intentic/sandbox-contract/schemas/capabilities";
2
+ import { type EgressOwnerKeys, type EgressPolicy, type EgressProof, type EgressStatus } from "@intentic/sandbox-contract/schemas/egress";
3
+ import { type EgressPinStore, type PinnedSandbox } from "./pins.js";
4
+ export interface SetEgressAsk {
5
+ readonly slug: string;
6
+ readonly policy: EgressPolicy;
7
+ readonly proof: EgressProof | undefined;
8
+ readonly ownerKeys: EgressOwnerKeys | undefined;
9
+ readonly via: string;
10
+ }
11
+ export interface SetEgressSeams {
12
+ readonly sandbox: (slug: string) => Promise<PinnedSandbox>;
13
+ readonly status: (slug: string) => Promise<EgressStatus>;
14
+ readonly apply: (slug: string, policy: EgressPolicy, onLine: (line: string) => void) => Promise<void>;
15
+ readonly identity: () => Promise<{
16
+ readonly machineId: string;
17
+ readonly environment: string;
18
+ }>;
19
+ readonly pins: EgressPinStore;
20
+ readonly audit: (entry: {
21
+ tool: string;
22
+ ok: boolean;
23
+ detail: string;
24
+ }) => void;
25
+ readonly now: () => number;
26
+ }
27
+ export declare const egressStatusFrom: (stdout: string) => EgressStatus | undefined;
28
+ export declare const setEgressWith: (ask: SetEgressAsk, seams: SetEgressSeams, onLine: (line: string) => void) => Promise<string>;
29
+ export declare const setEgress: (ask: SetEgressAsk, scopes: DeviceScopes, onLine: (line: string) => void) => Promise<string>;
30
+ //# sourceMappingURL=set-egress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-egress.d.ts","sourceRoot":"","sources":["../../../src/device/egress/set-egress.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iDAAiD,CAAC;AACpF,OAAO,EAGH,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EAEpB,MAAM,2CAA2C,CAAC;AAQnD,OAAO,EAAkB,KAAK,cAAc,EAAoC,KAAK,aAAa,EAAiB,MAAM,WAAW,CAAC;AAQrI,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,eAAe,GAAG,SAAS,CAAC;IAEhD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACxB;AAGD,MAAM,WAAW,cAAc;IAE3B,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3D,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACzD,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtG,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/F,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/E,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;CAC9B;AAGD,eAAO,MAAM,gBAAgB,WAAY,MAAM,KAAG,YAAY,GAAG,SAMhE,CAAC;AA4FF,eAAO,MAAM,aAAa,QAAe,YAAY,SAAS,cAAc,UAAU,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAG,OAAO,CAAC,MAAM,CAW5H,CAAC;AAqCF,eAAO,MAAM,SAAS,QAAe,YAAY,UAAU,YAAY,UAAU,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAG,OAAO,CAAC,MAAM,CAGvH,CAAC"}