ai-sdk-harness
Packages

ai-sdk-harness-network

The hosts an AI SDK harness agent's sandbox may reach: trusted lists for the forges, registries, toolchains and container registries of the whole ecosystem, the hosts of each harness and model provider, and a closed network policy built from them.

The hosts the sandbox of an AI SDK harness agent may reach, so that its network can be closed to everything else: trusted lists for the forges, package registries, toolchains, container registries and Linux archives of the whole ecosystem, the hosts of every @ai-sdk/harness-* runtime and of the model providers, and the functions that check and merge them — with those of a workspace and of plugins — into the network policy every sandbox package takes.

At a glance

A coding agent needs few hosts: its model's API, the registry its runtime installs from, the forge of its repository, the registries of the project's packages. Every sandbox package takes them as allowedHosts and lets nothing else through; every application ends up writing the same list. This package writes it once:

import { harnessHosts, networkPolicy, trustedHosts } from 'ai-sdk-harness-network';
import { createSbxNetworkSandboxSession } from 'ai-sdk-sandbox-sbx';

const sandboxSession = await createSbxNetworkSandboxSession({
  ports: [4000],
  networkPolicy: networkPolicy(
    trustedHosts('github', 'javascript'), // GitHub, npm, Yarn, pnpm, Bun, JSR, Deno, Node.js
    harnessHosts(harness.harnessId), // Claude Code: registry.npmjs.org, api.anthropic.com
    ['api.acme.dev'], // your own
  ),
});
// { mode: 'custom', allowedHosts: ['*.githubusercontent.com', 'api.acme.dev', 'api.anthropic.com', …] }
The sandbox tries to reachWith the policy above
git clone https://github.com/acme/widgetsAllowed: github.com
pnpm installAllowed: registry.npmjs.org
A release asset, a raw file of GitHubAllowed: *.githubusercontent.com, where GitHub serves them
The model API, for the agent's turnAllowed: api.anthropic.com
curl https://example.comRefused by the sandbox's proxy
pip install requestsRefused: Python was not listed (trustedHosts('python'))

Installation

pnpm add ai-sdk-harness-network

No dependency, peer or runtime: it is lists of hosts and the functions that check them. It runs in the browser too, where a settings form checks the hosts a user types as your server does.

Usage

Trusted hosts

trustedHosts(...names) gives the hosts of the services named, or of the groups of them, every service without any. TRUSTED_HOSTS holds them by service, TRUSTED_GROUPS the services of each group (— for a service in none):

GroupServiceHosts
forgesgithubgithub.com, api.github.com, codeload.github.com, *.githubusercontent.com
gitlabgitlab.com, registry.gitlab.com, cdn.registry.gitlab-static.net
bitbucketbitbucket.org, api.bitbucket.org
codebergcodeberg.org
javascriptnpmregistry.npmjs.org
yarnregistry.yarnpkg.com, repo.yarnpkg.com
pnpmget.pnpm.io
bunbun.sh
jsrjsr.io, npm.jsr.io
denodeno.land, dl.deno.land
nodejsnodejs.org, unofficial-builds.nodejs.org
pythonpypipypi.org, files.pythonhosted.org
uvastral.sh, releases.astral.sh
condaconda.anaconda.org, repo.anaconda.com
cpythonwww.python.org, bootstrap.pypa.io
rustcratescrates.io, index.crates.io, static.crates.io
rustupsh.rustup.rs, static.rust-lang.org
—goproxy.golang.org, storage.googleapis.com, sum.golang.org, go.dev, dl.google.com
jvmmavenrepo.maven.apache.org, repo1.maven.org
google-mavenmaven.google.com, dl.google.com
gradleservices.gradle.org, downloads.gradle.org, plugins.gradle.org, plugins-artifacts.gradle.org
—rubygemsrubygems.org, index.rubygems.org
—packagistpackagist.org, repo.packagist.org, getcomposer.org
dotnetnugetapi.nuget.org, www.nuget.org, globalcdn.nuget.org
dotnet-sdkdot.net, builds.dotnet.microsoft.com
—pubpub.dev, storage.googleapis.com
—hexhex.pm, repo.hex.pm, builds.hex.pm
—cocoapodscdn.cocoapods.org, trunk.cocoapods.org
—swiftdownload.swift.org
—crancloud.r-project.org, cran.r-project.org
—hackagehackage.haskell.org, downloads.haskell.org, get-ghcup.haskell.org
—cpanwww.cpan.org, cpan.metacpan.org, fastapi.metacpan.org
—zigziglang.org
infrastructureterraformregistry.terraform.io, releases.hashicorp.com
opentofuregistry.opentofu.org
helmget.helm.sh
kubernetesdl.k8s.io, registry.k8s.io, *.pkg.dev
containersdocker-hubregistry-1.docker.io, auth.docker.io, production.cloudfront.docker.com, production.cloudflare.docker.com
ghcrghcr.io, pkg-containers.githubusercontent.com
quayquay.io, *.quay.io
mcrmcr.microsoft.com, *.data.mcr.microsoft.com
gcrgcr.io, *.pkg.dev
linuxdebiandeb.debian.org, security.debian.org
ubuntuarchive.ubuntu.com, security.ubuntu.com, ports.ubuntu.com
alpinedl-cdn.alpinelinux.org

Each list names the hosts its default tool reaches, redirects followed: Gradle's distributions, uv, pnpm, Bun and OpenTofu's providers download from GitHub's releases (github), Go's module proxy from Cloud Storage. And the domain beside its *. wildcard: whether *.example.com covers example.com itself differs from one sandbox to another (microsandbox's does, srt's does not). test/presets.e2e-spec.ts fetches from each service, a Docker Hub image included, in a Docker Sandbox closed to all but trustedHosts(), and checks the proxy refused nothing.

Some services rely on hosts shared with everyone: storage.googleapis.com (Go, Dart and Flutter), dl.google.com, *.pkg.dev. A sandbox that reaches them reaches any public bucket or registry there: name the services a project needs rather than all of them when that matters.

The harness's own hosts

harnessHosts(harnessId, options?) gives the hosts the harness's runtime reaches from the sandbox, by the harnessId of its adapter: where it installs, its own service, and the API of its models' provider. HARNESS_NETWORK holds them, read from each adapter — the packages it installs, the hosts its credentials are brokered to — and from each runtime's network documentation:

HarnessInstalls fromIts serviceIts providerOptional
claude-coderegistry.npmjs.organthropichttp-intake.logs.us5.datadoghq.com, browser-intake-us5-datadoghq.com (telemetry, errors)
codexregistry.npmjs.orgopenai, chatgpt
opencoderegistry.npmjs.orgnone: name onemodels.dev (its list of models)
deepagentsregistry.npmjs.organthropic
cursorcursor.com, downloads.cursor.com*.cursor.sh
github-copilotregistry.npmjs.orggithub.com, api.github.com, api.githubcopilot.com, *.githubcopilot.comcollector.github.com, copilot-telemetry.githubusercontent.com, default.exp-tas.com
grok-buildregistry.npmjs.orgcli-chat-proxy.grok.com (a subscription)xaicode.grok.com, assets.grok.com
cline, pi, fx———They run on your server, the sandbox their file system and shell only: it reaches nothing
  • provider takes another provider in place of the harness's own: harnessHosts('claude-code', { provider: 'gateway' }) for the Vercel AI Gateway, { provider: 'amazon-bedrock', region: 'eu-west-3' }, { provider: 'google-vertex', region: 'europe-west1' }. OpenCode has no provider of its own: name it.
  • optional: true adds what a turn does without: refused, the runtime carries on. Most runtimes turn it off in their settings (CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 for Claude Code, --disable-client-telemetry for Copilot).
  • install: false leaves out where the runtime installs from, for a sandbox started from a template that holds it.
  • Codex calls api.openai.com with an API key and chatgpt.com with a ChatGPT sign-in; Claude Code and Codex refresh a subscription's token on your server, never in the sandbox.
  • An ACP agent (@ai-sdk/harness-acp) has the id you give it and the hosts of the agent you install: list them, beside registry.npmjs.org where its bridge installs from. A harness the package does not know gives none.

Model providers

providerHosts(provider, location?) gives the hosts of a provider's API, by the name of its AI SDK provider package (@ai-sdk/<provider>): the default baseURL of that package. PROVIDER_HOSTS holds those whose host is the same for everyone:

ProviderHosts
anthropic, openai, chatgpt (a ChatGPT sign-in), google (Gemini API), xai, mistral, groq, deepseek, cohere, perplexity, togetherai, fireworks, cerebras, deepinfra, baseten, huggingface, moonshotai, zai, alibaba, minimax, bytedance, gmicloudthe API host of each: api.anthropic.com, generativelanguage.googleapis.com, router.huggingface.co…
gateway (Vercel AI Gateway), openrouter, ollama (Ollama's cloud), github-copilotai-gateway.vercel.sh, openrouter.ai, ollama.com, api.githubcopilot.com
google-vertex, { region }: a region, or global (the default), us, eu<region>-aiplatform.googleapis.com, aiplatform.googleapis.com or aiplatform.<us|eu>.rep.googleapis.com, and oauth2.googleapis.com for a service account's token
amazon-bedrock, { region } (required)bedrock-runtime.<region>.amazonaws.com
azure, { resource }: your resource, any without it<resource>.openai.azure.com, <resource>.services.ai.azure.com

A region or a resource is a label of a host name: anything else throws. A provider the package does not know gives none.

With a workspace and plugins

ai-sdk-harness-workspace names the hosts it fetches the repositories from, and ai-sdk-harness-plugins those the selected plugins reach from the sandbox. Merge them with the presets:

import { harnessHosts, networkPolicy, trustedHosts } from 'ai-sdk-harness-network';

const policy = networkPolicy(
  trustedHosts('javascript'),
  harnessHosts('claude-code'),
  await workspace.hosts({ sessionId }), // ['github.com']
  catalog.hosts(selection).sandbox, // ['mcp.example.com', 'registry.npmjs.org']
);

The same list goes to every sandbox package, under the name it takes:

PackageWhere the hosts go
ai-sdk-sandbox-sbxnetworkPolicy at creation, setNetworkPolicy() while it runs
ai-sdk-sandbox-microsandboxnetworkPolicy at creation
ai-sdk-sandbox-cloud-runallowedHosts, on top of the service's SANDBOX_ALLOWED_HOSTS, and setNetworkPolicy()
ai-sdk-sandbox-runtimeallowedDomains at creation (allowedHosts(...)), and setNetworkPolicy()

Checking hosts

allowedHosts(...lists) returns the hosts of the lists, in lower case, each once and sorted; networkPolicy(...lists) wraps them in { mode: 'custom', allowedHosts }. Both throw an InvalidHostError, which names the host, for one isAllowedHost(host) refuses:

  • a host name, api.github.com, or *.<domain> for the subdomains of a domain of two labels at least, *.githubusercontent.com;
  • no scheme, path nor port: a network policy allows a host on every port;
  • never * alone nor *.com, which would open the whole network.
import {
  InvalidHostError,
  isAllowedHost,
  networkPolicy,
  trustedHosts,
} from 'ai-sdk-harness-network';

isAllowedHost('*.example.dev'); // true
isAllowedHost('https://example.dev'); // false

try {
  networkPolicy(trustedHosts('javascript'), settings.extraHosts);
} catch (error) {
  if (error instanceof InvalidHostError) console.error(`Not a host: ${error.host}`);
}

Reference

ExportWhat it is
networkPolicy(...){ mode: 'custom', allowedHosts } from lists of hosts, checked and merged
allowedHosts(...)The hosts of lists, checked, in lower case, unique and sorted
isAllowedHost(host)Whether a network policy takes host
trustedHosts(...)The hosts of services or groups (TrustedService, TrustedGroup), or of all
TRUSTED_HOSTSThe trusted hosts, by service
TRUSTED_GROUPSThe services of each ecosystem
harnessHosts(id, options?)The hosts of a harness's runtime, by its harnessId (HarnessHostsOptions)
HARNESS_NETWORKWhat each harness the package knows reaches (KnownHarness, HarnessNetwork)
providerHosts(provider)The hosts of a model provider's API (ModelProvider, ProviderLocation)
PROVIDER_HOSTSThe hosts of the providers whose host is the same for everyone (FixedProvider)
InvalidHostErrorThrown for a host a network policy cannot take; its host is the one given

Limitations

  • The hosts of each harness belong with its adapter (@ai-sdk/harness-*), and those of each provider with its package: none publishes them yet. HARNESS_NETWORK and PROVIDER_HOSTS stand in for them, and follow the runtimes and the providers as they change.
  • test/presets.e2e-spec.ts runs real turns of Claude Code and Codex only; the other runtimes' hosts come from their adapters and documentation.
  • A registry set in a project's own configuration (.npmrc, a mirror, a private index) is not known: add its host. So are Fedora's mirrors, which vary from one download to the next, and Amazon ECR Public, whose layers come from an opaque CloudFront host.
  • A wildcard's reach depends on the sandbox: list a domain beside its *. wildcard when you need both.

Development

pnpm --filter ai-sdk-harness-network test
pnpm --filter ai-sdk-harness-network test:e2e # Docker Sandboxes, Claude Code and Codex credentials

License

MIT

On this page