> ## Documentation Index
> Fetch the complete documentation index at: https://docs.genesiscomputing.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Outpost Security

> Who can manage and use Genesis Outposts, what an Outpost will and will not reach, how it signs in, and what Genesis stores.

An Outpost gives Genesis a path into your network, so it is built to reach only what you
allow and nothing else. Three layers decide what Genesis can reach, and the last one runs on
your side.

## Who can do what

| Layer | Who decides | What it controls |
| - | - | - |
| **Manage** | Genesis administrators | Creating, changing and removing Outposts, and turning the feature on or off. |
| **Route** | Administrators, plus roles granted per Outpost | Choosing an Outpost for a connection, or using an Outpost secret reference. Enforced by the Genesis backend on every connection create, update and test of unsaved settings. |
| **Reach** | You, on the Outpost | The Outpost's own `ALLOWED_DESTINATIONS`. Whatever Genesis asks, the Outpost reaches only these `host:port` entries. |

To let a role route connections through an Outpost, click **Edit** on its card on
**Config -> Outposts** and check the role under **Who can route connections through it**.
Admins always can. Every grant and every routing change is audited.

The route layer applies when routing is *set or changed*. Using a connection someone already
routed follows the normal connection permissions for people and agents.

## Routed hosts inside Genesis

Genesis reaches a routed host through a listener for the Outpost's tunnel, on a loopback
address in `127.77.0.0/16`, and makes the host's name resolve to that address in the Genesis
server. That is how routed connections work without driver changes, TLS certificate checks
against the real host name included.

Connections routed through the Outpost are the intended way to reach a routed host. The
parts of Genesis that agents drive directly hold no Outpost grant, and Genesis refuses them
routed hosts where they make their requests:

* **Agent web tools** (fetching a URL, API calls, webhooks, logo downloads, and the redirects
  they follow) are refused a routed host name and a tunnel address. The error names the
  Outpost and says to use a connection to the system instead.
* **Bulk downloads** are refused a routed host name, a tunnel address, and any name that
  resolves to a tunnel address, redirects included.
* **The agent browser** does not load a page, resource or WebSocket from a routed host name, a
  tunnel address, or a name that resolves to one. A redirect from another site is not checked.
* **Python run by agents** (`execute_python_code`) cannot open a socket to a tunnel address,
  or to a name that resolves to one.
* **On a Databricks App,** the processes Genesis starts for agents (MCP servers, coding-agent
  CLIs, dashboards) start without the host-name resolver, so routed names do not resolve in
  them. The Twin lineage server keeps it: it scans the connections it is given, routed ones
  included.

Name resolution alone does not decide who reaches a routed host: on self-hosted Genesis and
on Snowflake, routed names are written to `/etc/hosts`, so they resolve for every process in
the container, and a process that opens its own sockets can dial a tunnel address directly.
Agent web tools do not look a name up, so a public name that points into `127.77.0.0/16` is
not caught by them. The tunnel listeners themselves are guarded, as described next.

## Who can use a tunnel listener

Genesis's tunnel worker, the Genesis process that holds the tunnel listeners, checks every
connection to a tunnel listener before it forwards anything to the Outpost. It asks the operating system's kernel which account opened the
connection and, where it needs to, which processes hold it:

* **Snowflake, Docker and Kubernetes.** Genesis runs as `root`, and the code agents run
  (Python, flow steps, coding tools) runs as the restricted `cortex_sandbox` account. The
  tunnel worker forwards a connection only from a socket `root` owns. A connection from
  `cortex_sandbox`, or from any other account, is refused.
* **Databricks App.** Genesis and its code sandbox share one account, and the sandbox runs
  agent code in its own user namespace. The tunnel worker forwards a connection only when
  every process holding it is in Genesis's own user namespace. A connection held by a
  sandboxed process, or by no process the worker can see, is refused.

Genesis's own processes are admitted: the server and its workers, Twin lineage scans and dbt
runs. Anything the worker cannot decide is refused. A Databricks App that runs with a fixed
instance count runs in a container that rewrites the source address of loopback traffic;
the worker recognizes that rewrite and still identifies the connecting process. When the
worker starts, it checks that it can identify a connection of its own, and it does not start
if it cannot (Genesis's log then has an `admission_failed` line with the reason). Each
connection costs one kernel lookup; on a Databricks App, connections that arrive together also
share one scan of the process table. When more than 256 connections are waiting to be
checked, a new one is refused (`busy`).

A refused connection is closed before anything reaches the Outpost. Genesis's log records it
at WARNING as an `outpost tunnel worker` line whose `event` is `admission_refused` and whose
text reads `admission: refused a connection to <listener> from <peer> (uid <n>): <reason>: <detail>`, at most once a minute for the same listener, account and reason. The worker's start
line (`"event":"started"`, also at WARNING) names the admission mode: `uid`, `userns`, or
`off`.

The check is controlled by
[`outpost.enforce_tunnel_admission`](/docs/Setup/Advanced-Settings#outpost-routing). Turning
it off forwards every connection to a tunnel listener. A change takes effect within about
15 seconds and restarts the tunnel worker, which drops open tunnel connections; Outposts
reconnect on their own. If the check cannot work where Genesis runs, the tunnel worker does
not start and its log says why.

### What the check does not cover

The check tells Genesis's own processes apart from sandboxed ones; it cannot tell which code
runs inside a Genesis process. Agent-driven work that runs in Genesis's own account, outside
the sandbox, is admitted like the rest of Genesis:

* **Snowflake, Docker and Kubernetes:** MCP servers Genesis starts, agent git operations, the
  `sqlite3` command line and the Chromium browser run as `root`. Skill shell steps,
  dashboard rendering, the Spark worker and local Talend jobs run as `cortex_sandbox`, so
  their connections to a tunnel listener are refused.
* **Databricks App:** MCP servers Genesis starts outside the sandbox, the Chromium browser,
  and coding agents an administrator has turned on.

Only hosts that are already routed can be reached through a tunnel listener, and the Outpost
adds no authentication of its own: a destination asks for credentials only if it is
configured to. Keep `ALLOWED_DESTINATIONS` to the exact `host:port` entries you intend
Genesis to reach.

## What the Outpost enforces

The Outpost checks every connection at the moment it opens it, default-deny:

* **Only allowed destinations.** A route is held only if its destination is inside
  `ALLOWED_DESTINATIONS`. Destinations outside it are reported to Genesis as refused.
* **Only the route's own destination.** Each route can reach exactly one `host:port`. A
  request on that route for any other host or port is rejected before anything is looked up
  or dialed.
* **Cloud metadata and link-local addresses are always blocked,** even if listed in
  `ALLOWED_DESTINATIONS`: `169.254.0.0/16` (the AWS, Azure, GCP and OCI instance metadata
  services and container credential endpoints), `fe80::/10`, the AWS IPv6 metadata
  addresses, Alibaba's `100.100.100.200`, Azure's `168.63.129.16`, `0.0.0.0/8`, unspecified
  and multicast addresses, and names such as `metadata.google.internal`. The Outpost checks
  the name and every address it resolves to, then connects to the checked address itself, so
  DNS cannot answer differently at connect time. This applies to what Genesis asks the
  Outpost to reach; the Outpost's own sign-in to your secret store (which may read the
  instance metadata service for its managed identity or service account) is not a route and
  keeps working.
* **No general proxy.** The tunnel carries only TCP connections that Genesis opens toward a
  route the Outpost itself declared. Genesis cannot name any other destination, and the
  protocol has no way for the Outpost to open a connection toward Genesis or into it. There
  is no SOCKS, UDP or forward mode.
* **No command execution.** The Outpost answers a small set of typed requests (health,
  routes, route checks, secret reads). It runs no commands and no code from Genesis.
* **Bounded.** At most 256 concurrent connections per route; see
  [Tunnel limits](#tunnel-limits) for the limits Genesis applies.

Route checks from the Outposts page and from Eve go through the same checks.

## How the Outpost signs in

The Outpost connects out over HTTPS (port 443) and must sign in before a tunnel exists. You
open no inbound port. It signs in twice, and both must succeed:

1. **At the platform's front door**, with its own platform identity, before the connection
   reaches Genesis:
   * **Databricks App:** the Outpost's own service principal with CAN USE on the Genesis App
     and nothing else. The Outpost mints a short-lived token before every connection.
   * **Self-hosted Genesis:** none required; the tunnel key below authenticates it. Your
     ingress may add its own controls.
   * **Snowflake Native App** (release 1.19.97 or later): a dedicated Snowflake service user with a
     key pair and no password. Its role holds only the `APP_OUTPOST_TUNNEL` application role,
     which allows nothing but connecting to the tunnel endpoint. The generated SQL also pins
     the user to your Outpost's egress IP with a network policy, so a copied key does not
     work from elsewhere. The private key is generated where the Outpost runs and never
     leaves your network.
2. **Inside the tunnel, with its own tunnel key.** The connection is one WebSocket to
   `/outpost-tunnel` using the Genesis-native tunnel protocol, `genesis-outpost.v1`. Genesis
   sends a fresh random challenge; the Outpost answers with a proof made with its key over
   that challenge, its own random challenge and its slot; Genesis checks it, then answers
   with a proof of its own that the Outpost checks before it carries anything. The key itself
   never crosses the network, so a recorded sign-in cannot be replayed. Genesis refuses a
   sign-in that does not finish within 10 seconds.

**Every Outpost has its own tunnel key** (Outpost 0.7.0 or later). Genesis issues it, keeps it
in its secret store and shows it once. The key signs in only that Outpost, and Genesis holds
for it only listeners on that Outpost's own loopback range (`127.77.<slot>.x`), never on a port
Genesis itself serves: it cannot open another Outpost's addresses, nor use the tunnel to reach
anything inside Genesis. One session per Outpost is active at a time; a newer sign-in for the
same slot ends the older one. Each answer from an Outpost to a control request also carries a
proof made with that key over a fresh per-request challenge and the request, and Genesis does
not use the answer without a valid proof. An Outpost that has not been issued a key cannot
sign in, and Genesis sends it nothing.

Each Outpost's identity and key are its own, so you can rotate or revoke one without
touching the others. See [Rotate, revoke or remove](/docs/Setup/Outposts-Install#rotate-revoke-or-remove).

## Tunnel limits

Over its one WebSocket, each Outpost's tunnel carries many connections at once as separate
streams, each with its own flow control, so one large transfer does not hold up the others.
Genesis applies these limits:

| Limit | Value |
| - | - |
| Routes per Outpost | 254 |
| Open connections per route | 256 |
| Open connections per Outpost | 2,048 |
| New connections per route | 50 a second, after a burst of 200; a connection past that waits up to 2 seconds for its turn, then is refused (`rate_limited`) |
| Outposts signing in at once | 64, each with 10 seconds to finish |
| Keepalive | a ping every 20 seconds; two unanswered pings end the session |
| Listener kept after a disconnect | 30 seconds, so a quick reconnect keeps the Outpost's routes |

A connection refused by one of these limits is closed at once; see
[Troubleshooting](/docs/Setup/Outposts-Troubleshooting#a-connection-through-the-outpost-is-closed-at-once).

## What Genesis stores

* **The Outpost definition:** its name, slot, optional domain patterns and route grants.
* **Connections, as usual.** A connection routed through an Outpost records which Outpost it
  uses.
* **References, not secret values,** for credentials kept in your store. A connection stores
  `outpost_acme-prod/genesis/postgres-prod#password`, not the password. When Genesis
  connects, the Outpost reads the value from your secret store with its own identity and
  hands it to Genesis, which holds it in memory only, for a few minutes, and never writes it
  to its database. The Outpost reads only secrets whose names start with its
  `ALLOWED_SECRET_PREFIX`, and only when its `secrets` capability is on. It reads from the
  one store you configure (AWS Secrets Manager, Azure Key Vault or Google Secret Manager)
  under its own cloud identity, which needs read access only: `secretsmanager:GetSecretValue`,
  the **Key Vault Secrets User** role, or the **Secret Manager Secret Accessor** role. Scope
  that grant to the same prefix (`genesis/` in AWS, `genesis--` in Azure and Google) or to
  the individual secrets.

Genesis never holds the Outpost's private key, service-principal secret or cloud
credentials.

## Audit

* **In Genesis:** route grants and every change to what a connection routes through an
  Outpost are recorded in the Genesis audit log.
* **On the Outpost:** every route change, connection admitted or refused, route check and
  secret read is written to the Outpost's log as an audit record
  (`"logger":"genesis_outpost.audit"`, JSON lines in production). Collect it with your usual
  log pipeline.

## Recommendations for production

* Run the Outpost on a VM, Docker or Kubernetes, not a laptop.
* Keep `ALLOWED_DESTINATIONS` to the exact `host:port` entries Genesis needs.
* Keep the Snowflake network-policy pin, and store the private key in a secret store or a
  Kubernetes Secret.
* Run a separate Outpost per network or trust zone rather than one Outpost that can reach
  everything.
* Grant routing to roles only on the Outposts they need.
