Docs navigation
Get started
AI agents
Connect
Operate
Day to day
When it breaks
Govern access
Team & account
Access
Identity concepts
Provider guides
Account
Network requirements
The domains, ports, and protocols runner hosts and workstations need to reach.
What has to be reachable#
Use these rows to build outbound rules for runner hosts and workstations. "Your emisar
origin" is the control plane you connect to — on the hosted control plane, https://emisar.dev.
| Operation and component | Direction | Destination and port | Protocol | Why | When required |
|---|---|---|---|---|---|
| Runner registration | Outbound | Your emisar origin, TCP 443 | HTTPS POST /runner/register | Exchanges the enrollment key for this runner's own token | First connect, and again after an enrollment-key change |
| Runner token refresh | Outbound | Your emisar origin, TCP 443 | HTTPS POST /runner/token/refresh | Swaps the runner's token for a successor before the old one expires | On the next connect after its token is 60 days old |
| Runner control connection | Outbound | Your emisar origin, TCP 443 | WSS /runner/socket/websocket | Carries every dispatch, result, and heartbeat on one connection | Continuously, for as long as the runner runs |
| Installer release download (runner and bridge) | Outbound | Your emisar origin, TCP 443 | HTTPS | Downloads the release manifest, tarball, checksums, and signed checksum bundle from /releases | Only while install.sh or install-mcp.sh runs |
| Release checksum trust roots | Outbound | tuf-repo-cdn.sigstore.dev and tuf-repo.github.com, TCP 443 | HTTPS | Lets GitHub CLI authenticate the downloaded checksum bundle against public trust roots | During install and upgrade when the verifier cache is empty or refreshes |
| GitHub release mirror fallback | Outbound | api.github.com, github.com, and release-assets.githubusercontent.com, TCP 443 | HTTPS | Provides the optional secondary release source | Only when the Emisar release path is unavailable or EMISAR_REPO points at another repository |
| Pack install and update | Outbound | Your emisar origin and registry.emisar.dev, TCP 443, or your configured artifact hosts | HTTPS | Fetches the immutable pack tarball named by the registry index | Only while emisar pack install or pack update runs |
| Action and provider traffic | Outbound | Whatever that pack's command talks to, on the provider's own port | The provider's own protocol | The action does its work. This traffic never passes through emisar. | While that action runs |
| MCP bridge | Outbound | Your emisar origin, TCP 443 | HTTPS POST /api/mcp/rpc | One request per JSON-RPC frame from the LLM client | While an LLM client session is active |
| LLM client to bridge | Local, no network | The bridge process on the same machine | stdio pipes | The client starts the bridge as a child process | Whenever the client is running |
| Name resolution | Outbound | Your configured resolver | DNS | Resolves the emisar, registry, provider, and optional GitHub fallback hosts | On connect, reconnect, install, and update |
| Clock synchronization | Outbound | Whatever time source the host already uses, commonly UDP 123 | NTP | Certificate validity — and signed-dispatch freshness — is judged against this clock | Continuously |
The runner's control connection#
A runner opens one long-lived outbound connection. On first connect it exchanges its
enrollment key for a token (POST /runner/register), then upgrades to a
WebSocket at /runner/socket/websocket, sending the token as a bearer
during the upgrade. Every dispatch, result, and heartbeat rides that one connection.
-
—
TLS 1.2 or newer, validated against the host trust store.
Loopback HTTP is accepted without a flag. The runner rejects cleartext HTTP and
WebSocket connections to non-loopback hosts unless
cloud.allow_insecureis set — that sends runner credentials without TLS, so use it only on an isolated development network. -
—
Redirects are not followed.
Registration and the socket upgrade both stop at a
3xxrather than chase the bearer token to whatever host answered. A captive portal or a proxy that answers with a redirect therefore fails closed instead of leaking a credential. -
—
The runner reconnects on its own.
After a drop it retries with backoff and advertises again. A connection timeout often
means outbound port 443 is filtered — use
emisar doctorto check that the control plane is reachable.
Installing and upgrading binaries#
Both installers download releases from
https://emisar.dev/releases
over
emisar.dev:443
and verify the selected archive
against its checksum. When GitHub CLI with
gh attestation verify --bundle
is installed, they also authenticate the signed checksum metadata. That optional check needs outbound HTTPS to
tuf-repo-cdn.sigstore.dev:443
and tuf-repo.github.com:443, but no GitHub login.
To permit the optional GitHub release fallback, also allow outbound HTTPS to api.github.com:443, github.com:443, and release-assets.githubusercontent.com:443.
An unavailable primary artifact may be fetched from that fallback. A malformed manifest,
invalid signature, or checksum mismatch stops the install instead of switching sources.
A custom
EMISAR_REPO
uses GitHub directly. For a disconnected host, first save the trusted roots on a connected
staging machine:
$ gh attestation trusted-root > trusted_root.jsonl
Transfer that file with the selected archive, combined checksum, and
*.sigstore.jsonl
bundle. Pass it to the manual bundle check as --custom-trusted-root trusted_root.jsonl, verify the
exact archive entry, then install the binary manually. The installers do not accept a
custom trusted-root path. The
Linux host
page covers the surrounding setup, and
Upgrade runners
page owns the upgrade path.
The pack registry#
emisar pack install
and
emisar pack update
fetch from the registry configured by
--registry
or EMISAR_PACKS_REGISTRY,
defaulting to https://emisar.dev.
By default, a pack fetch redirects to the immutable tarball on registry.emisar.dev. The runner
follows only HTTPS redirects and rejects a cleartext downgrade. Allowlist both hosts.
These commands look packs up by name, and the public emisar origin serves those
lookups. A static tree built with packctl
has a different layout — install from its exact immutable tarball URL instead of passing
that host to --registry. Running one is covered in Host your own registry.
The MCP bridge#
The LLM client starts the bridge as a child process and talks to it over stdin and
stdout — no port, no socket. The bridge sends one HTTPS
POST
to
/api/mcp/rpc
per JSON-RPC frame, with no server-sent events, WebSocket, or open network session.
- — Requests can take a while on purpose. A wait can hold a request for up to a minute on the control plane, and the bridge allows 90 seconds before it gives up. An intermediary that cuts idle HTTP requests shorter than that will break waits while ordinary calls keep working — a confusing failure worth ruling out early.
-
—
It refuses to put your key on the wire in the clear.
Loopback HTTP is accepted without an override; a cleartext
http://URL to any other host is rejected unless insecure transport is explicitly enabled — that sends the key without TLS and is only for an isolated development network. Redirects are refused for the same reason. - — Cloud clients need nothing from you. Claude.ai and ChatGPT reach the hosted control plane themselves over OAuth. No bridge runs on your machines for those connections, so there is no egress rule to write.
Provider and action traffic#
A pack action runs a real command on the host, and that command talks to its own systems
— Nomad, a database, a cloud API — directly. The traffic never passes through emisar, so
build each pack's allowlist from the pack itself:
emisar pack info <id>
lists the binaries it needs and the environment variables it authenticates with, and
their values carry the endpoints.
Proxies and TLS inspection#
The runner and the bridge use the platform's standard HTTP client, with no emisar-specific proxy, CA bundle, certificate pinning, or client certificate settings.
-
—
Proxy variables come from the process environment.
HTTP_PROXY,HTTPS_PROXY, andNO_PROXYapply. On a systemd host, put them in/etc/emisar/runner.env. For the bridge, define them in the LLM client environment. -
—
Every intermediary must pass the Upgrade header and keep the connection open.
The runner uses a long-lived WebSocket. An appliance that strips the
Upgradeheader, buffers frames, or closes idle connections makes the runner reconnect over and over — often with no clear error to find. - — TLS interception is a host trust-store question. If you terminate and re-sign TLS, the host has to trust the interception CA the same way it trusts any CA. emisar needs no configuration for this, and accepts only certificates the host trust store validates.
DNS and the clock#
A runner resolves the emisar origin on every connection, not only at boot. It resolves the registry during pack installs and GitHub only when the release fallback is used. Split-horizon DNS can send some hosts to an unreachable internal address. This often explains why one host works and another fails.
Keep the clock synchronized. TLS judges certificate validity against the host clock, and signed dispatch checks signature freshness against it too — so one skewed host can refuse actions while the rest of the fleet works.
Check your egress rules#
The product's own commands are the test — each fails in a recognizable place when a rule is missing:
# on the runner host — config, credential, packs, action binaries, reachability $ sudo emisar doctor # on the workstation that runs your LLM client $ emisar-mcp --version
The egress path is open when all of these are true:
-
—
emisar doctorpasses — the host side works. The runner appears online in the console — the WebSocket connection made it through. - — That runner advertises the packs and actions you installed. If a pack never downloaded, fix the registry rule.
- — If the host runs a pack that talks to a provider, one low-risk read-only action from it succeeds. Provider traffic leaves your host directly, so a real action is the end-to-end check for that leg.
-
—
emisar-mcp --versionworks on the workstation — the binary is installed. One LLM discovery call lists runners or finds an action — the bridge can reach the control plane.