docs: document sandbox backend contracts

This commit is contained in:
Peter Steinberger
2026-06-04 00:38:21 -04:00
parent 61d16dd173
commit d901f85abb
3 changed files with 29 additions and 0 deletions
+12
View File
@@ -3,12 +3,20 @@ import type { SandboxBackendHandle } from "./backend-handle.types.js";
import type { SandboxRegistryEntry } from "./registry.js";
import type { SandboxConfig } from "./types.js";
/**
* Shared sandbox backend registration contracts.
*
* Docker, SSH, and future backends expose a factory plus optional manager so
* runtime creation and lifecycle cleanup stay behind one backend boundary.
*/
/** Current runtime state reported by a sandbox backend manager. */
export type SandboxBackendRuntimeInfo = {
running: boolean;
actualConfigLabel?: string;
configLabelMatch: boolean;
};
/** Optional lifecycle manager for an existing registered sandbox runtime. */
export type SandboxBackendManager = {
describeRuntime(params: {
entry: SandboxRegistryEntry;
@@ -22,6 +30,7 @@ export type SandboxBackendManager = {
}): Promise<void>;
};
/** Inputs needed to create a sandbox backend handle for one session scope. */
export type CreateSandboxBackendParams = {
sessionKey: string;
scopeKey: string;
@@ -30,10 +39,12 @@ export type CreateSandboxBackendParams = {
cfg: SandboxConfig;
};
/** Factory that creates a backend handle for a sandbox session. */
export type SandboxBackendFactory = (
params: CreateSandboxBackendParams,
) => Promise<SandboxBackendHandle>;
/** Registry input accepted for sandbox backend registration. */
export type SandboxBackendRegistration =
| SandboxBackendFactory
| {
@@ -41,6 +52,7 @@ export type SandboxBackendRegistration =
manager?: SandboxBackendManager;
};
/** Normalized backend registration stored in the sandbox backend registry. */
export type RegisteredSandboxBackend = {
factory: SandboxBackendFactory;
manager?: SandboxBackendManager;
+9
View File
@@ -1,15 +1,24 @@
/**
* Public sandbox filesystem bridge contracts.
*
* Tool and backend code use this interface to access files through the sandbox
* boundary instead of reaching directly into host paths.
*/
/** Resolved sandbox path with host, relative, and container views. */
export type SandboxResolvedPath = {
hostPath?: string;
relativePath: string;
containerPath: string;
};
/** Minimal file stat shape returned by sandbox fs bridge implementations. */
export type SandboxFsStat = {
type: "file" | "directory" | "other";
size: number;
mtimeMs: number;
};
/** Filesystem operations exposed across the sandbox boundary. */
export type SandboxFsBridge = {
resolvePath(params: { filePath: string; cwd?: string }): SandboxResolvedPath;
readFile(params: { filePath: string; cwd?: string; signal?: AbortSignal }): Promise<Buffer>;
+8
View File
@@ -1,10 +1,17 @@
import path from "node:path";
/**
* POSIX path helpers for sandbox container paths.
*
* Container paths are always normalized as POSIX paths even when the host is not.
*/
/** Normalizes a container path and treats "." as the container root. */
export function normalizeContainerPath(value: string): string {
const normalized = path.posix.normalize(value);
return normalized === "." ? "/" : normalized;
}
/** Returns whether target is lexically inside root after container-path normalization. */
export function isPathInsideContainerRoot(root: string, target: string): boolean {
const normalizedRoot = normalizeContainerPath(root);
const normalizedTarget = normalizeContainerPath(target);
@@ -14,6 +21,7 @@ export function isPathInsideContainerRoot(root: string, target: string): boolean
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(`${normalizedRoot}/`);
}
/** Returns whether a relative path would escape its container root. */
export function relativePathEscapesContainerRoot(relativePath: string): boolean {
return (
relativePath === ".." || relativePath.startsWith("../") || path.posix.isAbsolute(relativePath)