@namzu/sandbox 18.1.1 → 19.0.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 (43) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +35 -5
  3. package/dist/backends/docker/index.d.ts +46 -1
  4. package/dist/backends/docker/index.d.ts.map +1 -1
  5. package/dist/backends/docker/index.js +187 -51
  6. package/dist/backends/docker/index.js.map +1 -1
  7. package/dist/egress/index.d.ts +1 -0
  8. package/dist/egress/index.d.ts.map +1 -1
  9. package/dist/egress/index.js +1 -0
  10. package/dist/egress/index.js.map +1 -1
  11. package/dist/egress/profile-wiring.d.ts +40 -0
  12. package/dist/egress/profile-wiring.d.ts.map +1 -0
  13. package/dist/egress/profile-wiring.js +75 -0
  14. package/dist/egress/profile-wiring.js.map +1 -0
  15. package/dist/egress/profile.d.ts +153 -0
  16. package/dist/egress/profile.d.ts.map +1 -0
  17. package/dist/egress/profile.js +244 -0
  18. package/dist/egress/profile.js.map +1 -0
  19. package/dist/egress/proxy.d.ts +16 -0
  20. package/dist/egress/proxy.d.ts.map +1 -1
  21. package/dist/egress/proxy.js +37 -3
  22. package/dist/egress/proxy.js.map +1 -1
  23. package/dist/index.d.ts +19 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +12 -4
  26. package/dist/index.js.map +1 -1
  27. package/dist/seed/index.d.ts +165 -0
  28. package/dist/seed/index.d.ts.map +1 -0
  29. package/dist/seed/index.js +505 -0
  30. package/dist/seed/index.js.map +1 -0
  31. package/dist/testing/sandbox-conformance.d.ts +30 -1
  32. package/dist/testing/sandbox-conformance.d.ts.map +1 -1
  33. package/dist/testing/sandbox-conformance.js +18 -0
  34. package/dist/testing/sandbox-conformance.js.map +1 -1
  35. package/package.json +3 -3
  36. package/src/backends/docker/index.ts +246 -51
  37. package/src/egress/index.ts +1 -0
  38. package/src/egress/profile-wiring.ts +125 -0
  39. package/src/egress/profile.ts +380 -0
  40. package/src/egress/proxy.ts +53 -3
  41. package/src/index.ts +54 -5
  42. package/src/seed/index.ts +710 -0
  43. package/src/testing/sandbox-conformance.ts +52 -6
package/src/index.ts CHANGED
@@ -92,6 +92,8 @@ import {
92
92
  // local name and `ContainerBackendConfig.brokeredCredentials` needs one to
93
93
  // point at. The public export is unchanged and stays type-only.
94
94
  import type { BrokeredCredential } from './egress/index.js'
95
+ import { resolveProviderEgress } from './egress/profile-wiring.js'
96
+ import type { SandboxEgressProfile } from './egress/profile.js'
95
97
 
96
98
  // Re-export the layout types so consumers of `@namzu/sandbox` can
97
99
  // import them without also depending on `@namzu/sdk`. The canonical
@@ -1162,7 +1164,7 @@ export interface KubernetesBackendConfig {
1162
1164
  * - `static` — fixed allowlist of hostnames at construction.
1163
1165
  * - `resolver` — async closure returning the allowlist.
1164
1166
  * Parameterless **on purpose**: the resolver is a closure that
1165
- * captures whatever context the host has (tenantId, runId,
1167
+ * captures whatever context the host has (tenantId, sessionId, turnId,
1166
1168
  * auth token, etc.) at provider-construction time. Compass-
1167
1169
  * platform's JWT-minting flow already works this way: the
1168
1170
  * server knows the tenant when it issues the JWT, and the
@@ -1181,6 +1183,33 @@ export type {
1181
1183
  EgressProxyOptions,
1182
1184
  RunningEgressProxy,
1183
1185
  } from './egress/index.js'
1186
+ export {
1187
+ defineEgressProfile,
1188
+ egressPortsForRules,
1189
+ egressProfileAllowsPort,
1190
+ kubernetesEgressFromProfile,
1191
+ SandboxEgressProfileError,
1192
+ } from './egress/profile.js'
1193
+ export type {
1194
+ KubernetesEgressProfileEnforcement,
1195
+ SandboxEgressHostRule,
1196
+ SandboxEgressProfile,
1197
+ SandboxEgressProfileErrorCode,
1198
+ } from './egress/profile.js'
1199
+ export {
1200
+ defineSandboxSeed,
1201
+ ensureSandboxSeed,
1202
+ sandboxSeedDigest,
1203
+ SandboxSeedError,
1204
+ } from './seed/index.js'
1205
+ export type {
1206
+ EnsureSandboxSeedOptions,
1207
+ SandboxSeed,
1208
+ SandboxSeedErrorCode,
1209
+ SandboxSeedReport,
1210
+ SandboxSeedRepository,
1211
+ SandboxSeedRepositoryReport,
1212
+ } from './seed/index.js'
1184
1213
 
1185
1214
  export type EgressPolicy =
1186
1215
  | { readonly kind: 'deny-all' }
@@ -1239,7 +1268,7 @@ export interface SandboxBackend {
1239
1268
  * is in play.
1240
1269
  *
1241
1270
  * `layout` is **not** here — see the type-level note on
1242
- * {@link SandboxBackend}. Identity-aware fields (tenantId / runId /
1271
+ * {@link SandboxBackend}. Identity-aware fields (tenantId / sessionId / turnId /
1243
1272
  * agentId) are deliberately NOT in this shape either; hosts that
1244
1273
  * need per-tenant sandbox config bake the tenant into the closure
1245
1274
  * that constructs the provider — see the `EgressPolicy` resolver
@@ -1294,6 +1323,18 @@ export type SandboxProviderConfig =
1294
1323
 
1295
1324
  interface SandboxProviderConfigBase {
1296
1325
  readonly defaultEgress?: EgressPolicy
1326
+ /**
1327
+ * A named, validated host allowlist with optional ports, instead of
1328
+ * `defaultEgress`. Validated here, synchronously, before any I/O, and
1329
+ * refused (`SandboxEgressProfileError`) where the backend cannot honour all
1330
+ * of it: beside `defaultEgress`; on the kubernetes backend, whose egress is
1331
+ * config-level (use `kubernetesEgressFromProfile` for `backend.egress`); on
1332
+ * the ACI standby pool; and ports on firecracker. On docker and runsc a
1333
+ * brokered credential for a host outside the profile is refused, and a live
1334
+ * `setNetworkPolicy` may only narrow within the profile. No hosts is
1335
+ * `deny-all`. See `docs/sdk/sandbox-egress-profiles.md`.
1336
+ */
1337
+ readonly egressProfile?: SandboxEgressProfile
1297
1338
  readonly defaultTimeoutMs?: number
1298
1339
  readonly defaultMemoryLimitMb?: number
1299
1340
  readonly defaultMaxProcesses?: number
@@ -1324,7 +1365,10 @@ interface SandboxProviderConfigBase {
1324
1365
  * of a provider that confines nothing.
1325
1366
  */
1326
1367
  export function createSandboxProvider(config: SandboxProviderConfig): SandboxProvider {
1327
- const backend = pickBackend(config)
1368
+ // Before the backend is built, so a profile the backend cannot honour is
1369
+ // refused with nothing constructed.
1370
+ const resolvedEgress = resolveProviderEgress(config)
1371
+ const backend = pickBackend(config, resolvedEgress.profile)
1328
1372
  const id = `namzu-${backend.tier}-${backend.name}`
1329
1373
  const name = `@namzu/sandbox: ${describeBackend(config.backend)}`
1330
1374
  return {
@@ -1339,7 +1383,7 @@ export function createSandboxProvider(config: SandboxProviderConfig): SandboxPro
1339
1383
  return await backend.create({
1340
1384
  ...(perCall?.signal !== undefined ? { signal: perCall.signal } : {}),
1341
1385
  workingDirectory: perCall?.workingDirectory ?? '/workspace',
1342
- ...(config.defaultEgress !== undefined ? { egress: config.defaultEgress } : {}),
1386
+ ...(resolvedEgress.egress !== undefined ? { egress: resolvedEgress.egress } : {}),
1343
1387
  ...(perCall?.timeoutMs !== undefined
1344
1388
  ? { timeoutMs: perCall.timeoutMs }
1345
1389
  : config.defaultTimeoutMs !== undefined
@@ -1361,7 +1405,10 @@ export function createSandboxProvider(config: SandboxProviderConfig): SandboxPro
1361
1405
  }
1362
1406
  }
1363
1407
 
1364
- function pickBackend(config: SandboxProviderConfig): SandboxBackend {
1408
+ function pickBackend(
1409
+ config: SandboxProviderConfig,
1410
+ egressProfile: SandboxEgressProfile | undefined,
1411
+ ): SandboxBackend {
1365
1412
  const backend = config.backend
1366
1413
  // Checked ahead of the `docker` default below: `ACIStandbyPoolBackendConfig`
1367
1414
  // is a real arm of `SandboxProviderConfig` (see the discriminated union
@@ -1434,6 +1481,7 @@ function pickBackend(config: SandboxProviderConfig): SandboxBackend {
1434
1481
  ...(backend.writableRootfsPaths !== undefined
1435
1482
  ? { writableRootfsPaths: backend.writableRootfsPaths }
1436
1483
  : {}),
1484
+ ...(egressProfile !== undefined ? { egressProfile } : {}),
1437
1485
  })
1438
1486
  }
1439
1487
  if (backend.tier === 'container' && backend.runtime === 'runsc') {
@@ -1468,6 +1516,7 @@ function pickBackend(config: SandboxProviderConfig): SandboxBackend {
1468
1516
  ...(backend.writableRootfsPaths !== undefined
1469
1517
  ? { writableRootfsPaths: backend.writableRootfsPaths }
1470
1518
  : {}),
1519
+ ...(egressProfile !== undefined ? { egressProfile } : {}),
1471
1520
  })
1472
1521
  }
1473
1522
  // `microvm:self-hosted` targeting the OWNED Azure Firecracker