Skip to content

Network Egress

llms.txt lists every page for an agent

chant sends no telemetry. Nothing in core or in any lexicon reports usage, versions, errors or identities anywhere, and there is no endpoint to opt out of.

This page lists all the network chant uses: every module that calls a network primitive, which command reaches it, and where it dials. The tables below are generated from test/egress-catalogue.ts, which the same test that guards the offline phases checks against the tree on every run. A module that starts calling fetch and is not in the catalogue fails CI.

An air-gapped mode does not belong in core.

At apply time chant must reach the AWS, Azure, GCP, Kubernetes or Fly endpoint the project describes, so a mode that cut that off would leave chant nothing to deploy to. Every other connection an adopter needs to cut already has its own fix: committed spec snapshots for codegen, the forgejo lexicon’s mirror base for uses: resolution, the build archive’s load-image-on-host backend for registry-free delivery, and the emulators for a whole substrate.

So offline use is a pattern documented on this page, and a test guarantees the offline phases.

A guarded row is executed by test/no-egress.e2e.test.ts over every project in the example corpus, with net.Socket.prototype.connect and globalThis.fetch replaced by throwing stubs. Adding a network call to one of these paths fails that test.

CommandBehaviourGuarded by test
chant buildDiscovery, evaluation and serialization. No network in process, except a k8sInclude URL whose digest is not yet cached (see above). One declaration puts a network-reaching child on this path — HelmRender with a repo, which renders the chart at synthesis time; see the shell-outs below.yes
chant build --foldThe same, with modules reduced to values instead of executed. No network in process.yes
chant build --sandboxProject code is evaluated in a child process instead. Neither process reaches the network.yes
chant lintDeclarative rules and post-synth checks, over the built documents. No network in process.yes
chant scenario checkScenarios are evaluated against a fixture snapshot, which is what replaces the live read. A scenario whose given is snapshot(env) rather than a file first runs git fetch for the lifecycle branch — see the shell-outs below.yes
chant searchAnswers from the declared graph. --live and --at are the flags that read an estate, and they land in the apply phase above.yes
chant graph, describe, listProjections of the same built graph. --live again is the opt-in.static scan only

An in-process guard says nothing about a child process, so the offline phases’ shell-outs are enumerated instead. The guard records every spawn and fails on a binary that is not listed here.

Reaches a network is the column to read. Two of these do, and both are conditional — they need a declaration or a flag that a project either has or does not. Nothing else spawned on an offline path leaves the machine.

BinarySpawned byReaches a networkWhat it does
gitchant lintnogit check-ignore --stdin, to drop ignored paths from the file scan. Local repository read; no remote.
child.mjschant build --sandbox, chant lint --sandboxnoThe esbuild-bundled sandbox child, forked under process.execPath with --permission and a closed environment (PATH, plus CHANT_ENV for the config child). Node’s Permission Model has no network flag, so the child is bounded by what the bundle contains rather than by a kernel gate — see Sandboxed Execution.
gitchant scenario checkyes, conditionallygit remote then git fetch <remote> chant/lifecycle, and only when a scenario’s given is snapshot(env) rather than a fixture file. This one does reach the configured git remote — the same remote the repository is already cloned from.
helmchant build, on a project declaring HelmRenderyes, conditionallyhelm version, then helm template <name> <chart> --include-crds [--repo <url> --version <v>]. A HelmRender composite resolves at synthesis time, so a project that declares one puts a chart render on its build path. With repo set, helm fetches the chart from that repository — this is the one build-path shell-out that reaches a network, on first synth only: the rendered manifests are cached under the render root (CHANT_HELM_RENDER_ROOT, default ~/.chant/helm-renders/) and every later build reads the cache. A project declaring no HelmRender never spawns it.

chant lifecycle diff --live, plan, snapshot, apply, converge, chant search --live, chant import --live, chant workspace graph --live, chant workspace check --live, chant run and every component verb talk to the substrate they manage. The two workspace commands do it through each member’s own chant graph --live, so they reach what that member’s lexicon reaches, and they are the only workspace commands that do (chant workspace check without --live reads files only). This is the provider API, not a chant service — the same endpoint a console session or an SDK call would use, with the same credentials.

ModulePrimitiveDestinationWhy
packages/core/src/components/verbs/cloud-executor.tsnode:neta declared cluster member’s bolt portThe agnostic wait-cluster-healthy verb probes each member’s port to decide quorum; injectable, so a test never opens a socket.
packages/core/src/components/verbs/wait-verify.tsfetchthe endpoint a component declareswait-endpoint and health-gate poll the deployed thing they were pointed at; the fetcher is injectable for the same reason.
lexicons/aws/src/api/read-client.tsfetchAWS Cloud Control and CloudFormation, or AWS_ENDPOINT_URLThe aws lexicon’s read transport, signed with SigV4 and retargetable at a local emulator; there is no AWS SDK in the tree.
lexicons/aws/src/api/credential-chain.tsfetchthe AWS IAM Identity Center portal (portal.sso.<region>.amazonaws.com) and SSO OIDC (oidc.<region>.amazonaws.com)chant lifecycle whoami resolves an SSO profile as the AWS CLI does (#3954): it refreshes an expired cached session token through OIDC when it can, then trades the token for role credentials. A credential_process profile runs the configured command, which may reach whatever it reaches.
lexicons/aws/src/op/activities/aws-apply.tsfetchAWS Cloud Control, or AWS_ENDPOINT_URLawsApply writes the change set the plan produced, over the same transport the read client uses.
lexicons/aws/src/receipt-store.tsfetchAWS SSM Parameter Store, or AWS_ENDPOINT_URL_SSMEffect receipts are read before an effect step and written after it succeeds; the store is the aws implementation of core’s injectable receipt seam.
lexicons/aws/src/agentcore/trace-fetch.tsfetchBedrock AgentCoreawsAgentCoreFetchTrace pulls a session history and renders it as replay-trace text for a later cedar policy replay.
lexicons/azure/src/api/read-client.tsfetchAzure Resource Manager, or a declared endpoint overrideThe azure lexicon’s read transport for observation and live import.
lexicons/azure/src/op/activities/az-apply.tsfetchAzure Resource Manager, or a declared endpoint overrideazApply performs ARM resource CRUD for the native local applier and for real subscriptions alike.
lexicons/gcp/src/api/read-client.tsfetchGoogle Cloud REST APIs, or a declared endpoint overrideThe gcp lexicon’s read transport, resolving Config Connector kinds to their underlying REST resources.
lexicons/gcp/src/op/activities/gcp-apply.tsfetchGoogle Cloud REST APIs, or a declared endpoint overridegcpApply performs the REST calls a Config Connector manifest implies.
lexicons/cpln/src/api.tsfetchthe Control Plane API, or a declared endpointThe cpln lexicon’s read transport, injectable through CplnHttp so tests need no network.
lexicons/grafana/src/api/client.tsfetchthe Grafana HTTP API named by grafana.profiles.<env> or GRAFANA_URLThe grafana lexicon’s transport for lifecycle diff --live and import --from, injectable through GrafanaHttp so tests need no network.
lexicons/fly/src/op/activities/fly-apply.tsfetchthe Fly Machines API, or FLY_API_HOSTNAME (the mudflaps emulator)flyApply creates, updates and destroys Machines; pointing it at the emulator is how the Fly tutorials run offline.
lexicons/fly/src/op/activities/machine-release.tsfetchthe app’s own health endpoint, at the URL a release step is givenflyMachineVerify checks that a released Machine’s app answers its health endpoint with that release (#2736). The Machines API calls go through fly-apply.ts’s client.
lexicons/fly/src/components/fly-release.tsfetchthe app’s own health endpoint, through flyMachineVerifyThe fly-release and fly-rollback capabilities hand an injectable fetch to flyMachineVerify, so tests reach no network (#2736).
lexicons/fly/src/op/activities/sprites.tsfetchthe Sprites API, or SPRITES_BASE_URLSprite lifecycle activities — create, exec, destroy — for the Sprites Ops.
lexicons/fly/src/op/activities/sprite-service-converge.tsfetcha box service’s own health URL, as the Op declares itspriteServicesObserve probes each declared service’s health URL for a ConvergeOp, and spriteServiceRestart waits for it after a restart (#2778). The Sprites API calls go through sprite-services.ts.
lexicons/fly/src/op/activities/sprite-fs.tsfetchthe Sprites filesystem API, or SPRITES_BASE_URLStages an input file into a sprite and reads a result out, without shelling it through spriteExec.
lexicons/fountain/src/op/activities/fountain-apply.tsfetchthe fountain control-plane API, or a declared endpointfountainApply performs the lexicon’s resource CRUD.
lexicons/fountain/src/op/runtime.tsfetchthe fountain control-plane API, or a declared endpointchant run <op> --on fountain posts the run to a steward’s thread and tails its SSE stream.
lexicons/render/src/op/activities/render-apply.tsfetchthe Render APIrenderApply performs the lexicon’s resource CRUD.
lexicons/k8s/src/op/activities/argo.tsfetchan Argo CD server’s APIThe Argo sync/wait activities drive an installed Argo CD rather than reimplementing it.
lexicons/cedar/src/avp/client.tsfetchAmazon Verified PermissionsThe AVP read transport, hand-rolled over the AWS JSON protocol for the same reason the aws lexicon is: no AWS SDK in a vendor-neutral lexicon’s dependency tree.
packages/core/src/op/activities/http-check.tsfetchwhatever URL the Op step declareshttpCheck is a generic probe step — the URL is the caller’s, and the activity has no default of its own.
packages/core/src/op/decide-backend.tsfetcha Jev-compatible POST /v1/systemone server a backend declaresThe decide activity asks a decision point’s model decider. It runs only as an Op step: no chant read calls a model (ws-052).
packages/core/src/op/activities/workflow-audit.tsfetchapi.github.com, or a declared GitHub hostThe workflow-audit activity resolves an action reference to a commit so a pipeline can assert what it actually ran.
packages/core/src/pr-forge.tsfetchthe forge that hosts the pull request: api.github.com or $GITHUB_API_URL, a Forgejo instance’s API, or a GitLab instance’s APIThe pull-request loop (#3183): chant components pr-plan --forge keeps one note on the pull request and sets a commit status, and pr-apply lists approving reviews and finds the pull request that merged a commit. reconcilePr’s comment and issue finding modes post through the GitHub and Forgejo clients too (#3757). terragucci and SQL Yodeler import it from @intentius/chant/pr-forge for their own notes, statuses and tracking issues (#3759). On a Forgejo or GitLab job, reconcilePr’s pull-request mode, lexiconUpgrade, rollback and proposeWorkspaceUpgrade find, open, edit and label their pull request through it, since gh pr speaks a GraphQL API Forgejo does not serve (#3798). Only those CI paths reach it, and each client takes an injected fetch, so no test opens a socket.
packages/core/src/op/activities/reconcile.tsfetchthe forge a reconcile finding is posted to: api.github.com or $GITHUB_API_URL, a Forgejo instance’s API, or $CI_API_V4_URL on GitLabreconcilePr’s comment and issue finding modes keep one pull request comment, merge request note or issue (#2256, #2292, #3757): GitLab over its REST API here, GitHub and Forgejo through pr-forge.ts’s clients. Only an Op step in one of those modes reaches it.
packages/core/src/op/activities/forge-pull-request.tsfetchthe forge a Forgejo or GitLab job runs on: a Forgejo instance’s API, or $CI_API_V4_URL on GitLabPicks the pr-forge.ts client that reconcilePr’s pull-request mode, lexiconUpgrade, rollback and proposeWorkspaceUpgrade open their pull request through on Forgejo and GitLab, where gh pr cannot (#3798), and passes it the caller’s fetch. GitHub, and a run outside CI, keep gh.
packages/core/src/gate-resume-run.tsfetchthe forge that runs the job waiting at a gate: $GITHUB_API_URL, a Forgejo instance’s API, or $CI_API_V4_URL on GitLab, as the pending fact recorded itGate resume (#3683): chant approve --resume and chant run resume read the waiting run’s state and re-run it (GitHub), retry its job (GitLab) or dispatch its workflow (Forgejo). Only those two commands reach it, and the client takes an injected fetch, so no unit test opens a socket.
packages/core/src/cli/handlers/gate-resume.tsfetchthe same forge as packages/core/src/gate-resume-run.ts, through itThe handlers of chant approve --resume and chant run resume (#3683) pass an injected fetch through to resumeGateRun; they open no connection of their own.
packages/core/src/notify.tsfetchthe URLs the environment variables named by an Op waves spec’s notify hold: a Slack or Microsoft Teams incoming webhook, or the team’s own webhookNotify (#3761): chant run wave posts an event when a wave waits for an approval, refuses a moved plan or fails, and products built on chant send the same event. Only a spec with notify reaches it, a failed delivery never changes the exit code, and sendNotify takes an injected fetch, so no unit test opens a socket.
packages/core/src/op/gate-review.tsfetchthe forge that hosts the merged pull request: $GITHUB_API_URL, a Forgejo instance’s API, or $CI_API_V4_URL on GitLabA pr-review wave gate (#3684): chant run wave finds the pull request that merged the applied commit and reads its reviews, its author and the reviewers’ access. Only a wave whose approval mode is pr-review reaches it, and the client takes an injected fetch, so no unit test opens a socket.
packages/core/src/lifecycle/release-publish.tsfetchthe registries and forges a member’s release.publish declareschant release publish --yes (#4047, #4048) hands the global fetch to the OCI and forge targets as their default HTTP client. It only names the default; the requests are the targets’. Tests inject a fake fetch.
packages/core/src/lifecycle/publish-oci.tsfetchan OCI registry a release.publish target names (GHCR, Docker Hub, any registry API v2)chant release publish --yes pushes a release’s images by digest and, at the reveal, moves their tags (#4047, #4048). Tests run against a registry fake on 127.0.0.1.
packages/core/src/lifecycle/publish-forge.tsfetchthe GitHub, GitLab or Forgejo API a release.publish forge target nameschant release publish --yes uploads a release’s assets to a draft release and publishes it at the reveal (#4047, #4048). Tests inject a recording fetch.
packages/core/src/op/activities/pipeline-audit.tsfetcha GitLab instance’s APIThe pipeline-audit activity reads project and pipeline metadata for the same governance question on GitLab.
lexicons/prometheus/src/api/client.tsfetcha Mimir, Cortex or Loki ruler, a plain Prometheus, or an Alertmanager, at the target a profile declaresThe prometheus lexicon’s transport (#3371): lifecycle diff --live, import --from and the ruler ApplyOp target all read and write through it, with the declared tenant and credentials. It takes an injected PromHttp, so tests need no network.
lexicons/prometheus/src/op/activities/rules-loaded.tsfetcha Prometheus at the declared URL, $PROMETHEUS_URL, or http://localhost:9090rulesLoadedObserve reads /api/v1/rules so a ConvergeOp can see whether each declared rule group is loaded and healthy (#3369).
lexicons/prometheus/src/op/activities/rule-audit.tsfetcha Prometheus at the declared URL, $PROMETHEUS_URL, or http://localhost:9090ruleAudit, the activity behind RuleAuditOp, reads rule health and alerts and queries each rule selector to find series nothing writes (#3369).
lexicons/prometheus/src/op/activities/silence.tsfetchan Alertmanager at the declared URL, $ALERTMANAGER_URL, or http://localhost:9093alertmanagerSilence and alertmanagerUnsilence create and expire the silence an Op step holds over a change (#3369).
lexicons/otel/src/op/activities/collector-health.tsfetcha running collector’s own health_check, zpages and metrics endpoints, or the hosts an Op step names in their placecollectorHealthObserve probes the endpoints a collector’s config declares so a ConvergeOp can tell whether it is up (#3369).
lexicons/otel/src/op/activities/github.tsfetchapi.github.com release listsCollectorAuditOp reads the collector and semantic-conventions release lists here (#3369). The sticky issue that CollectorAuditOp and RuleAuditOp keep, and the proposal pull request, go through git and gh child processes.
lexicons/otel/src/op/activities/collector-audit.tsfetchraw.githubusercontent.com, for each built-in component’s metadata.yaml in the collector repositoriescollectorAudit, the activity behind CollectorAuditOp, compares component stability at the pinned and the newest collector release (#3369). The release lists come through github.ts.
packages/core/src/behaviour-http.tsfetchthe behaviour engine an operator named in CHANT_BEHAVIOUR_ENGINE, where that address is an http or https URLThe first behaviour-engine adapter (#2359): chant graph --live --overlay POSTs the resource graph a lexicon rendered and reads back per-entity cost, headroom and resilience. Not a provider API and not a chant service — a third party the operator chose, and the only row here that dials one. Unset, nothing is dialled and the overlay refuses by name; the transport is injectable, so no test opens a socket.
lexicons/sql/src/clickhouse/http.tsfetcha ClickHouse server’s HTTP interface: the one sql.profiles.<env> (or CLICKHOUSE_URL) binds the environment to, and on a ClickHouse pin move the pinned clickhouse/clickhouse-server container generation starts on a loopback portThe sql lexicon’s only client. describeResources reads system.databases and system.tables, and chant import --from adds SHOW CREATE per object; all reads. clickhouseApply (the clickhouse ApplyOp target) writes: the CREATE, ALTER and, under a prune, DROP statements a build’s classified changes need, and reads system.mutations while a background rewrite runs. Generation reads the pinned server’s system.* catalog only when the pin moves or npm run generate -- --force asks; every other generate, bundle, validate and build reads the committed snapshot and reaches nothing.
lexicons/sql/src/postgres/live/client.tsdatabase-client-packagea Postgres server: the one sql.profiles.<env> (a postgres:// URL) or POSTGRES_URL binds the environment toThe Postgres dialect’s client, node-postgres (pg), loaded only when a Postgres server is read. describeResources, observeResourcesDeep and chant import --from read the catalog (pg_catalog, and pg_get_*def() for each object’s statement); all reads, in a session with an empty search_path. A ClickHouse-only project never loads it.

chant emulator up pulls a container image and then polls the container it just started on localhost. The poll is loopback traffic; the image pull is the container runtime’s, and is the only egress in the phase.

ModulePrimitiveDestinationWhy
lexicons/fly/src/op/activities/machines-local.tsnode:net127.0.0.1 only: a free port for a Machine’s service, and a connect that waits for it to listenThe opt-in running mode of the local Machines API runs each started Machine on this host and publishes its service on a loopback port (#2831). Only tests and a local runner reach it.
scripts/api-call-proxy.mjsnode:httpthe local floci emulator, on TARGET_PORT (default 4691), from a proxy bound to loopbackchant#2403’s counting reverse proxy. The scale bench’s cost number has to be measured rather than asserted, so test/scale-estate.sh points AWS_ENDPOINT_URL at this and it forwards each AWS Query POST to floci unchanged while counting it by Action. Loopback to loopback: it dials the emulator a developer already started, never a network chant does not control, and nothing on an adopter’s path runs it.
packages/core/src/op/emulator-lifecycle.tsfetch, node:netthe just-started emulator container on localhost, and 127.0.0.1 portsThe shared boot loop polls the container’s health endpoint until it answers, and hostPortInUse opens a connect to 127.0.0.1 to see whether a host port is taken before a container is bound to it; loopback only, and the image pull it depends on is the container runtime’s.
lexicons/sql/src/op/activities/clickhouse-emulator.tsfetchthe ClickHouse emulator’s HTTP endpoint, as chant emulator status prints itThe identity probe asks the server for its UUID and version (#3673) so a server of another origin on the same port is told apart from the emulator; the endpoint is the container chant emulator up started, on localhost.

chant dev generate, chant dev pinned-upgrade and chant vendor fetch upstream schemas. Every lexicon’s spec/fetch.ts runs here and nowhere else: the generated types and the committed spec snapshot are what a build reads, so a machine that never runs codegen never needs the endpoints below. chant dev pinned-upgrade is the one command here that queries api.github.com, and it is a lexicon-maintainer command — it moves a pin in a lexicon’s own source, and nothing on an adopter’s build, lint or apply path calls it.

ModulePrimitiveDestinationWhy
packages/core/src/codegen/fetch.tsfetchthe upstream schema URL a caller passesfetchWithCache/fetchWithRetry — the single transport under every lexicon’s spec/fetch.ts, the k8s CRD loader, chant vendor and chant dev pinned-upgrade. Caches to disk, bounds each attempt, and falls back to the committed snapshot when the endpoint is unreachable.
lexicons/aws/src/spec/fetch.tsfetchthe CloudFormation resource specification and Cloud Control schema endpointsThe aws spec fetch follows redirects itself rather than going through the shared cache helper; run by chant dev generate, never by a build.
lexicons/k8s/src/crd/oci-chart.tsfetchthe OCI registry of a type: "helm" CRD source (today ghcr.io, for the KubeMicroVM operator chart at its pinned version and layer digest)Pulls a chart’s content layer over the registry API so k8s generation reads chart-only CRDs without needing helm on PATH (chant #3300). The bare fetch is the anonymous-token probe, which expects a 401; the token, manifest and blob go through fetchWithRetry. Run by npm run generate, never by a build.
lexicons/aws/scripts/refresh-enum-overlay.tsfetchraw.githubusercontent.com, for the botocore service models the overlay’s own entries nameDiffs the curated enum overlay (chant #1497) against the botocore shapes its values were read from, and with --write restamps them. A maintainer command, reached only by hand or by just refresh-enum-overlay; generate, bundle, validate, build and the test suite all read the committed src/codegen/enum-overlay.json and reach nothing.
lexicons/cpln/src/spec/snapshot-cli.tsfetchthe Control Plane OpenAPI documentjust snapshot refreshes the committed offline spec snapshot; run on a networked machine on purpose, and the snapshot is what everything else reads.
lexicons/grafana/src/spec/fetch-cli.tsfetchraw.githubusercontent.com, for the grafana-foundation-sdk JSON Schemas at the pinned commitjust fetch-schemas re-downloads the vendored Grafana schemas when bumping GRAFANA_SCHEMA_PIN; a maintainer command. Generate, bundle, validate, build and the tests read the committed files under src/spec/schemas/ and reach nothing.
lexicons/grafana/src/spec/fetch-units-cli.tsfetchraw.githubusercontent.com, for Grafana’s valueFormats/categories.ts at the tag in GRAFANA_UNITS_SOURCEjust fetch-units re-extracts the unit ids GRAF115 accepts when moving to a newer Grafana; a maintainer command. Build, lint and the tests read the committed src/spec/units.gen.ts and reach nothing.
lexicons/azure/scripts/fetch-quickstart-templates.tsfetchthe Azure quickstart-templates repositoryBuilds the ARM fixture corpus the azure serializer is tested against; a maintainer script, not part of the lexicon’s published surface.
CommandSpawnsModuleDestinationWhy
npm run generate in lexicons/sql after a ClickHouse pin move, or with --forcedocker runlexicons/sql/src/core/container.tsthe container registry, for clickhouse/clickhouse-server at the pinned tag and digest when the image is not already localdocker run starts the pinned server on a random loopback port so generation can read its catalog, and the container is removed afterwards. The pull is the only egress; the catalog read itself is loopback.

A k8sInclude whose source is an http(s) URL is fetched by chant build the first time the build sees its digest. The bytes must match the declared sha256 digest. They are cached under it in ~/.chant/includes/ (moved by CHANT_INCLUDE_CACHE_ROOT). Every later build reads the cache and reaches nothing. A project with no URL include, or with its includes already cached, builds offline.

ModulePrimitiveDestinationWhy
lexicons/k8s/src/include/resolve.tsfetchthe URL a k8sInclude declares as its sourceFetches a pinned multi-document YAML at buildRoots() when no cached copy matches its sha256 digest. Bytes that do not match the digest fail the build and are not cached; matching bytes are cached by digest, so later builds are offline (chant #3566).

chant init --from <repo>@<ref> fetches one commit of a template repository, and nothing else in chant init reaches a network. chant init --template <name> renders a lexicon’s templates from the installed package and reaches nothing, and chant init --from <dir> copies a template directory already on disk and reaches nothing (#2647). The fetch is a git child process, so it is listed below as a shell-out rather than as a module.

CommandSpawnsModuleDestinationWhy
chant init --from <repo>@<ref>git fetchpackages/core/src/workspace/lineage-init.tsthe repository named in --from: a git URL, git@host:path, or https://github.com/<owner>/<name>.git for owner/name. A local path reaches nothinggit fetch --depth 1 <repo> <ref> into a scratch repository that is deleted afterwards. The commit and tree it resolves are the content address the lineage lock records (#2540). Credentials are git’s own, and GIT_TERMINAL_PROMPT=0 stops it asking for any.

chant workspace upgrade <scope> fetches the target version of a git template, and the commit the scope was made from, to rebuild the merge base. Everything after the fetch runs offline in a local worktree. A vendor scope is read from its source as chant vendor pull reads it, and a scope made from a directory reads the --to directory from disk and reaches nothing. The proposeWorkspaceUpgrade activity stages the same upgrade, then pushes a proposal branch and opens or edits a pull request. chant workspace adopt-lineage and chant workspace hash-index list a template’s tags and fetch the versions they compare, and chant workspace versions --available lists the tags of each git template. Every step is a git or gh child process, listed below as shell-outs.

CommandSpawnsModuleDestinationWhy
chant workspace upgrade <scope>git fetchpackages/core/src/workspace/lineage-upgrade.tsthe template repository the lineage lock records for the scope (source.url), or the one --source names when the upgrade moves the scope to another template. A local repository reaches nothinggit fetch --depth 1 <url> <ref> for the target version, then git fetch --depth 1 <url> <commit> for the commit the scope was made from, into a scratch repository deleted afterwards. The second fetch rebuilds the merge base, always from the scope’s recorded source; when a server refuses it, the upgrade goes on without a base for edited files. Credentials are git’s own, and GIT_TERMINAL_PROMPT=0 stops it asking for any (#2550, #2551).
chant workspace adopt-lineage --from <repo>, chant workspace hash-index --from <repo> and chant workspace versions --availablegit ls-remotepackages/core/src/workspace/lineage-hash-index.tsthe template repository named in --from, or for versions --available the one each family’s lock records. A local repository reaches nothinggit ls-remote --tags <url> lists the template’s tags and the commit each names, so a cached hash index is reused only for tags that still name the same commit, and versions can say how many versions are newer than each lock (#2551).
chant workspace adopt-lineage --from <repo> and chant workspace hash-index --from <repo>git fetchpackages/core/src/workspace/lineage-hash-index.tsthe template repository named in --from. A local repository reaches nothinggit fetch --depth 1 --no-tags <url> refs/tags/<tag>... for the version tags the index has to compute, or git fetch --depth 1 <url> <ref> for the one ref --from <repo>@<ref> names, then a cached winner’s tag again to re-check it, into a scratch repository deleted afterwards. Credentials are git’s own, and GIT_TERMINAL_PROMPT=0 stops it asking for any (#2551).
an Op step running the proposeWorkspaceUpgrade activity, in branch or pull-request modegit pushpackages/core/src/op/activities/propose-upgrade.tsthe repository’s own remote (origin unless the step names another)git push --force <remote> <commit>:refs/heads/<branch>, only for the proposal branch (default chant/upgrade/<scope>). The activity refuses the default branch, the base branch and the checked-out branch (#2550).
an Op step running the proposeWorkspaceUpgrade activity, in pull-request modegh prpackages/core/src/op/activities/propose-upgrade.tsthe forge that hosts the remote, through the GitHub CLI and its own credentialsgh pr list to find the scope’s open pull request, then gh pr create or gh pr edit with the staged summary and the patch digest (#2550). On a Forgejo or GitLab job the same calls go over REST through pr-forge.ts instead (#3798).

chant workspace graph --intent, graph --intent --record and chant workspace runs read git history locally and reach nothing, unless --follow-squash is given. Then a squash commit’s pull request head is fetched from origin when the clone lacks it, once per pull request, into refs/chant/pull/<n>/head where later reads find it (#3035). Its row is in the shell-out table below, since git does the fetching.

CommandSpawnsModuleDestinationWhy
chant workspace graph --intent --follow-squash, chant workspace graph --intent --record --follow-squash and chant workspace runs --follow-squashgit fetchpackages/core/src/workspace/squash.tsthe clone’s origin remote, GitHub or Forgejo, which keeps each pull request’s head at refs/pull/<n>/head. A local repository reaches nothinggit fetch --no-tags origin +refs/pull/<n>/head:refs/chant/pull/<n>/head for each squash whose pull request head the clone lacks, in one fetch, so the walk can read the pull request’s original commits (#3035). Credentials are git’s own, GIT_TERMINAL_PROMPT=0 stops it asking for any, and the fetch times out after a minute. Without --follow-squash it never runs.

chant audit <url> reads someone else’s repository over a git host’s API. Auditing a local path reaches nothing.

ModulePrimitiveDestinationWhy
packages/core/src/audit/fetch.tsfetchan allowlisted git host’s API — GitHub, GitLab or ForgejoThe only audit module that touches the network: chant audit <url> pulls candidate files so the auditor can run on a URL. Hosts are allowlisted, redirects refused, and file count, size, bytes and time all capped.

chant ci tick reads each recent commit’s check runs from the forge, then fetches the branch and the ci/* tags from origin and pushes the tags it makes (#3573). It runs in the generated CI file, not on a developer’s path. chant ci last-green and chant ci list read local tags only and reach nothing, except that chant ci list --forge github reads the check runs of the commits that have no tag. A tick from chant runs (#4044) asks the forge for nothing and fetches chant/lifecycle instead.

ModulePrimitiveDestinationWhy
packages/core/src/workspace/ci-green-forge.tsfetchapi.github.com, or $GITHUB_API_URL for GitHub Enterprise Serverchant ci tick lists every check run on each commit within ci.green’s window, to judge its phases by each run’s latest attempt (#3573). The client takes an injected fetch, so no test opens a socket.
CommandSpawnsModuleDestinationWhy
chant ci tickgit fetchpackages/core/src/workspace/ci-green.tsthe repository’s origin remotegit fetch --no-tags --prune origin +refs/heads/<branch>:refs/remotes/origin/<branch> +refs/tags/ci/*:refs/tags/ci/* brings the branch and the ci tags, pruning a ci tag deleted on the remote by hand, so the tick judges what the remote holds (#3573). Credentials are git’s own, and GIT_TERMINAL_PROMPT=0 stops it asking for any.
chant ci tickgit pushpackages/core/src/workspace/ci-green.tsthe repository’s origin remotegit push origin refs/tags/ci/green/<sha> ... pushes the tags the tick made, never forced, so a tag that already exists there is refused rather than moved (#3573).
chant ci tick --source runs, or with ci.green.source set to runsgit fetchpackages/core/src/lifecycle/git.tsthe repository’s origin remote, or its first remote when there is no origingit fetch <remote> chant/lifecycle:chant/lifecycle, never forced, brings the run ledger whose signed Op runs the tick judges (#4044). A local branch ahead of the remote is kept, and one that has diverged stops the tick.
chant components pr-plan --report-url s3://... and chant components pr-apply --report-url s3://...aws s3packages/core/src/pr-report-url.tsthe S3 bucket --report-url names, at the endpoint and region the AWS CLI reads from the job’s environmentaws s3 cp - s3://<bucket>/<prefix>/<pr>/<commit>/<file> copies the stage’s report, its change set and, with --events-json, its run events, the events again as each wave and component settles, so a reader outside CI can fetch them (#4007). Credentials are the CLI’s own; chant passes none and prints none. A file:// URL reaches nothing, and without --report-url it never runs.

Freshness checks that run in this repository’s own CI, against api.github.com. Nothing here sits on a path an adopter runs — no CLI command reaches these modules.

ModulePrimitiveDestinationWhy
packages/core/src/op/emulator-freshness.tsfetchapi.github.com release metadataReports how far behind an emulator’s pinned image is. Advisory, never gating, and reached only from scripts/check-emulator-freshness.ts — no CLI command calls it.
packages/core/src/workspace/conformance/broker.tsfetchthe broker under test, at the base URL its start(env) returnsThe broker conformance suite (#3164) makes each check’s request to the broker a maintainer points it at, as a box would. No CLI command or build, lint or apply path imports it; only a broker’s own test run does, and chant’s own test dials a loopback stand-in.
scripts/dogwood-freshness.tsfetchapi.github.com commit and tree metadataCompares the committed dogwood pin against upstream for scripts/check-dogwood-freshness.ts. The transport is injectable, which is why the comparison half is unit-tested without a network.
scripts/prior-art-sweep.tsfetchthe rule indexes of the tools the audit rules credit: raw.githubusercontent.com, api.github.com directory listings, and a few tools’ own docs sites (checkov.io, PSRule for Azure, the Bicep linter page)Diffs each credited tool’s published rule list against scripts/prior-art/snapshot.json so a rule that appears or disappears upstream, or a credit whose upstream id no longer resolves, is reported. Run by the monthly prior-art-sweep workflow and by hand; never by a CLI command or a test.

67 modules across packages/, lexicons/, scripts/, ops/ call a network primitive directly. Every other module that reaches the network does so through one of them, or through a child process listed on this page.

The tables above leave out how chant and its outputs arrive on a machine.

npm install reaches a registry, like any Node project. A private mirror or an offline cache serves chant the same way it serves anything else. The install path has no chant-specific endpoint.

An emitted pipeline references container images and, on GitHub and Forgejo, uses: action refs. Those are fetched by the CI runner, not by chant, at a time chant is not running. The forgejo lexicon resolves uses: against a configurable mirror base for exactly this reason, so a self-hosted Forgejo with a local action mirror emits refs that resolve inside the perimeter.

Container images that chant itself produces travel in the build archive, and the archive’s load-image-on-host backend delivers them to a host without a registry in between.

SurfaceHow to cut it
CodegenDo not run chant dev generate. The generated types and the committed spec snapshot in each lexicon are what a build reads; a lexicon is fully usable on a machine that never fetches a spec.
Kubernetes CRDsCRD YAML is baked in at generation time, so chant build on a project using third-party CRDs stays offline. See Add a Third-Party CRD.
Helm chart rendersWarm the render cache on a networked machine before going offline: run chant build once, and ~/.chant/helm-renders/ carries every later build. A HelmRender pointed at a local chart path instead of a repo needs no fetch at all.
Action referencesPoint the forgejo lexicon at a mirror base, and uses: refs resolve against it instead of a public forge.
Image deliveryUse the build archive’s load-image-on-host publish backend instead of a registry.
A whole substrateRun against an emulator. chant emulator up needs the image once; everything after it is loopback.
Build-ledger referrersNothing to cut. Referrer lookup defaults to noopReferrerLookup, which reaches nothing; the oras-backed lookup is opt-in, because a query that was network-free should not silently start reaching a registry.

test/egress-catalogue.ts holds the catalogue as data. test/no-egress.e2e.test.ts checks it from three sides on every run:

  • A scan of packages/, lexicons/, scripts/ and ops/ finds every module naming a network primitive, and it must equal the catalogue exactly. An added fetch fails, and so does a stale row for a call that no longer exists.
  • Each row declares which primitives it reaches for, and the scan must agree.
  • The generated block above must match what the catalogue renders, byte for byte. Refresh it with npm run generate:egress-catalogue.

A prose audit is wrong after the next merge. The test suite checks this page on every run.

It is not a security boundary against project code you do not trust.

The guard is a regression gate over chant’s own source, run by chant’s own CI, and every mechanism in it is defeatable by code that wants to defeat it. A module can import its own copy of node:http, or shell out. That is the same argument Sandboxed Execution makes for why --sandbox does not ship a bootstrap-time fetch patch and does not claim to block egress: against a hostile file, a userland patch stops nothing.

The two are answering different questions. The sandbox asks whether a repository chant does not own can reach out from inside a build; the answer is yes, and the deployment guidance there (a container with no route out, a network namespace, a seatbelt profile) closes it. This page asks whether chant itself reaches out on the paths you run every day; the answer is no, and a test fails the day that stops being true.

chant