@questpie/sandbox 3.16.0 → 3.17.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.
@@ -0,0 +1,259 @@
1
+ import { a as SandboxCustomToolsRunOptions } from "./custom-tools-CgfgyVNv.mjs";
2
+ import { ExecutorAdapter, ExecutorRunOptions, ExecutorRunResult } from "questpie/executor";
3
+
4
+ //#region src/workload-admission.d.ts
5
+ interface SandboxWorkloadAdmissionClaims {
6
+ readonly kind: "sandbox_workload_admission";
7
+ readonly version: 1;
8
+ readonly admissionId: string;
9
+ readonly supervisorInstanceId: string;
10
+ readonly expiresAt: string;
11
+ readonly requestSha256: string;
12
+ }
13
+ interface SandboxWorkloadAdmissionKey {
14
+ readonly keyId: string;
15
+ readonly secret: Uint8Array;
16
+ /** Unique per supervisor process; rotate on every restart. */
17
+ readonly instanceId: string;
18
+ }
19
+ type SandboxWorkloadAdmissionDenialReason = "invalid" | "wrong_instance" | "expired" | "body_mismatch";
20
+ type SandboxWorkloadAdmissionVerification = {
21
+ readonly ok: true;
22
+ readonly claims: SandboxWorkloadAdmissionClaims;
23
+ } | {
24
+ readonly ok: false;
25
+ readonly reason: SandboxWorkloadAdmissionDenialReason;
26
+ readonly claims?: SandboxWorkloadAdmissionClaims;
27
+ };
28
+ //#endregion
29
+ //#region src/types.d.ts
30
+ /**
31
+ * Wire contract between the `executor` adapter (Bun/Node, main app) and the
32
+ * standalone Deno sandbox server. Kept intentionally minimal: the main app
33
+ * never imports Deno; it only speaks this JSON over HTTP.
34
+ */
35
+ /**
36
+ * Per-request guest capabilities. `net` and `import` are INDEPENDENT axes
37
+ * (Deno enforces them separately — `--allow-net` gates `fetch`, `--allow-import`
38
+ * gates module imports). The richer app-binding capabilities (`data`, `services`,
39
+ * etc.) live on the core `ExecutorCapabilities` type and are NOT enforced by the
40
+ * sandbox server — they are the later bindings-broker's job.
41
+ */
42
+ interface SandboxCapabilities {
43
+ /** Runtime `fetch()` allowlist as `host[:port]`. Empty = no network. */
44
+ net: string[];
45
+ /** Module import allowlist as `host[:port]` (e.g. `esm.sh:443`). Empty = no remote imports. */
46
+ import: string[];
47
+ /** Hard wall-clock timeout; the subprocess is force-killed on exceed. */
48
+ timeoutMs: number;
49
+ /** Hard per-guest memory bound (`--v8-flags=--max-old-space-size`). */
50
+ memoryMb: number;
51
+ }
52
+ /**
53
+ * App-bindings wiring for the UNTRUSTED path. When present, the supervisor
54
+ * exposes a capability-scoped `globalThis.questpie` proxy to the guest whose
55
+ * methods RPC (over STDIO, never the network) to the host broker at {@link url},
56
+ * authenticated by the per-run scoped {@link token}.
57
+ *
58
+ * The token is OPAQUE and is held by the SUPERVISOR only — it is never written
59
+ * into the guest subprocess, so guest code cannot read or forge it to widen
60
+ * scope. The guest only ever sees the typed proxy.
61
+ *
62
+ * Critically, the broker is reached SERVER-TO-SERVER (supervisor → app), NOT by
63
+ * the guest's `fetch`: the guest's egress allowlist rejects loopback/private IPs
64
+ * (the SSRF defense), so a localhost broker is unreachable from guest code by
65
+ * design — we do not poke a hole in that.
66
+ */
67
+ interface SandboxBindings {
68
+ /** Absolute URL of the host broker endpoint (e.g. `https://app/sandbox/rpc`). */
69
+ url: string;
70
+ /** Per-run opaque scoped token. Supervisor-only; never exposed to the guest. */
71
+ token: string;
72
+ }
73
+ /** POST /run request body. */
74
+ interface SandboxRunRequest {
75
+ /** Explicit transport admission. The supervisor rejects omitted/unknown modes. */
76
+ mode: "workload" | "host";
77
+ /** Guest TypeScript source. Must `export default` a `function(input)`. */
78
+ source: string;
79
+ /** Payload passed to the guest entry function. */
80
+ input: unknown;
81
+ capabilities: SandboxCapabilities;
82
+ /**
83
+ * Secrets injected into the guest (NOT in source). Surfaced to the guest as
84
+ * `globalThis.__secrets`. The sandbox never persists these.
85
+ */
86
+ secrets?: Record<string, string>;
87
+ /**
88
+ * App-bindings broker wiring. When omitted, the guest gets no `questpie`
89
+ * proxy (the legacy compute-only path). When present, the supervisor brokers
90
+ * the guest's binding RPCs to the host. See {@link SandboxBindings}.
91
+ */
92
+ bindings?: SandboxBindings;
93
+ }
94
+ /** POST /run response body — also the `ExecutorAdapter.run` result shape. */
95
+ type SandboxRunResult = {
96
+ ok: true;
97
+ output?: unknown;
98
+ logs: string[];
99
+ error?: never;
100
+ timedOut?: never;
101
+ /** Wall-clock duration in ms (server-measured). */
102
+ ms?: number;
103
+ } | {
104
+ ok: false;
105
+ error: string;
106
+ logs: string[];
107
+ output?: never;
108
+ timedOut?: never;
109
+ /** Wall-clock duration in ms (server-measured). */
110
+ ms?: number;
111
+ } | {
112
+ ok: false;
113
+ error: string;
114
+ logs: string[];
115
+ output?: never;
116
+ /** True when the run was killed by the wall-clock timeout. */
117
+ timedOut: true;
118
+ /** Wall-clock duration in ms (server-measured). */
119
+ ms?: number;
120
+ };
121
+ /** Line prefix marking a framed protocol message on guest stdout. */
122
+ declare const FRAME_MARKER = "__QP_SANDBOX_MSG__";
123
+ /**
124
+ * Header carrying the per-run scoped token on the supervisor → broker request.
125
+ * Mirrors `questpie`'s `BINDINGS_TOKEN_HEADER`. Supervisor-only; never set by or
126
+ * exposed to guest code.
127
+ */
128
+ declare const BINDINGS_TOKEN_HEADER$1 = "x-questpie-sandbox-token";
129
+ /** Host-issued admission binding an authorized workload to one exact request. */
130
+ declare const WORKLOAD_ADMISSION_HEADER = "x-questpie-workload-admission";
131
+ /** Trusted service identity for the structurally separate direct-host path. */
132
+ declare const HOST_ADMISSION_HEADER = "x-questpie-sandbox-host-admission";
133
+ /** Structured error returned by the broker (mirrors `questpie` `BindingError`). */
134
+ interface SandboxBindingError {
135
+ code: string;
136
+ message: string;
137
+ correlationId?: string;
138
+ }
139
+ /** Guest → supervisor: a binding RPC awaiting a brokered result. */
140
+ interface GuestRpcMessage {
141
+ type: "rpc";
142
+ id: number;
143
+ method: string;
144
+ args: unknown;
145
+ }
146
+ /** Guest → supervisor: the run's final result. */
147
+ interface GuestResultMessage {
148
+ type: "result";
149
+ ok: boolean;
150
+ output?: unknown;
151
+ error?: string;
152
+ logs: string[];
153
+ }
154
+ /** Any message the guest emits on stdout (framed). */
155
+ type GuestMessage = GuestRpcMessage | GuestResultMessage;
156
+ /** Supervisor → guest: the brokered outcome of a prior {@link GuestRpcMessage}. */
157
+ interface HostRpcResultMessage {
158
+ type: "rpc-result";
159
+ id: number;
160
+ ok: boolean;
161
+ value?: unknown;
162
+ error?: SandboxBindingError;
163
+ }
164
+ //#endregion
165
+ //#region src/workload.d.ts
166
+ declare const SANDBOX_WORKLOAD_DENIAL_MESSAGE = "The workload is not authorized for this sandbox operation.";
167
+ declare class SandboxWorkloadDeniedError extends Error {
168
+ readonly code: "sandbox_authority_denied";
169
+ constructor();
170
+ }
171
+ interface SandboxWorkloadPolicy {
172
+ readonly source: string;
173
+ readonly input?: unknown;
174
+ readonly capabilities: SandboxCapabilities;
175
+ readonly secrets?: Readonly<Record<string, string>>;
176
+ readonly bindings?: SandboxBindings;
177
+ /** Host-only custom-tool authority. Never serialized to the supervisor. */
178
+ readonly sandboxTools?: SandboxCustomToolsRunOptions;
179
+ /** Optional consumer deadline; transport admission is additionally capped at 5s. */
180
+ readonly validUntil?: string;
181
+ }
182
+ type SandboxWorkloadAuthorizationPhase = "prepare" | "dispatch";
183
+ interface SandboxWorkloadAuthorizationContext {
184
+ readonly phase: SandboxWorkloadAuthorizationPhase;
185
+ readonly signal: AbortSignal;
186
+ }
187
+ type SandboxWorkloadAuthorizer = (envelope: unknown, context: SandboxWorkloadAuthorizationContext) => SandboxWorkloadPolicy | null | Promise<SandboxWorkloadPolicy | null>;
188
+ interface SandboxWorkloadAuditEvent {
189
+ readonly boundary: "sandbox.workload";
190
+ readonly phase: SandboxWorkloadAuthorizationPhase | "transport";
191
+ readonly decision: "allowed" | "denied";
192
+ readonly reason: "authorization_failed" | "authorization_timed_out" | "cancelled" | "egress_denied" | "policy_changed" | "policy_invalid" | "authorization_succeeded" | "transport_completed" | "transport_denied" | "transport_cancelled" | "transport_timed_out" | "transport_invalid_response" | "transport_failed";
193
+ }
194
+ interface SandboxWorkloadAuditContext {
195
+ readonly signal: AbortSignal;
196
+ }
197
+ interface SandboxWorkloadRunOptions {
198
+ /** Consumer-owned opaque value. QUESTPIE never parses or persists it. */
199
+ readonly envelope: unknown;
200
+ readonly signal?: AbortSignal;
201
+ }
202
+ //#endregion
203
+ //#region src/adapter-http.d.ts
204
+ interface WorkloadHttpSandboxOptions {
205
+ readonly authorize: SandboxWorkloadAuthorizer;
206
+ readonly admission: SandboxWorkloadAdmissionKey;
207
+ /** Hard bound around each consumer authorization read. Default 2s, max 5s. */
208
+ readonly authorizationTimeoutMs?: number;
209
+ /** Redacted audit seam. Allowed decisions fail closed when it cannot settle. */
210
+ readonly audit?: (event: SandboxWorkloadAuditEvent, context: SandboxWorkloadAuditContext) => void | Promise<void>;
211
+ /** Hard bound around the audit callback. Default 250ms, max 1s. */
212
+ readonly auditTimeoutMs?: number;
213
+ }
214
+ interface HttpSandboxAdapterOptions {
215
+ /**
216
+ * Base URL of the standalone Deno sandbox service (`sandbox-server.ts`).
217
+ * Defaults to `process.env.SANDBOX_URL`. Throws on `run` if neither is set.
218
+ */
219
+ url?: string;
220
+ /**
221
+ * Per-request HTTP fetch timeout (ms). A safety margin above the guest
222
+ * wall-timeout so the server's own kill fires first; if the server is
223
+ * unreachable this bounds the client wait. Default: guest timeout + 10s.
224
+ */
225
+ fetchTimeoutMs?: number;
226
+ /** Optional custom `fetch` (e.g. for auth headers / mTLS). */
227
+ fetch?: typeof fetch;
228
+ /**
229
+ * Validate net/import hosts against the private-IP policy BEFORE sending to
230
+ * the sandbox (defense-in-depth; the server validates too). Default: true.
231
+ */
232
+ validateEgress?: boolean;
233
+ /** Consumer-owned fail-closed admission for opaque remote workloads. */
234
+ workload?: WorkloadHttpSandboxOptions;
235
+ /** Trusted host credential for the direct framework `run()` path. */
236
+ hostAdmissionSecret?: string;
237
+ }
238
+ /**
239
+ * Production sandboxed `ExecutorAdapter` — POSTs the run to the standalone Deno
240
+ * sandbox service over HTTP. The main app (Bun/Node) holds NO Deno; it only
241
+ * speaks this JSON contract.
242
+ *
243
+ * Use as the `sandboxed` adapter in the executor config:
244
+ * executor: { sandboxed: httpSandboxAdapter({ url: process.env.SANDBOX_URL }) }
245
+ */
246
+ declare class HttpSandboxAdapter implements ExecutorAdapter {
247
+ private readonly options;
248
+ private readonly fetchImpl;
249
+ private readonly validateEgress;
250
+ constructor(options?: HttpSandboxAdapterOptions);
251
+ private resolveUrl;
252
+ private postRun;
253
+ run(options: ExecutorRunOptions): Promise<ExecutorRunResult>;
254
+ runWorkload(options: SandboxWorkloadRunOptions): Promise<ExecutorRunResult>;
255
+ }
256
+ /** Factory for the production sandboxed adapter. */
257
+ declare function httpSandboxAdapter(options?: HttpSandboxAdapterOptions): HttpSandboxAdapter;
258
+ //#endregion
259
+ export { SandboxWorkloadAdmissionVerification as A, SandboxCapabilities as C, SandboxWorkloadAdmissionClaims as D, WORKLOAD_ADMISSION_HEADER as E, SandboxWorkloadAdmissionDenialReason as O, SandboxBindings as S, SandboxRunResult as T, GuestResultMessage as _, SANDBOX_WORKLOAD_DENIAL_MESSAGE as a, HostRpcResultMessage as b, SandboxWorkloadAuthorizationContext as c, SandboxWorkloadDeniedError as d, SandboxWorkloadPolicy as f, GuestMessage as g, FRAME_MARKER as h, httpSandboxAdapter as i, SandboxWorkloadAdmissionKey as k, SandboxWorkloadAuthorizationPhase as l, BINDINGS_TOKEN_HEADER$1 as m, HttpSandboxAdapterOptions as n, SandboxWorkloadAuditContext as o, SandboxWorkloadRunOptions as p, WorkloadHttpSandboxOptions as r, SandboxWorkloadAuditEvent as s, HttpSandboxAdapter as t, SandboxWorkloadAuthorizer as u, GuestRpcMessage as v, SandboxRunRequest as w, SandboxBindingError as x, HOST_ADMISSION_HEADER as y };
@@ -1,2 +1,3 @@
1
- import { n as HttpSandboxAdapterOptions, r as httpSandboxAdapter, t as HttpSandboxAdapter } from "./adapter-http-7ypWHBck.mjs";
2
- export { HttpSandboxAdapter, type HttpSandboxAdapterOptions, httpSandboxAdapter };
1
+ import { f as SandboxWorkloadPolicy, i as httpSandboxAdapter, n as HttpSandboxAdapterOptions, o as SandboxWorkloadAuditContext, p as SandboxWorkloadRunOptions, r as WorkloadHttpSandboxOptions, s as SandboxWorkloadAuditEvent, t as HttpSandboxAdapter, u as SandboxWorkloadAuthorizer } from "./adapter-http-DDeL-Ym3.mjs";
2
+ import { a as SandboxCustomToolsRunOptions, i as SandboxCustomToolsLimits, n as SandboxCustomToolEvidenceEvent, o as sandboxCustomTools, r as SandboxCustomToolsConfig, t as SandboxCustomToolEvidenceContext } from "./custom-tools-CgfgyVNv.mjs";
3
+ export { HttpSandboxAdapter, type HttpSandboxAdapterOptions, type SandboxCustomToolEvidenceContext, type SandboxCustomToolEvidenceEvent, type SandboxCustomToolsConfig, type SandboxCustomToolsLimits, type SandboxCustomToolsRunOptions, type SandboxWorkloadAuditContext, type SandboxWorkloadAuditEvent, type SandboxWorkloadAuthorizer, type SandboxWorkloadPolicy, type SandboxWorkloadRunOptions, type WorkloadHttpSandboxOptions, httpSandboxAdapter, sandboxCustomTools };
package/dist/adapter.mjs CHANGED
@@ -1,3 +1,4 @@
1
- import { n as httpSandboxAdapter, t as HttpSandboxAdapter } from "./adapter-http-w7SjAfcC.mjs";
1
+ import { r as sandboxCustomTools } from "./custom-tools-DxI7JvUn.mjs";
2
+ import { n as httpSandboxAdapter, t as HttpSandboxAdapter } from "./adapter-http-Coy4jqsj.mjs";
2
3
 
3
- export { HttpSandboxAdapter, httpSandboxAdapter };
4
+ export { HttpSandboxAdapter, httpSandboxAdapter, sandboxCustomTools };
@@ -0,0 +1,55 @@
1
+ import { McpWorkloadAuditEvent, McpWorkloadAuthorizer, McpWorkloadContextBinder, McpWorkloadHandoff } from "@questpie/mcp";
2
+ import "questpie";
3
+ import "questpie/executor";
4
+
5
+ //#region src/custom-tools.d.ts
6
+ interface SandboxCustomToolsLimits {
7
+ readonly maxTools?: number;
8
+ readonly maxListBytes?: number;
9
+ readonly maxArgumentBytes?: number;
10
+ readonly maxResultBytes?: number;
11
+ /** Total `tools.list` plus `tools.call` operations allowed in one run. */
12
+ readonly maxCalls?: number;
13
+ readonly timeoutMs?: number;
14
+ readonly concurrency?: number;
15
+ }
16
+ interface SandboxCustomToolEvidenceEvent {
17
+ readonly boundary: "sandbox.tool";
18
+ readonly phase: "discovery" | "call";
19
+ readonly toolName?: string;
20
+ readonly result: "succeeded" | "denied" | "failed";
21
+ readonly reason: "completed" | "authorization_or_tool_denied" | "invalid_request" | "limit_exceeded" | "timed_out" | "execution_failed";
22
+ readonly durationMs: number;
23
+ }
24
+ interface SandboxCustomToolEvidenceContext {
25
+ readonly signal: AbortSignal;
26
+ }
27
+ interface SandboxCustomToolsConfig {
28
+ readonly authorizer: McpWorkloadAuthorizer;
29
+ readonly contextBinder: McpWorkloadContextBinder;
30
+ /** Existing MCP authorization audit seam; receives no input or result bodies. */
31
+ readonly authorizationAudit?: (event: McpWorkloadAuditEvent) => void | Promise<void>;
32
+ readonly handoff?: McpWorkloadHandoff;
33
+ /** Product-neutral terminal evidence. Arguments/results/envelopes are omitted. */
34
+ readonly evidence?: (event: SandboxCustomToolEvidenceEvent, context: SandboxCustomToolEvidenceContext) => void | Promise<void>;
35
+ readonly evidenceTimeoutMs?: number;
36
+ readonly limits?: SandboxCustomToolsLimits;
37
+ }
38
+ interface SandboxCustomToolsRunOptions {
39
+ /** Consumer-owned opaque authorization input. Never sent to the guest. */
40
+ readonly envelope: unknown;
41
+ }
42
+ declare module "questpie" {
43
+ interface RuntimeConfigExtensions {
44
+ sandboxCustomTools?: SandboxCustomToolsConfig;
45
+ }
46
+ }
47
+ declare module "questpie/executor" {
48
+ interface ExecutorRunOptions {
49
+ /** Host-only custom-tool authority for this sandbox run. */
50
+ sandboxTools?: SandboxCustomToolsRunOptions;
51
+ }
52
+ }
53
+ declare function sandboxCustomTools(config: SandboxCustomToolsConfig): SandboxCustomToolsConfig;
54
+ //#endregion
55
+ export { SandboxCustomToolsRunOptions as a, SandboxCustomToolsLimits as i, SandboxCustomToolEvidenceEvent as n, sandboxCustomTools as o, SandboxCustomToolsConfig as r, SandboxCustomToolEvidenceContext as t };