Port Forwarding & Auto-Expose
Scion’s port forwarding feature allows users, developers, and integrations to access HTTP services running inside agent containers—such as web servers, developer APIs, or debugging interfaces—securely through the Hub’s reverse proxy.
Instead of opening container ports to the public internet or managing complex VPNs/ingress rules, Scion establishes a secure WebSocket-based reverse tunnel between the agent container and the Hub, routing incoming traffic on-demand.
How It Works
Section titled “How It Works”Port forwarding in Scion relies on a reverse tunnel architecture:
- Tunnel Establishment: When an agent container boots,
sciontool(the in-container agent helper) establishes a persistent WebSocket connection to the Hub’s tunnel registration endpoint:This connection is authenticated using the agent’s uniqueGET /api/v1/agents/{agentID}/ports/tunnelX-Scion-Agent-Token. - Port Registration: A port must be registered with the Hub before it can receive forwarded traffic. Ports can be registered manually via the CLI/API or automatically detected via Auto-Expose.
- Request Proxying: When a user or system sends an HTTP request to the Hub proxy URL for a specific agent port, the Hub encapsulates the request headers, method, path, query parameters, and body into a multiplexed control message.
- Local Forwarding: The Hub sends this message over the WebSocket tunnel to the agent’s in-container tunnel manager. The manager unwraps the request and makes a standard HTTP request to the local loopback address (
127.0.0.1orlocalhost) on the specified port. - Response Delivery: The local service’s response is streamed back over the WebSocket tunnel, reconstructed by the Hub, and returned to the caller.
URL Patterns & Proxy Endpoints
Section titled “URL Patterns & Proxy Endpoints”Every exposed port on an agent is allocated a dedicated base path under the Hub’s API. You can access the service using the following pattern:
https://<hub-url>/api/v1/agents/<agent-id>/ports/<port>/proxy/<subpath>For example, if your Hub is hosted at hub.scion.local, the agent ID is agent-abc-123, and you want to access a service listening on port 8000, the base proxy URL is:
https://hub.scion.local/api/v1/agents/agent-abc-123/ports/8000/proxy/Any subpath or query parameters appended to the proxy path are forwarded faithfully to the container. For instance, accessing:
https://hub.scion.local/api/v1/agents/agent-abc-123/ports/8000/proxy/api/v1/metrics?raw=true
will forward a local request to http://127.0.0.1:8000/api/v1/metrics?raw=true.
Manual Port Management
Section titled “Manual Port Management”You can manually manage exposed ports using the sciontool CLI from inside the agent container, or via the Hub API.
Exposing a Port
Section titled “Exposing a Port”To expose a local port manually, run sciontool expose with the target port:
sciontool expose 8080 --label "my-web-app"Options:
--label: An optional label to identify the service.--host: The local host to forward to (defaults to127.0.0.1). Target hosts are strictly validated and must resolve to a loopback address in this revision.
Unexposing a Port
Section titled “Unexposing a Port”To stop exposing a port, run:
sciontool unexpose 8080This operation is idempotent; if the port is not currently exposed, the command completes successfully and silently.
Listing Exposed Ports
Section titled “Listing Exposed Ports”To list all currently exposed ports for the agent, run:
sciontool expose --listThis outputs the current port number, label, and full proxy URL.
Auto-Expose Ports
Section titled “Auto-Expose Ports”Scion includes an Auto-Expose engine that can automatically detect listening TCP services inside the agent container and register them with the Hub without manual developer intervention.
How Auto-Detection Works
Section titled “How Auto-Detection Works”- Procfs Scanning: The in-container agent helper (
sciontool) runs a periodic reconciliation loop (by default every 3 seconds) that reads/proc/net/tcpand/proc/net/tcp6in pure Go. - State Filtering: It identifies all sockets in the
TCP_LISTENstate (hex code0A), resolving their local IP addresses and port numbers. - Policy Evaluation: The scanned ports are filtered against configured minimums, allowlists, or denylists.
- Hub Registration: Any eligible newly discovered ports are registered with the Hub using the label
auto-scan(allowing operators and users to distinguish them from manually registered ports). - Reconciliation & Unexposure: If a port was auto-exposed but the service subsequently stops listening (is no longer found in
procfsscans),sciontoolautomatically deregisters and unexposes the port from the Hub. - System Notifications: When a port is auto-exposed, the reconciler optionally sends a platform event message to the agent channel with category
system_category: agent:port:forward, generating a notification. This notification suggests sharing the proxy URL with collaborating users so they can collaborate on the exposed service.
Configuring Auto-Expose on the Agent
Section titled “Configuring Auto-Expose on the Agent”Auto-expose behavior inside the agent container is governed by several environment variables:
| Environment Variable | Default Value | Description |
|---|---|---|
SCION_AUTO_EXPOSE_PORTS |
false |
Set to true or 1 to enable the auto-expose scanner loop. |
SCION_AUTO_EXPOSE_INTERVAL |
3s |
The scanning and reconciliation cycle interval (minimum 1s). |
SCION_AUTO_EXPOSE_MODE |
allowlist |
The filtering strategy. Allowed values: allowlist or denylist. |
SCION_AUTO_EXPOSE_PORTS_LIST |
empty | A comma-separated list of ports. In allowlist mode, only these ports are exposed. In denylist mode, these ports are excluded. |
SCION_AUTO_EXPOSE_MIN_PORT |
1024 |
The minimum port number eligible for auto-exposure (prevents exposing privileged system ports). |
Example Allowlist Configuration:
SCION_AUTO_EXPOSE_PORTS=trueSCION_AUTO_EXPOSE_MODE=allowlistSCION_AUTO_EXPOSE_PORTS_LIST=8000,8080,3000Settings & Administration
Section titled “Settings & Administration”Administrators can control whether port forwarding and auto-expose are permitted at both the global Hub level and individual Project level.
Which Setting Wins
Section titled “Which Setting Wins”SCION_AUTO_EXPOSE_PORTS can come from four places. Highest first:
- The agent itself: the agent-create request’s
config.env, or the auto-expose control on the agent’s configure page. This is recorded as the agent’s explicit choice and survives reincarnate. - The project annotation
scion.io/auto-expose-ports-enabled(see below). - Template or harness-config env.
- The Hub default
auto_expose_ports.enabled(see below).
A lower source applies only when no higher one sets the variable. The Hub default is not stored on the agent: it is sent on every start, so changing it affects every agent that inherits it at that agent’s next start. A value set with scion hub env set outranks template and harness-config env but loses to the agent and project settings. An agent started from the scion CLI directly on a broker host, rather than through the Hub, does not receive the Hub default. See Settings Precedence for details.
Global Server Configuration
Section titled “Global Server Configuration”The Hub-wide default, the lowest tier above, is configured via settings.yaml under the operational (Layer-1) settings hierarchy:
auto_expose_ports: enabled: true- File Mode: Editable directly in the global config file.
- Database Mode: Managed via the Hub Admin Settings API (
PUT /api/v1/admin/server-config) or UI. - Seeds: Seeded initially from
auto_expose_ports.enabledinsettings.yaml. NoSCION_SEED_*variable maps to this key.
Project-Level Overrides
Section titled “Project-Level Overrides”Project owners and admins can control the auto-expose feature for all agents within a specific project using project annotations:
# Project Settings / Metadata Annotationsscion.io/auto-expose-ports-enabled: "true"When set, the Hub gives every agent in that Project SCION_AUTO_EXPOSE_PORTS with the annotation’s value. It overrides template, harness-config and Hub-default values, but not a value set on the agent itself. The value is applied when the agent is created and re-read when it is reincarnated.
Precedence
Section titled “Precedence”The SCION_AUTO_EXPOSE_* variables resolve the same way when an agent is created and when its configuration is changed later (PATCH): a value set explicitly on the agent wins, then the project or template value, then the Hub default. A configuration change that does not mention the auto-expose keys leaves the explicit value alone and re-derives the inherited one.
In the web UI, the agent Configure page shows the effective auto-expose value and where it comes from (set on the agent, project or template, or Hub default), and saves the auto-expose keys only when you change the control. On the create page, the control starts at the Hub default; if you leave it untouched, the agent inherits the project, template, and Hub default values.
Security & Network Isolation
Section titled “Security & Network Isolation”Port forwarding is designed with multi-tenant security in mind, utilizing strict host validation, access control lists, and infrastructure port protections.
1. Target Validation & Host Isolation
Section titled “1. Target Validation & Host Isolation”- Loopback Enforcement: Forwarding is strictly limited to services bound to the container’s loopback interface (
127.0.0.1,localhost,::1). Requests targeting external or network-routable IPs are rejected with an error. - Max Port Limit: Each agent container is permitted a maximum of 10 simultaneously exposed ports. Requests to expose additional ports are rejected with a validation error.
2. Infrastructure Reserved Ports (Denylist)
Section titled “2. Infrastructure Reserved Ports (Denylist)”Certain infrastructure and internal control-plane ports are reserved for system services and are permanently banned from manual exposure or auto-exposure:
| Port | Reserved Service | Reason |
|---|---|---|
9810 |
Scion Hub API | Protects the Hub’s local control API from external exposure. |
18380 |
Scion Metadata Server | Protects the agent’s internal instance identity metadata server. |
Note: Port 8080 is intentionally permitted. Because the reverse tunnel operates over an established connection, the agent-side port never collides with the Hub’s HTTP listeners, making it safe to expose user services on 8080.
3. Authentication & Authorization
Section titled “3. Authentication & Authorization”Access to the proxy and port registration APIs requires authentication, verified under the unified permission policy:
- Managing Ports (Register/Delete):
- Only the agent container itself (authenticating with its token containing
ScopeAgentPortForward) or a global Hub Admin user is authorized to register or delete exposed ports.
- Only the agent container itself (authenticating with its token containing
- Accessing Proxied Ports:
- An agent can only access its own port registrations.
- A user must be authenticated and must hold the
ActionPortAccess(orActionRead) permission for that specific agent. Unauthorized users are blocked with an HTTP403 Forbiddenresponse. - When authenticating with a user access token, the
token must have the
agent:port_accessscope selected, and the holder must currently have access to that specific agent — your own agents and their descendants, or any agent in a project where your role grantsagent.port_access(the built-inproject-ownerandproject-adminroles do). Selecting the scope at mint time is not by itself access to any agent; it is re-checked on every request.
Browser vs. Non-Browser Error Handling
Section titled “Browser vs. Non-Browser Error Handling”When an unexposed port is accessed, or when the agent container’s reverse tunnel is temporarily offline, the Hub reverse proxy performs smart content negotiation based on the request’s Accept header:
Browser Requests (Accept: text/html)
Section titled “Browser Requests (Accept: text/html)”If the request is initiated from a web browser, the proxy returns a friendly, self-contained HTML error page containing troubleshooting guidance:
- Port Not Exposed: Returns an
HTTP 404 Not Foundpage stating that the requested port has not been registered on this agent. - Tunnel Offline: Returns an
HTTP 503 Service Unavailablepage indicating that the reverse tunnel is disconnected, and the agent may not be running or is starting up.
API / Programmatic Requests (Other headers)
Section titled “API / Programmatic Requests (Other headers)”For CLI, script, or webhook requests, the proxy bypasses the HTML template and returns standard, machine-readable JSON error responses:
{ "code": "runtime_error", "message": "No active port-forward tunnel for this agent"}This ensures that API integrations are easy to write and parse, while humans accessing the endpoint in a browser get clear visual diagnostics.