/** * Isolate Executor * * Runs a compiled pure-JS connector bundle inside a V8 isolate in the worker * process (`isolated-vm`), speaking the `SyncExecutor` contract: `ExecutorJob` * in, `ExecutorResult` out, SDK context calls mapped onto `ExecutionHooks`. * Unlike the forked child this replaced, the connector gets no filesystem and * no module loader, and it opens nothing itself: every effect crosses the * boundary as a named host capability, so the host owns the network. Both * network capabilities dial through the one egress module: `fetch` (domain * allowlist from `@lobu/connector-sdk/egress-policy`, DNS pinning from * `@lobu/connector-worker/egress`; the response body streams back to the * guest one chunk per `fetchRead`, under a byte cap) and `socketOpen` (the * WinterCG `connect` the DB connectors need: a real TCP socket the HOST dials * at an address the same transport resolved and validated under the DB egress * policy). The job's OAuth access token does not cross into the guest either: * it is replaced by a per-run placeholder (`../egress/credentials.ts`) that the * host swaps back into the request header at `fetch`, once the egress decision * has admitted the destination. The run's other secret channels (`config`, * `sessionState`, `previousCredentials`) are not behind that vault yet. * * This is the only executor `executor/select.ts` builds; a bundle that still * requires a Node builtin is rejected before any isolate work with * `IsolateLaneIneligibleError`. */ import { type ResolveAllAddresses } from '../egress/transport.js'; import type { ExecutionHooks, ExecutorJob, ExecutorResult, SyncExecutor } from './interface.js'; export type IsolateLogLevel = 'log' | 'info' | 'debug' | 'warn' | 'error'; export interface IsolateExecutorOptions { /** Wall-clock budget in ms (default 600000 = 10 minutes); `0` disables it. */ timeoutMs: number; /** V8 heap limit in MB (default 512, matching the process lane's old space). */ memoryMb: number; /** Cap on any single message crossing the boundary (default 16 MiB). */ messageBytes: number; /** * Cap on the bytes one fetched response body may deliver to the guest * (default 16 MiB). Enforced as the guest pulls: the body stream errors * with `FetchBodyLimitExceeded` at the chunk that crosses it. */ fetchBodyBytes: number; /** Cap on total console output forwarded per run (default 1 MiB). */ logBytes: number; /** * Hosts the connector may reach, in the shared egress grammar * (`@lobu/connector-sdk/egress-policy`): `example.com` exact, * `.example.com` / `*.example.com` the apex and every subdomain, `*` * unrestricted. The default is `['*']`: the process lane this replaced had * no allowlist, so closing egress by default would take every connector * offline rather than preserve a boundary that never existed, and nothing on * the wire populates this yet. An EMPTY list denies everything, exactly as * it does for every other consumer of the grammar. For `fetch`, reserved and * internal addresses are refused under every list except where an EXACT * entry names one: `localhost` or `127.0.0.1` is how a self-hosted install * reaches its own services and how the fixture suites reach a loopback * server; even that exemption keeps cloud metadata refused. Raw sockets do * NOT inherit it: a DB socket's address policy is `LOBU_DB_EGRESS_POLICY` * plus the operator's `LOBU_DB_EGRESS_ALLOW_HOSTS`, so this list can only * ever narrow what a run reaches, never widen the DB boundary. */ allowedDomains: readonly string[]; /** Where redacted console lines, the lane's egress refusals and its credential spends go (default: the worker's stdout/stderr). */ logSink: (level: IsolateLogLevel, line: string) => void; /** * Name resolution for host-dialled sockets and `fetch`. The egress * transport's system resolver by default; tests inject one to stage a * dual-stack host without touching DNS. */ lookup?: ResolveAllAddresses; } /** Thrown when a job demands the isolate lane on a host that cannot run one. */ export declare class IsolateRuntimeUnavailableError extends Error { constructor(reason: string | null); } export declare class IsolateExecutor implements SyncExecutor { private readonly options; /** Exact allowlist entries: `fetch`'s exemptions from the reserved-address rule (see `allowedDomains`). */ private readonly exactAllowedHosts; constructor(options?: Partial); /** The shared allowlist decision for both network capabilities. */ private assertHostAllowed; private requireIsolatedVm; execute(compiledCode: string, job: ExecutorJob, hooks?: ExecutionHooks): Promise; /** Build the `ConnectorExecutionError` the daemon reports for a guest-thrown error. */ private guestError; private hostFetch; } //# sourceMappingURL=isolate.d.ts.map