@synap-core/cli 1.12.2 → 1.13.1-next.20261102.246dc73
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/LICENSE +21 -0
- package/README.md +38 -11
- package/dist/build-info.json +2 -3
- package/dist/commands/agents.d.ts +0 -1
- package/dist/commands/agents.js +13 -3
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/app.js +2 -1
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/automation.js +46 -41
- package/dist/commands/automation.js.map +1 -1
- package/dist/commands/box.d.ts +42 -0
- package/dist/commands/box.js +89 -0
- package/dist/commands/box.js.map +1 -0
- package/dist/commands/bridge-setup.js +3 -1
- package/dist/commands/bridge-setup.js.map +1 -1
- package/dist/commands/capability.d.ts +87 -1
- package/dist/commands/capability.js +174 -64
- package/dist/commands/capability.js.map +1 -1
- package/dist/commands/close-questions.d.ts +27 -0
- package/dist/commands/close-questions.js +49 -0
- package/dist/commands/close-questions.js.map +1 -0
- package/dist/commands/close-questions.test.d.ts +1 -0
- package/dist/commands/close-questions.test.js +52 -0
- package/dist/commands/close-questions.test.js.map +1 -0
- package/dist/commands/connect.d.ts +1 -3
- package/dist/commands/connect.js +2 -2
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/context.js +74 -44
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +13 -0
- package/dist/commands/data-extra.js +35 -6
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/data.js +2 -1
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/doctor.d.ts +39 -0
- package/dist/commands/doctor.js +136 -14
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/hooks.d.ts +19 -0
- package/dist/commands/hooks.js +70 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/import.d.ts +25 -0
- package/dist/commands/import.js +98 -12
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.js +63 -21
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys-request.d.ts +48 -0
- package/dist/commands/keys-request.js +236 -0
- package/dist/commands/keys-request.js.map +1 -0
- package/dist/commands/keys-request.test.d.ts +1 -0
- package/dist/commands/keys-request.test.js +176 -0
- package/dist/commands/keys-request.test.js.map +1 -0
- package/dist/commands/knowledge.d.ts +42 -5
- package/dist/commands/knowledge.js +94 -29
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/lens.js +2 -1
- package/dist/commands/lens.js.map +1 -1
- package/dist/commands/market-authoring.js +1 -1
- package/dist/commands/market-authoring.js.map +1 -1
- package/dist/commands/market.d.ts +89 -31
- package/dist/commands/market.js +215 -222
- package/dist/commands/market.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -1
- package/dist/commands/mcp.js +14 -9
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/observe.category.test.d.ts +1 -0
- package/dist/commands/observe.category.test.js +59 -0
- package/dist/commands/observe.category.test.js.map +1 -0
- package/dist/commands/observe.d.ts +3 -0
- package/dist/commands/observe.js +7 -5
- package/dist/commands/observe.js.map +1 -1
- package/dist/commands/observe.title.test.d.ts +1 -0
- package/dist/commands/observe.title.test.js +37 -0
- package/dist/commands/observe.title.test.js.map +1 -0
- package/dist/commands/playbook.js +9 -0
- package/dist/commands/playbook.js.map +1 -1
- package/dist/commands/pods.d.ts +6 -1
- package/dist/commands/pods.js +38 -17
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.js +7 -1
- package/dist/commands/proposals-actions.js.map +1 -1
- package/dist/commands/proton-setup.js +3 -1
- package/dist/commands/proton-setup.js.map +1 -1
- package/dist/commands/providers.d.ts +22 -6
- package/dist/commands/providers.js +39 -32
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/raycast.d.ts +15 -0
- package/dist/commands/raycast.js +36 -10
- package/dist/commands/raycast.js.map +1 -1
- package/dist/commands/session-feed-wait.d.ts +48 -0
- package/dist/commands/session-feed-wait.js +261 -0
- package/dist/commands/session-feed-wait.js.map +1 -0
- package/dist/commands/session-post.d.ts +40 -0
- package/dist/commands/session-post.js +96 -0
- package/dist/commands/session-post.js.map +1 -0
- package/dist/commands/session-wait.d.ts +9 -4
- package/dist/commands/session-wait.js +20 -5
- package/dist/commands/session-wait.js.map +1 -1
- package/dist/commands/sessions.d.ts +2 -0
- package/dist/commands/sessions.js +23 -0
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skills-commands.d.ts +2 -0
- package/dist/commands/skills-commands.js +9 -1
- package/dist/commands/skills-commands.js.map +1 -1
- package/dist/commands/skills-commands.test.d.ts +1 -0
- package/dist/commands/skills-commands.test.js +57 -0
- package/dist/commands/skills-commands.test.js.map +1 -0
- package/dist/commands/status.js +13 -7
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.backoff.test.js +13 -3
- package/dist/commands/statusline.backoff.test.js.map +1 -1
- package/dist/commands/statusline.d.ts +104 -9
- package/dist/commands/statusline.js +392 -148
- package/dist/commands/statusline.js.map +1 -1
- package/dist/commands/statusline.line2.test.d.ts +15 -0
- package/dist/commands/statusline.line2.test.js +226 -0
- package/dist/commands/statusline.line2.test.js.map +1 -0
- package/dist/commands/statusline.needs-you.test.d.ts +1 -0
- package/dist/commands/statusline.needs-you.test.js +17 -0
- package/dist/commands/statusline.needs-you.test.js.map +1 -0
- package/dist/commands/statusline.now.test.d.ts +10 -0
- package/dist/commands/statusline.now.test.js +250 -0
- package/dist/commands/statusline.now.test.js.map +1 -0
- package/dist/commands/tracks.d.ts +68 -6
- package/dist/commands/tracks.js +272 -16
- package/dist/commands/tracks.js.map +1 -1
- package/dist/commands/upload.js +12 -0
- package/dist/commands/upload.js.map +1 -1
- package/dist/commands/wait.d.ts +124 -0
- package/dist/commands/wait.js +384 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/commands/wait.test.d.ts +8 -0
- package/dist/commands/wait.test.js +308 -0
- package/dist/commands/wait.test.js.map +1 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +17 -0
- package/dist/commands/whoami.js.map +1 -1
- package/dist/commands/worker-accounts.d.ts +149 -0
- package/dist/commands/worker-accounts.js +339 -0
- package/dist/commands/worker-accounts.js.map +1 -0
- package/dist/commands/worker-accounts.test.d.ts +1 -0
- package/dist/commands/worker-accounts.test.js +224 -0
- package/dist/commands/worker-accounts.test.js.map +1 -0
- package/dist/commands/worker-github-app.d.ts +63 -0
- package/dist/commands/worker-github-app.js +161 -0
- package/dist/commands/worker-github-app.js.map +1 -0
- package/dist/commands/worker-github-app.test.d.ts +1 -0
- package/dist/commands/worker-github-app.test.js +102 -0
- package/dist/commands/worker-github-app.test.js.map +1 -0
- package/dist/commands/worker-github-broker.d.ts +39 -0
- package/dist/commands/worker-github-broker.js +106 -0
- package/dist/commands/worker-github-broker.js.map +1 -0
- package/dist/commands/worker-github-broker.test.d.ts +1 -0
- package/dist/commands/worker-github-broker.test.js +90 -0
- package/dist/commands/worker-github-broker.test.js.map +1 -0
- package/dist/commands/worker-runs.test.d.ts +1 -0
- package/dist/commands/worker-runs.test.js +150 -0
- package/dist/commands/worker-runs.test.js.map +1 -0
- package/dist/commands/worker-worktrees.d.ts +34 -0
- package/dist/commands/worker-worktrees.js +87 -0
- package/dist/commands/worker-worktrees.js.map +1 -0
- package/dist/commands/worker.d.ts +406 -10
- package/dist/commands/worker.js +1180 -126
- package/dist/commands/worker.js.map +1 -1
- package/dist/commands/worker.test.js +588 -11
- package/dist/commands/worker.test.js.map +1 -1
- package/dist/index.js +220 -28
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-door.d.ts +1 -3
- package/dist/lib/agent-door.js +10 -7
- package/dist/lib/agent-door.js.map +1 -1
- package/dist/lib/agents-config.d.ts +9 -0
- package/dist/lib/agents-config.js +20 -0
- package/dist/lib/agents-config.js.map +1 -1
- package/dist/lib/atomic-file.d.ts +20 -1
- package/dist/lib/atomic-file.js +100 -2
- package/dist/lib/atomic-file.js.map +1 -1
- package/dist/lib/auth.d.ts +28 -2
- package/dist/lib/auth.js +52 -12
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/box/egress.d.ts +82 -0
- package/dist/lib/box/egress.js +264 -0
- package/dist/lib/box/egress.js.map +1 -0
- package/dist/lib/box/evidence.d.ts +86 -0
- package/dist/lib/box/evidence.js +121 -0
- package/dist/lib/box/evidence.js.map +1 -0
- package/dist/lib/box/job-server.d.ts +72 -0
- package/dist/lib/box/job-server.js +250 -0
- package/dist/lib/box/job-server.js.map +1 -0
- package/dist/lib/box/provisioner.d.ts +83 -0
- package/dist/lib/box/provisioner.js +312 -0
- package/dist/lib/box/provisioner.js.map +1 -0
- package/dist/lib/box/proxmox.d.ts +112 -0
- package/dist/lib/box/proxmox.js +211 -0
- package/dist/lib/box/proxmox.js.map +1 -0
- package/dist/lib/box/wire.d.ts +98 -0
- package/dist/lib/box/wire.js +70 -0
- package/dist/lib/box/wire.js.map +1 -0
- package/dist/lib/bridge-invocation.test.d.ts +1 -0
- package/dist/lib/bridge-invocation.test.js +75 -0
- package/dist/lib/bridge-invocation.test.js.map +1 -0
- package/dist/lib/bridge-status.d.ts +144 -0
- package/dist/lib/bridge-status.js +227 -0
- package/dist/lib/bridge-status.js.map +1 -0
- package/dist/lib/bridge-status.test.d.ts +6 -0
- package/dist/lib/bridge-status.test.js +197 -0
- package/dist/lib/bridge-status.test.js.map +1 -0
- package/dist/lib/client-header.test.d.ts +1 -0
- package/dist/lib/client-header.test.js +103 -0
- package/dist/lib/client-header.test.js.map +1 -0
- package/dist/lib/conversation-headers.d.ts +89 -9
- package/dist/lib/conversation-headers.js +156 -3
- package/dist/lib/conversation-headers.js.map +1 -1
- package/dist/lib/conversation-headers.test.d.ts +1 -0
- package/dist/lib/conversation-headers.test.js +78 -0
- package/dist/lib/conversation-headers.test.js.map +1 -0
- package/dist/lib/credential-class.d.ts +40 -2
- package/dist/lib/credential-class.js +48 -6
- package/dist/lib/credential-class.js.map +1 -1
- package/dist/lib/device-connect.d.ts +45 -0
- package/dist/lib/device-connect.js +60 -0
- package/dist/lib/device-connect.js.map +1 -1
- package/dist/lib/exporter-coverage.d.ts +1 -1
- package/dist/lib/exporter-coverage.js +1 -1
- package/dist/lib/goal-words.d.ts +38 -0
- package/dist/lib/goal-words.js +115 -0
- package/dist/lib/goal-words.js.map +1 -0
- package/dist/lib/hub-client.d.ts +87 -8
- package/dist/lib/hub-client.js +205 -70
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/init-handoff.d.ts +8 -0
- package/dist/lib/init-handoff.js +9 -2
- package/dist/lib/init-handoff.js.map +1 -1
- package/dist/lib/key-locations.d.ts +3 -3
- package/dist/lib/key-locations.js +3 -39
- package/dist/lib/key-locations.js.map +1 -1
- package/dist/lib/key-source.d.ts +3 -1
- package/dist/lib/key-source.js +7 -4
- package/dist/lib/key-source.js.map +1 -1
- package/dist/lib/knowledge-title.d.ts +3 -2
- package/dist/lib/knowledge-title.js +3 -2
- package/dist/lib/knowledge-title.js.map +1 -1
- package/dist/lib/machine-instance.d.ts +7 -5
- package/dist/lib/machine-instance.js +7 -5
- package/dist/lib/machine-instance.js.map +1 -1
- package/dist/lib/mcp-bridge.d.ts +50 -0
- package/dist/lib/mcp-bridge.js +232 -11
- package/dist/lib/mcp-bridge.js.map +1 -1
- package/dist/lib/mcp-bridge.test.js +224 -4
- package/dist/lib/mcp-bridge.test.js.map +1 -1
- package/dist/lib/mcp-call.d.ts +5 -1
- package/dist/lib/mcp-call.js +7 -6
- package/dist/lib/mcp-call.js.map +1 -1
- package/dist/lib/mcp-connector-url.test.d.ts +1 -0
- package/dist/lib/mcp-connector-url.test.js +13 -0
- package/dist/lib/mcp-connector-url.test.js.map +1 -0
- package/dist/lib/mcp-snippets.d.ts +16 -0
- package/dist/lib/mcp-snippets.js +20 -2
- package/dist/lib/mcp-snippets.js.map +1 -1
- package/dist/lib/now-line.d.ts +66 -0
- package/dist/lib/now-line.js +193 -0
- package/dist/lib/now-line.js.map +1 -0
- package/dist/lib/now-line.test.d.ts +5 -0
- package/dist/lib/now-line.test.js +177 -0
- package/dist/lib/now-line.test.js.map +1 -0
- package/dist/lib/package-updates.d.ts +66 -0
- package/dist/lib/package-updates.js +76 -0
- package/dist/lib/package-updates.js.map +1 -0
- package/dist/lib/pod-moved.d.ts +70 -0
- package/dist/lib/pod-moved.js +146 -0
- package/dist/lib/pod-moved.js.map +1 -0
- package/dist/lib/pod-reach.d.ts +39 -0
- package/dist/lib/pod-reach.js +117 -0
- package/dist/lib/pod-reach.js.map +1 -0
- package/dist/lib/pod-reach.test.d.ts +1 -0
- package/dist/lib/pod-reach.test.js +67 -0
- package/dist/lib/pod-reach.test.js.map +1 -0
- package/dist/lib/pod.d.ts +8 -4
- package/dist/lib/pod.js +12 -10
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/register-claude-code-mcp.test.d.ts +1 -0
- package/dist/lib/register-claude-code-mcp.test.js +97 -0
- package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
- package/dist/lib/review-credential.js +3 -1
- package/dist/lib/review-credential.js.map +1 -1
- package/dist/lib/safe-open.d.ts +5 -3
- package/dist/lib/safe-open.js +9 -8
- package/dist/lib/safe-open.js.map +1 -1
- package/dist/lib/self-host.d.ts +17 -0
- package/dist/lib/self-host.js +24 -0
- package/dist/lib/self-host.js.map +1 -0
- package/dist/lib/session-feed.d.ts +185 -0
- package/dist/lib/session-feed.js +257 -0
- package/dist/lib/session-feed.js.map +1 -0
- package/dist/lib/targets.d.ts +52 -3
- package/dist/lib/targets.js +150 -209
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/template-lifecycle.d.ts +0 -31
- package/dist/lib/template-lifecycle.js +0 -74
- package/dist/lib/template-lifecycle.js.map +1 -1
- package/dist/lib/term.d.ts +24 -0
- package/dist/lib/term.js +52 -0
- package/dist/lib/term.js.map +1 -0
- package/dist/lib/term.test.d.ts +1 -0
- package/dist/lib/term.test.js +29 -0
- package/dist/lib/term.test.js.map +1 -0
- package/dist/lib/transcript-index.d.ts +94 -0
- package/dist/lib/transcript-index.js +321 -0
- package/dist/lib/transcript-index.js.map +1 -0
- package/dist/lib/transcript-index.test.d.ts +10 -0
- package/dist/lib/transcript-index.test.js +200 -0
- package/dist/lib/transcript-index.test.js.map +1 -0
- package/dist/lib/wait-help.d.ts +11 -0
- package/dist/lib/wait-help.js +25 -0
- package/dist/lib/wait-help.js.map +1 -0
- package/dist/lib/wait-state.d.ts +57 -0
- package/dist/lib/wait-state.js +114 -0
- package/dist/lib/wait-state.js.map +1 -0
- package/dist/lib/wake-hook-settings.d.ts +57 -0
- package/dist/lib/wake-hook-settings.js +153 -0
- package/dist/lib/wake-hook-settings.js.map +1 -0
- package/dist/lib/wake-state.d.ts +65 -0
- package/dist/lib/wake-state.js +122 -0
- package/dist/lib/wake-state.js.map +1 -0
- package/package.json +5 -2
- package/skills/README.md +1 -1
- package/skills/agent-os/SKILL.md +8 -8
- package/skills/synap/SKILL.md +188 -94
- package/skills/synap/code-skills.md +84 -0
- package/skills/synap/concepts.md +38 -20
- package/skills/synap/creative-loop.md +1 -1
- package/skills/synap/document-embeds.md +17 -1
- package/skills/synap/focus-sessions.md +17 -8
- package/skills/synap/from-intent.md +33 -17
- package/skills/synap/governance-rules.md +3 -1
- package/skills/synap/hand-off.md +10 -6
- package/skills/synap/lenses.md +4 -4
- package/skills/synap/linking.md +2 -2
- package/skills/synap/mental-model.md +1 -1
- package/skills/synap/operating-mode-rules.md +1 -1
- package/skills/synap/quick-reference.md +2 -1
- package/skills/synap/reflexes.md +8 -8
- package/skills/synap/viewframe-cells.md +1 -1
- package/skills/synap/work-model.md +24 -10
- package/skills/synap/workspace-design.md +5 -5
- package/skills/synap/writes.md +16 -9
- package/skills/synap/writing-titles.md +7 -0
- package/skills/synap-app/SKILL.md +148 -66
- package/skills/synap-app/creating-the-base-app.md +12 -7
- package/skills/synap-app/governance.md +27 -16
- package/skills/synap-app/install-commands.md +18 -9
- package/skills/synap-app/overview.md +16 -9
- package/skills/synap-app/using-the-sdk.md +75 -25
- package/skills/synap-market/SKILL.md +1 -1
- package/skills/synap-market/governance-and-catalog.md +1 -1
- package/skills/synap-schema/SKILL.md +24 -21
- package/skills/synap-schema/intent-vocabulary-ssot.md +24 -21
- package/templates/box/runner.py +206 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run box's ONE way out: an HTTP(S) proxy on the provisioner, bound to the
|
|
3
|
+
* box bridge. A box has no gateway and no resolver; everything it fetches goes
|
|
4
|
+
* through here, and here decides (runbox design C4 "Egress"):
|
|
5
|
+
*
|
|
6
|
+
* setup phase → the package registries only (PyPI, npm)
|
|
7
|
+
* run phase → the hosts the script declared, nothing else
|
|
8
|
+
*
|
|
9
|
+
* A name that passes is resolved HERE, every address it resolves to is checked
|
|
10
|
+
* against internal ranges (RFC1918, loopback, link-local and cloud metadata,
|
|
11
|
+
* CGNAT = the tailnet), and the connection goes to the address that was
|
|
12
|
+
* checked, never to a second lookup — so a declared host that resolves to the
|
|
13
|
+
* pod (DNS rebinding) is refused. Every refusal is recorded for the evidence
|
|
14
|
+
* bundle. The bridge firewall is the wall; this is the policy.
|
|
15
|
+
*/
|
|
16
|
+
import dns from "node:dns/promises";
|
|
17
|
+
import http from "node:http";
|
|
18
|
+
import net from "node:net";
|
|
19
|
+
/** Hosts a box may reach while it installs declared packages. */
|
|
20
|
+
export const REGISTRY_HOSTS = [
|
|
21
|
+
"pypi.org",
|
|
22
|
+
"files.pythonhosted.org",
|
|
23
|
+
"registry.npmjs.org",
|
|
24
|
+
];
|
|
25
|
+
/** Ports a bare allow entry opens: the web only. */
|
|
26
|
+
export const ALLOWED_PORTS = [80, 443];
|
|
27
|
+
// ─── addresses ───────────────────────────────────────────────────────────────
|
|
28
|
+
function v4ToInt(ip) {
|
|
29
|
+
return ip.split(".").reduce((acc, o) => (acc << 8) + Number(o), 0) >>> 0;
|
|
30
|
+
}
|
|
31
|
+
function inV4(ip, cidr) {
|
|
32
|
+
const [base, bits] = cidr.split("/");
|
|
33
|
+
const mask = Number(bits) === 0 ? 0 : (~0 << (32 - Number(bits))) >>> 0;
|
|
34
|
+
return (ip & mask) === (v4ToInt(base) & mask);
|
|
35
|
+
}
|
|
36
|
+
/** IPv4 ranges a box must never reach, whatever name led there. */
|
|
37
|
+
const FORBIDDEN_V4 = [
|
|
38
|
+
"0.0.0.0/8", // "this network"
|
|
39
|
+
"10.0.0.0/8", // RFC1918: vmbr1 (the pod at 10.10.0.10) and the box bridge
|
|
40
|
+
"100.64.0.0/10", // CGNAT: the tailnet
|
|
41
|
+
"127.0.0.0/8", // loopback
|
|
42
|
+
"169.254.0.0/16", // link-local, incl. cloud metadata 169.254.169.254
|
|
43
|
+
"172.16.0.0/12", // RFC1918
|
|
44
|
+
"192.0.0.0/24", // IETF protocol assignments
|
|
45
|
+
"192.168.0.0/16", // RFC1918: the LAN, the Proxmox host
|
|
46
|
+
"198.18.0.0/15", // benchmarking
|
|
47
|
+
"224.0.0.0/4", // multicast
|
|
48
|
+
"240.0.0.0/4", // reserved, incl. broadcast
|
|
49
|
+
];
|
|
50
|
+
/** Expands a valid IPv6 literal to 8 hextets (zone id dropped, embedded v4 folded). */
|
|
51
|
+
function v6Hextets(ip) {
|
|
52
|
+
let addr = ip.split("%")[0].toLowerCase();
|
|
53
|
+
const embedded = /^(.*:)(\d+\.\d+\.\d+\.\d+)$/.exec(addr);
|
|
54
|
+
if (embedded) {
|
|
55
|
+
if (!net.isIPv4(embedded[2]))
|
|
56
|
+
return null;
|
|
57
|
+
const n = v4ToInt(embedded[2]);
|
|
58
|
+
addr = `${embedded[1]}${(n >>> 16).toString(16)}:${(n & 0xffff).toString(16)}`;
|
|
59
|
+
}
|
|
60
|
+
const parts = addr.split("::");
|
|
61
|
+
if (parts.length > 2)
|
|
62
|
+
return null;
|
|
63
|
+
const left = parts[0] ? parts[0].split(":") : [];
|
|
64
|
+
const right = parts.length === 2 && parts[1] ? parts[1].split(":") : [];
|
|
65
|
+
const missing = 8 - left.length - right.length;
|
|
66
|
+
if (parts.length === 1 ? missing !== 0 : missing < 0)
|
|
67
|
+
return null;
|
|
68
|
+
const out = [...left, ...Array(missing).fill("0"), ...right].map((h) => parseInt(h, 16));
|
|
69
|
+
return out.every((h) => Number.isInteger(h) && h >= 0 && h <= 0xffff) ? out : null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* True when a box must not connect to this address. An unparsable address is
|
|
73
|
+
* forbidden: the check fails closed.
|
|
74
|
+
*/
|
|
75
|
+
export function isForbiddenAddress(ip) {
|
|
76
|
+
if (net.isIPv4(ip)) {
|
|
77
|
+
const n = v4ToInt(ip);
|
|
78
|
+
return FORBIDDEN_V4.some((c) => inV4(n, c));
|
|
79
|
+
}
|
|
80
|
+
if (!net.isIPv6(ip))
|
|
81
|
+
return true;
|
|
82
|
+
const h = v6Hextets(ip);
|
|
83
|
+
if (!h)
|
|
84
|
+
return true;
|
|
85
|
+
// IPv4-mapped (::ffff:a.b.c.d) and NAT64 (64:ff9b::a.b.c.d) carry a v4 address.
|
|
86
|
+
const isMapped = h.slice(0, 5).every((x) => x === 0) && h[5] === 0xffff;
|
|
87
|
+
const isNat64 = h[0] === 0x64 && h[1] === 0xff9b && h.slice(2, 6).every((x) => x === 0);
|
|
88
|
+
if (isMapped || isNat64) {
|
|
89
|
+
const v4 = `${h[6] >>> 8}.${h[6] & 0xff}.${h[7] >>> 8}.${h[7] & 0xff}`;
|
|
90
|
+
return isForbiddenAddress(v4);
|
|
91
|
+
}
|
|
92
|
+
// ::/96 holds ::, ::1 and the deprecated IPv4-compatible ::a.b.c.d: none is a public peer.
|
|
93
|
+
if (h.slice(0, 6).every((x) => x === 0))
|
|
94
|
+
return true;
|
|
95
|
+
// 6to4 (2002::/16) carries a v4 address in hextets 1-2.
|
|
96
|
+
if (h[0] === 0x2002)
|
|
97
|
+
return isForbiddenAddress(`${h[1] >>> 8}.${h[1] & 0xff}.${h[2] >>> 8}.${h[2] & 0xff}`);
|
|
98
|
+
if (h[0] === 0x2001 && h[1] === 0)
|
|
99
|
+
return true; // Teredo 2001::/32 (an obfuscated v4 inside)
|
|
100
|
+
if (h[0] === 0x2001 && h[1] === 0x0db8)
|
|
101
|
+
return true; // documentation 2001:db8::/32
|
|
102
|
+
if ((h[0] & 0xfe00) === 0xfc00)
|
|
103
|
+
return true; // fc00::/7 unique local
|
|
104
|
+
if ((h[0] & 0xffc0) === 0xfe80)
|
|
105
|
+
return true; // fe80::/10 link-local
|
|
106
|
+
if ((h[0] & 0xff00) === 0xff00)
|
|
107
|
+
return true; // multicast
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
// ─── names ───────────────────────────────────────────────────────────────────
|
|
111
|
+
/** Lower-cased, trailing dot dropped, brackets of an IPv6 literal removed. */
|
|
112
|
+
export function normalizeHost(host) {
|
|
113
|
+
return host.trim().toLowerCase().replace(/^\[(.*)\]$/, "$1").replace(/\.$/, "");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* `host` matches an allow entry exactly, or a `*.example.com` entry as a
|
|
117
|
+
* strict subdomain (never the apex, never a suffix like `evilexample.com`).
|
|
118
|
+
*/
|
|
119
|
+
export function hostMatches(host, allow) {
|
|
120
|
+
const h = normalizeHost(host);
|
|
121
|
+
return allow.some((raw) => {
|
|
122
|
+
const a = normalizeHost(raw);
|
|
123
|
+
if (a.startsWith("*."))
|
|
124
|
+
return h.endsWith(a.slice(1)) && h.length > a.length - 1;
|
|
125
|
+
return h === a;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/** An allow entry split into its host and, when it names one, its port. */
|
|
129
|
+
function splitEntry(entry) {
|
|
130
|
+
const m = /^(.*):(\d{1,5})$/.exec(entry.trim());
|
|
131
|
+
return m && !m[1].includes(":") ? { host: m[1], port: Number(m[2]) } : { host: entry, port: null };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The name-level decision, before any lookup. A bare entry opens the web
|
|
135
|
+
* ports (80, 443); a `host:port` entry opens that port on that host only.
|
|
136
|
+
*/
|
|
137
|
+
export function decideByName(host, port, policy) {
|
|
138
|
+
const h = normalizeHost(host);
|
|
139
|
+
if (net.isIP(h))
|
|
140
|
+
return { allow: false, reason: "an address literal is never allowed; declare a host name" };
|
|
141
|
+
const entries = (policy.phase === "setup" ? REGISTRY_HOSTS : policy.runHosts).map(splitEntry);
|
|
142
|
+
const named = entries.filter((e) => hostMatches(h, [e.host]));
|
|
143
|
+
if (named.length === 0) {
|
|
144
|
+
return {
|
|
145
|
+
allow: false,
|
|
146
|
+
reason: policy.phase === "setup"
|
|
147
|
+
? "only the package registries are reachable while packages install"
|
|
148
|
+
: "not a host this script declared",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const portOk = named.some((e) => (e.port === null ? ALLOWED_PORTS.includes(port) : e.port === port));
|
|
152
|
+
if (!portOk)
|
|
153
|
+
return { allow: false, reason: `port ${port} is not declared for this host` };
|
|
154
|
+
return { allow: true };
|
|
155
|
+
}
|
|
156
|
+
const systemResolver = async (host) => (await dns.lookup(host, { all: true, verbatim: true })).map((a) => a.address);
|
|
157
|
+
/**
|
|
158
|
+
* The full decision: by name, then by every resolved address. Returns the ONE
|
|
159
|
+
* address to connect to, so the connection never re-resolves.
|
|
160
|
+
*/
|
|
161
|
+
export async function decideEgress(host, port, policy, resolve = systemResolver) {
|
|
162
|
+
const byName = decideByName(host, port, policy);
|
|
163
|
+
if (!byName.allow)
|
|
164
|
+
return byName;
|
|
165
|
+
let addresses;
|
|
166
|
+
try {
|
|
167
|
+
addresses = await resolve(normalizeHost(host));
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
return { allow: false, reason: `could not resolve: ${err.message}` };
|
|
171
|
+
}
|
|
172
|
+
if (addresses.length === 0)
|
|
173
|
+
return { allow: false, reason: "resolved to no address" };
|
|
174
|
+
const bad = addresses.find(isForbiddenAddress);
|
|
175
|
+
if (bad)
|
|
176
|
+
return { allow: false, reason: `resolves to an internal address (${bad})` };
|
|
177
|
+
return { allow: true, address: addresses[0] };
|
|
178
|
+
}
|
|
179
|
+
function sourceIpOf(sock) {
|
|
180
|
+
return (sock.remoteAddress ?? "").replace(/^::ffff:/, "");
|
|
181
|
+
}
|
|
182
|
+
function splitHostPort(authority, defaultPort) {
|
|
183
|
+
const m = /^(\[[^\]]+\]|[^:]+)(?::(\d+))?$/.exec(authority);
|
|
184
|
+
if (!m)
|
|
185
|
+
return null;
|
|
186
|
+
return { host: m[1], port: m[2] ? Number(m[2]) : defaultPort };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* An HTTP proxy: CONNECT for HTTPS (a tunnel to the checked address), and
|
|
190
|
+
* absolute-form requests for plain HTTP. Anything else is refused.
|
|
191
|
+
*/
|
|
192
|
+
export function createEgressProxy(opts) {
|
|
193
|
+
const resolve = opts.resolve ?? systemResolver;
|
|
194
|
+
const connect = opts.connect ?? ((address, port) => net.connect({ host: address, port }));
|
|
195
|
+
async function gate(sock, host, port) {
|
|
196
|
+
const src = sourceIpOf(sock);
|
|
197
|
+
const policy = opts.policyFor(src);
|
|
198
|
+
if (!policy)
|
|
199
|
+
return { status: 403, reason: "not a running box" };
|
|
200
|
+
const d = await decideEgress(host, port, policy, resolve);
|
|
201
|
+
if (!d.allow) {
|
|
202
|
+
opts.onBlock(src, { host: normalizeHost(host), port, phase: policy.phase, reason: d.reason, at: new Date().toISOString() });
|
|
203
|
+
return { status: 403, reason: d.reason };
|
|
204
|
+
}
|
|
205
|
+
return { address: d.address };
|
|
206
|
+
}
|
|
207
|
+
const server = http.createServer(async (req, res) => {
|
|
208
|
+
let target;
|
|
209
|
+
try {
|
|
210
|
+
target = new URL(req.url ?? "");
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
res.writeHead(400).end("absolute-form request required\n");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (target.protocol !== "http:") {
|
|
217
|
+
res.writeHead(400).end("only http:// is proxied in absolute form; use CONNECT for https\n");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const port = target.port ? Number(target.port) : 80;
|
|
221
|
+
const g = await gate(req.socket, target.hostname, port);
|
|
222
|
+
if (!("address" in g)) {
|
|
223
|
+
res.writeHead(g.status, { "content-type": "text/plain" }).end(`synap box egress refused: ${g.reason}\n`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const upstream = http.request({
|
|
227
|
+
host: g.address,
|
|
228
|
+
port,
|
|
229
|
+
method: req.method,
|
|
230
|
+
path: target.pathname + target.search,
|
|
231
|
+
headers: { ...req.headers, host: target.host },
|
|
232
|
+
createConnection: () => connect(g.address, port),
|
|
233
|
+
}, (up) => {
|
|
234
|
+
res.writeHead(up.statusCode ?? 502, up.headers);
|
|
235
|
+
up.pipe(res);
|
|
236
|
+
});
|
|
237
|
+
upstream.on("error", () => res.headersSent ? res.destroy() : res.writeHead(502).end());
|
|
238
|
+
req.pipe(upstream);
|
|
239
|
+
});
|
|
240
|
+
server.on("connect", async (req, client, head) => {
|
|
241
|
+
const hp = splitHostPort(req.url ?? "", 443);
|
|
242
|
+
if (!hp) {
|
|
243
|
+
client.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const g = await gate(client, hp.host, hp.port);
|
|
247
|
+
if (!("address" in g)) {
|
|
248
|
+
client.end(`HTTP/1.1 ${g.status} Forbidden\r\ncontent-type: text/plain\r\n\r\nsynap box egress refused: ${g.reason}\n`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const upstream = connect(g.address, hp.port);
|
|
252
|
+
upstream.once("connect", () => {
|
|
253
|
+
client.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
254
|
+
if (head.length)
|
|
255
|
+
upstream.write(head);
|
|
256
|
+
upstream.pipe(client);
|
|
257
|
+
client.pipe(upstream);
|
|
258
|
+
});
|
|
259
|
+
upstream.on("error", () => client.destroy());
|
|
260
|
+
client.on("error", () => upstream.destroy());
|
|
261
|
+
});
|
|
262
|
+
return server;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=egress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress.js","sourceRoot":"","sources":["../../../src/lib/box/egress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a box run hands back: the evidence bundle (runbox design C4 "Outputs";
|
|
3
|
+
* BOX-CONTRACT v2 finish). Exit code, duration, the log kept as head + tail
|
|
4
|
+
* under a cap, the /out files (with their upload tokens), the blocked-egress
|
|
5
|
+
* list and the packages actually installed.
|
|
6
|
+
*
|
|
7
|
+
* A run that did not answer (the clone failed, the box never reported) carries
|
|
8
|
+
* status `infra`: INFRA is never the script's verdict. A wall-clock kill is
|
|
9
|
+
* `timeout`, the script's doing, not INFRA.
|
|
10
|
+
*/
|
|
11
|
+
import type { BlockedEgress } from "./egress.js";
|
|
12
|
+
import type { FinishBody, FinishedFile, FinishStatus } from "./wire.js";
|
|
13
|
+
/** BOX-CONTRACT: head 64 KiB + tail 64 KiB. */
|
|
14
|
+
export declare const LOG_HEAD_BYTES: number;
|
|
15
|
+
export declare const LOG_TAIL_BYTES: number;
|
|
16
|
+
/** Keeps the first HEAD bytes and the last TAIL bytes of a stream, counts all. */
|
|
17
|
+
export declare class LogCapture {
|
|
18
|
+
private readonly headCap;
|
|
19
|
+
private readonly tailCap;
|
|
20
|
+
private head;
|
|
21
|
+
private headLen;
|
|
22
|
+
private tail;
|
|
23
|
+
private tailLen;
|
|
24
|
+
totalBytes: number;
|
|
25
|
+
constructor(headCap?: number, tailCap?: number);
|
|
26
|
+
append(chunk: Buffer | string): void;
|
|
27
|
+
snapshot(): RunLog;
|
|
28
|
+
}
|
|
29
|
+
export interface RunLog {
|
|
30
|
+
head: string;
|
|
31
|
+
/** Everything after the head when nothing was dropped; otherwise the last bytes. */
|
|
32
|
+
tail: string;
|
|
33
|
+
totalBytes: number;
|
|
34
|
+
/** Bytes were dropped between head and tail. */
|
|
35
|
+
truncated: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface InstalledPackage {
|
|
38
|
+
name: string;
|
|
39
|
+
version: string;
|
|
40
|
+
manager: "pip" | "npm";
|
|
41
|
+
}
|
|
42
|
+
export interface RunOutcome {
|
|
43
|
+
/** The exit code the box runner reported, or null when it reported none. */
|
|
44
|
+
exitCode: number | null;
|
|
45
|
+
timedOut: boolean;
|
|
46
|
+
/** A person pressed Stop. */
|
|
47
|
+
stopped: boolean;
|
|
48
|
+
/** Set when the run did not answer for an infrastructure reason. */
|
|
49
|
+
infraReason?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* One status from what happened. Order matters: a person's Stop beats the
|
|
53
|
+
* wall clock, the wall clock beats an exit code that came too late, and INFRA
|
|
54
|
+
* is reported only when the script gave no verdict.
|
|
55
|
+
*/
|
|
56
|
+
export declare function deriveStatus(o: RunOutcome): {
|
|
57
|
+
status: FinishStatus;
|
|
58
|
+
infraReason?: string;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* The pod's finish schema (synap-backend `routers/hub-protocol/rest/box-runs.ts`
|
|
62
|
+
* FinishBody). A body over any of these is refused whole (400), and the run's
|
|
63
|
+
* evidence is lost with it, so the bundle is cut to fit HERE, and every cut is
|
|
64
|
+
* said in the log the person reads.
|
|
65
|
+
*/
|
|
66
|
+
export declare const WIRE: {
|
|
67
|
+
readonly maxBlocked: 500;
|
|
68
|
+
readonly maxPackages: 500;
|
|
69
|
+
readonly hostChars: 300;
|
|
70
|
+
readonly reasonChars: 500;
|
|
71
|
+
readonly infraReasonChars: 1000;
|
|
72
|
+
readonly pathChars: 500;
|
|
73
|
+
readonly titleChars: 255;
|
|
74
|
+
readonly packageNameChars: 214;
|
|
75
|
+
readonly packageVersionChars: 64;
|
|
76
|
+
};
|
|
77
|
+
export declare function assembleFinish(input: {
|
|
78
|
+
leaseId: string;
|
|
79
|
+
outcome: RunOutcome;
|
|
80
|
+
startedAt: number;
|
|
81
|
+
finishedAt: number;
|
|
82
|
+
log: LogCapture;
|
|
83
|
+
blockedEgress: BlockedEgress[];
|
|
84
|
+
installedPackages: InstalledPackage[];
|
|
85
|
+
files: FinishedFile[];
|
|
86
|
+
}): FinishBody;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a box run hands back: the evidence bundle (runbox design C4 "Outputs";
|
|
3
|
+
* BOX-CONTRACT v2 finish). Exit code, duration, the log kept as head + tail
|
|
4
|
+
* under a cap, the /out files (with their upload tokens), the blocked-egress
|
|
5
|
+
* list and the packages actually installed.
|
|
6
|
+
*
|
|
7
|
+
* A run that did not answer (the clone failed, the box never reported) carries
|
|
8
|
+
* status `infra`: INFRA is never the script's verdict. A wall-clock kill is
|
|
9
|
+
* `timeout`, the script's doing, not INFRA.
|
|
10
|
+
*/
|
|
11
|
+
/** BOX-CONTRACT: head 64 KiB + tail 64 KiB. */
|
|
12
|
+
export const LOG_HEAD_BYTES = 64 * 1024;
|
|
13
|
+
export const LOG_TAIL_BYTES = 64 * 1024;
|
|
14
|
+
/** Keeps the first HEAD bytes and the last TAIL bytes of a stream, counts all. */
|
|
15
|
+
export class LogCapture {
|
|
16
|
+
headCap;
|
|
17
|
+
tailCap;
|
|
18
|
+
head = [];
|
|
19
|
+
headLen = 0;
|
|
20
|
+
tail = [];
|
|
21
|
+
tailLen = 0;
|
|
22
|
+
totalBytes = 0;
|
|
23
|
+
constructor(headCap = LOG_HEAD_BYTES, tailCap = LOG_TAIL_BYTES) {
|
|
24
|
+
this.headCap = headCap;
|
|
25
|
+
this.tailCap = tailCap;
|
|
26
|
+
}
|
|
27
|
+
append(chunk) {
|
|
28
|
+
let buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
29
|
+
this.totalBytes += buf.length;
|
|
30
|
+
if (this.headLen < this.headCap) {
|
|
31
|
+
const take = buf.subarray(0, this.headCap - this.headLen);
|
|
32
|
+
this.head.push(take);
|
|
33
|
+
this.headLen += take.length;
|
|
34
|
+
buf = buf.subarray(take.length);
|
|
35
|
+
}
|
|
36
|
+
if (buf.length === 0)
|
|
37
|
+
return;
|
|
38
|
+
this.tail.push(buf);
|
|
39
|
+
this.tailLen += buf.length;
|
|
40
|
+
while (this.tail.length > 1 && this.tailLen - this.tail[0].length >= this.tailCap) {
|
|
41
|
+
this.tailLen -= this.tail.shift().length;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
snapshot() {
|
|
45
|
+
const head = Buffer.concat(this.head).toString("utf8");
|
|
46
|
+
let tailBuf = Buffer.concat(this.tail);
|
|
47
|
+
if (tailBuf.length > this.tailCap)
|
|
48
|
+
tailBuf = tailBuf.subarray(tailBuf.length - this.tailCap);
|
|
49
|
+
const truncated = this.totalBytes > this.headLen + tailBuf.length;
|
|
50
|
+
return { head, tail: tailBuf.toString("utf8"), totalBytes: this.totalBytes, truncated };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One status from what happened. Order matters: a person's Stop beats the
|
|
55
|
+
* wall clock, the wall clock beats an exit code that came too late, and INFRA
|
|
56
|
+
* is reported only when the script gave no verdict.
|
|
57
|
+
*/
|
|
58
|
+
export function deriveStatus(o) {
|
|
59
|
+
if (o.stopped)
|
|
60
|
+
return { status: "stopped" };
|
|
61
|
+
if (o.timedOut)
|
|
62
|
+
return { status: "timeout" };
|
|
63
|
+
if (o.infraReason)
|
|
64
|
+
return { status: "infra", infraReason: o.infraReason };
|
|
65
|
+
if (o.exitCode === null)
|
|
66
|
+
return { status: "infra", infraReason: "the box ended without reporting an exit code" };
|
|
67
|
+
return { status: o.exitCode === 0 ? "done" : "failed" };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The pod's finish schema (synap-backend `routers/hub-protocol/rest/box-runs.ts`
|
|
71
|
+
* FinishBody). A body over any of these is refused whole (400), and the run's
|
|
72
|
+
* evidence is lost with it, so the bundle is cut to fit HERE, and every cut is
|
|
73
|
+
* said in the log the person reads.
|
|
74
|
+
*/
|
|
75
|
+
export const WIRE = {
|
|
76
|
+
maxBlocked: 500,
|
|
77
|
+
maxPackages: 500,
|
|
78
|
+
hostChars: 300,
|
|
79
|
+
reasonChars: 500,
|
|
80
|
+
infraReasonChars: 1000,
|
|
81
|
+
pathChars: 500,
|
|
82
|
+
titleChars: 255,
|
|
83
|
+
packageNameChars: 214,
|
|
84
|
+
packageVersionChars: 64,
|
|
85
|
+
};
|
|
86
|
+
const cut = (s, n) => (s.length > n ? `${s.slice(0, n - 1)}…` : s);
|
|
87
|
+
export function assembleFinish(input) {
|
|
88
|
+
const d = deriveStatus(input.outcome);
|
|
89
|
+
const blocked = input.blockedEgress;
|
|
90
|
+
if (blocked.length > WIRE.maxBlocked) {
|
|
91
|
+
input.log.append(`\n[synap box] ${blocked.length - WIRE.maxBlocked} more blocked connections not listed (first ${WIRE.maxBlocked} kept)\n`);
|
|
92
|
+
}
|
|
93
|
+
const packages = input.installedPackages.filter((p) => p.name && p.version);
|
|
94
|
+
if (packages.length > WIRE.maxPackages) {
|
|
95
|
+
input.log.append(`\n[synap box] ${packages.length - WIRE.maxPackages} more installed packages not listed\n`);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
leaseId: input.leaseId,
|
|
99
|
+
status: d.status,
|
|
100
|
+
exitCode: input.outcome.exitCode,
|
|
101
|
+
durationMs: Math.max(0, Math.round(input.finishedAt - input.startedAt)),
|
|
102
|
+
...(d.infraReason ? { infraReason: cut(d.infraReason, WIRE.infraReasonChars) } : {}),
|
|
103
|
+
log: input.log.snapshot(),
|
|
104
|
+
files: input.files.map((f) => ({
|
|
105
|
+
...f,
|
|
106
|
+
path: cut(f.path, WIRE.pathChars),
|
|
107
|
+
...(f.title !== undefined ? { title: cut(f.title, WIRE.titleChars) } : {}),
|
|
108
|
+
})),
|
|
109
|
+
blockedEgress: blocked.slice(0, WIRE.maxBlocked).map((b) => ({
|
|
110
|
+
...b,
|
|
111
|
+
host: cut(b.host, WIRE.hostChars),
|
|
112
|
+
reason: cut(b.reason, WIRE.reasonChars),
|
|
113
|
+
})),
|
|
114
|
+
installedPackages: packages.slice(0, WIRE.maxPackages).map((p) => ({
|
|
115
|
+
...p,
|
|
116
|
+
name: cut(p.name, WIRE.packageNameChars),
|
|
117
|
+
version: cut(p.version, WIRE.packageVersionChars),
|
|
118
|
+
})),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../../src/lib/box/evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the box talks to: a small HTTP server on the provisioner's bridge
|
|
3
|
+
* address. The box holds no token: its identity IS its address on the isolated
|
|
4
|
+
* bridge (the provisioner assigned it), so a request is answered only for the
|
|
5
|
+
* run that address was given to.
|
|
6
|
+
*
|
|
7
|
+
* GET /job the script, its packages, parameters and wall clock
|
|
8
|
+
* POST /phase {"phase":"run"}: packages are in, egress narrows to the
|
|
9
|
+
* declared hosts (one way: never back to setup)
|
|
10
|
+
* POST /log a chunk of the run's stdout+stderr
|
|
11
|
+
* PUT /out/<path> one /out file, streamed to staging under the caps
|
|
12
|
+
* POST /done {"exitCode", "installedPackages"}
|
|
13
|
+
*/
|
|
14
|
+
import http from "node:http";
|
|
15
|
+
import type { BlockedEgress, BoxPhase, EgressPolicy } from "./egress.js";
|
|
16
|
+
import { LogCapture, type InstalledPackage } from "./evidence.js";
|
|
17
|
+
import type { LeasedRun } from "./wire.js";
|
|
18
|
+
/** BOX-CONTRACT v1: at most 20 files per run. */
|
|
19
|
+
export declare const MAX_OUT_FILES = 20;
|
|
20
|
+
export interface StagedFile {
|
|
21
|
+
path: string;
|
|
22
|
+
size: number;
|
|
23
|
+
sha256: string;
|
|
24
|
+
}
|
|
25
|
+
export interface BoxReport {
|
|
26
|
+
/** null when the runner sent no integer exit code (the runner's fault, read as INFRA). */
|
|
27
|
+
exitCode: number | null;
|
|
28
|
+
installedPackages: InstalledPackage[];
|
|
29
|
+
}
|
|
30
|
+
export interface ActiveBox {
|
|
31
|
+
run: LeasedRun;
|
|
32
|
+
phase: BoxPhase;
|
|
33
|
+
log: LogCapture;
|
|
34
|
+
blocked: BlockedEgress[];
|
|
35
|
+
outDir: string;
|
|
36
|
+
outBytes: number;
|
|
37
|
+
outFiles: StagedFile[];
|
|
38
|
+
/** The runner asked for its job (the box booted and reached the provisioner). */
|
|
39
|
+
jobFetched: boolean;
|
|
40
|
+
/** Resolves when the runner reports done. */
|
|
41
|
+
done: Promise<BoxReport>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A safe relative path for an /out file, or null. Each segment is decoded
|
|
45
|
+
* once; no empty, `.`, `..`, NUL or separator inside a segment.
|
|
46
|
+
*/
|
|
47
|
+
export declare function safeOutPath(urlPath: string): string | null;
|
|
48
|
+
/** The job document the box runner reads. */
|
|
49
|
+
export declare function jobFor(run: LeasedRun): Record<string, unknown>;
|
|
50
|
+
type Entry = ActiveBox & {
|
|
51
|
+
finish: (r: BoxReport) => void;
|
|
52
|
+
};
|
|
53
|
+
/** `job`: the box booted and asked for its job; `run`: its packages are in, the script starts. */
|
|
54
|
+
export type BoxEvent = "job" | "run";
|
|
55
|
+
export declare class BoxRegistry {
|
|
56
|
+
private readonly stagingRoot;
|
|
57
|
+
private boxes;
|
|
58
|
+
/** Called once per box and event (the provisioner sets it per run: its clocks hang on it). */
|
|
59
|
+
onEvent: (run: LeasedRun, event: BoxEvent) => void;
|
|
60
|
+
constructor(stagingRoot: string);
|
|
61
|
+
register(ip: string, run: LeasedRun): ActiveBox;
|
|
62
|
+
/** Forget the box and drop its staged files. */
|
|
63
|
+
unregister(ip: string): void;
|
|
64
|
+
get(ip: string): Entry | undefined;
|
|
65
|
+
policyFor(ip: string): EgressPolicy | null;
|
|
66
|
+
recordBlock(ip: string, block: BlockedEgress): void;
|
|
67
|
+
markJobFetched(ip: string): void;
|
|
68
|
+
enterRunPhase(ip: string): void;
|
|
69
|
+
}
|
|
70
|
+
export declare function parseReport(raw: unknown): BoxReport;
|
|
71
|
+
export declare function createJobServer(registry: BoxRegistry): http.Server;
|
|
72
|
+
export {};
|