Elizon Docs
Deployment

Network egress

Where Elizon may send requests, the policy host allowlist, and how to make a Docker Compose deployment default-deny.

Requests Elizon sends out on behalf of an Agent, a workflow or a Connection are checked twice (the exceptions are listed under Not covered by the allowlist yet):

  1. The deployment floor. The same for every Organization on the deployment, and configured by the operator. Private, loopback, link-local and metadata addresses and the platform's own service names (postgres, redis, …) are refused unless the operator opened the private network with EGRESS_ALLOW_PRIVATE_HOSTS=true. The address is checked after DNS and again on every redirect hop.
  2. The policy allowlist. Per Organization, Project, Agent, Team or Workflow: the hosts a call may reach (egress.allowed_hosts in a policy's rules). A host that is not listed is refused by name, before it is resolved, with the reason policy_egress_denied.

The allowlist decides where a call may go. What a call may carry is a separate rule: data rules on outbound workflow nodes. For policies as a whole, see Policies.

The host allowlist

An entry is one of:

EntryMatches
api.example.comThat host only
*.example.comEvery subdomain of example.com, at any depth. Not example.com itself (list it as well), and never evil-example.com
203.0.113.7That IPv4 address
[2001:db8::1]That IPv6 address (in brackets)

A URL, a path, a port, a bare * or a wildcard anywhere but the first label is refused when the policy is saved. Matching ignores case and a trailing dot.

An empty list means no allowlist: only the deployment floor applies. When several policies apply (the Organization's, the Project's and the ones attached to the Agent or Workflow), their lists are intersected: a host must be allowed by every one of them. Narrowing a parent's *.example.com to api.example.com is a restriction and is accepted; adding a host the parent does not cover is refused at save. Two lists that share no host leave nothing allowed, never everything.

A policy in monitor mode refuses nothing: a host it would refuse is reached, and the would-be refusal is written to the policy decision log.

Where it is enforced

PathChecked
Workflow nodes: HTTP Request, FTP / SFTP, Send Email (SMTP), app nodes, MCP Tool, Connection callsThe run's Workflow, Project and Organization policies
Email Trigger (IMAP) and its connection test, App Trigger polls, the MCP tool refresh on the canvasThe Workflow's policies
Agent tools: MCP, Swagger and app toolsThe Agent's policies, also when it runs as a Team member or in a workflow AI Agent node (the Team's or Workflow's list does not apply)
Tool setup: MCP connect, OAuth and refresh, Swagger spec import, Connection test, the Package wizard's tool testThe Project's policies
Model providers called by ai-workers (OpenAI, Anthropic, Gemini, Mistral, Ollama, Azure, Bedrock, …)The run's policies (a Team run: the Team's; a workflow: the Workflow's), on every request before it is sent
Voice calls: speech-to-text and text-to-speech providers, and the direct Gemini modelThe voice Agent's policies, when the call is set up
Embedding calls when a knowledge base is indexed or searched (OpenAI, Gemini, Ollama and OpenAI-compatible)The knowledge base's Project policies (folding in the Organization's), on every request before it is sent

Every refusal is written to the policy decision log with boundary = egress, rule_family = egress and action = deny. A refused request is never sent: no DNS lookup, no connection. A redirect to an unlisted host is refused at that hop. A voice call whose provider is refused does not start, a run whose model provider is refused ends with policy_denied, and a knowledge base document whose embedding provider is refused shows the reason (policy_egress_denied, the host and the rule) on its status.

Not covered by the allowlist yet

These requests are checked against the deployment floor only, not the policy allowlist:

  • OAuth sign-in and token refresh for app Connections (the provider's fixed token endpoints).
  • Testing an LLM Configuration or provider key in Settings, and SIP trunk provider (Infobip) account calls.
  • Fetches by the knowledge and crawler services.

Making a Docker Compose deployment default-deny

The allowlist is enforced inside Elizon's own services. For defence in depth, a self-hosted deployment can also make the container network itself default-deny, so that a container with no business on the internet has no route to it at all.

The building block is a Docker network declared internal: true: containers attached only to it can reach each other but nothing outside. Elizon's outbound proxy, ssrf-proxy (Squid), stays the private-address floor: it refuses private, loopback and link-local destinations and passes everything else. It does not read policies; the per-Organization allowlist is enforced in-process as described above.

  1. Add an override file next to your Compose file, for example docker-compose.egress.yml. It turns the stack's own network (elizon in infra/docker/docker-compose.community.yml) into an internal one, and adds a second, ordinary network only to the services that must reach the internet or publish a port:

    networks:
      elizon:
        internal: true # no route out for anything attached only to it
      elizon-egress: {} # an ordinary bridge with a route out
    
    services:
      # The proxy itself, and the services that dial out directly.
      ssrf-proxy: { networks: [elizon, elizon-egress] }
      api-gateway: { networks: [elizon, elizon-egress] }
      infisical: { networks: [elizon, elizon-egress] }
      # Services that publish a port to the host need a non-internal network for it.
      web: { networks: [elizon, elizon-egress] }
      temporal: { networks: [elizon, elizon-egress] }
      temporal-ui: { networks: [elizon, elizon-egress] }

    Compose merges a service's networks across files, so an override can add a network to a service but cannot take one away. That is why the recipe makes the shared network itself internal instead of listing a new internal network for every service. Every service left out of the override (postgres, redis, minio, ai-workers, ai-workers-celery, marketplace-api, …) keeps only elizon and so has no route out; ai-workers still reaches model providers through ssrf-proxy (its HTTP_PROXY / HTTPS_PROXY already point there). If your own Compose file has other services that dial out (a voice worker, a knowledge or crawler service, LiveKit), add elizon-egress to them the same way.

  2. Start with both files (your Compose file first): docker compose -f docker-compose.community.yml -f docker-compose.egress.yml up -d.

  3. Check it. From ai-workers, a request that bypasses the proxy must fail, and one through it must succeed:

    docker compose exec ai-workers python -c "import httpx; httpx.get('https://example.com', proxy=None, trust_env=False)"   # fails: no route
    docker compose exec ai-workers python -c "import httpx; print(httpx.get('https://example.com').status_code)"            # 200, via ssrf-proxy

Why some services keep a route out:

  • api-gateway sends tool, workflow-node and Connection requests itself. Its egress guard resolves each host once and connects to the vetted address, so it does not go through an HTTP proxy; the floor and the policy allowlist are applied in-process.
  • voice-agent-worker streams audio to the speech providers and media to LiveKit; WebSocket and media traffic does not fit an HTTP proxy. Its provider hosts are checked against the allowlist when a call is set up.
  • infisical reaches its own upstream services when configured to.
  • knowledge-service and crawler-service (where deployed) fetch the documents and sites you add to a knowledge base.
  • livekit and its egress recorder carry call media.

Squid's configuration (infra/docker/squid/squid.conf) is unchanged by any of this: it remains a private-range denylist, not an allowlist.

Kubernetes

Default-deny for a Kubernetes deployment (NetworkPolicy) is out of scope for this release; it belongs with the Kubernetes deployment work. The policy allowlist applies on every deployment, whatever the network does.