API Reference

Network

Configure network policies for your workspace - internet access, inbound/outbound firewall rules, private links between workspaces, and dedicated outbound IPs.

Get network

Get the current network configuration.

const network = await ws.network.get('ws_a1b2c3d4');
GET /workspace/:workspaceId/network
curl "https://api.oblien.com/workspace/ws_a1b2c3d4/network" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "ip": "10.0.0.15",
  "gateway": "10.0.0.1",
  "public_access": false,
  "allow_internet": true,
  "ingress": [],
  "ingress_ports": [],
  "egress": [],
  "egress_ports": [],
  "outbound_ip": null,
  "outbound_mode": "managed",
  "outbound_proxy": null,
  "private_links": []
}

Response fields

FieldTypeDescription
ipstringWorkspace private IP address (use this for direct connections)
gatewaystringNetwork gateway
public_accessbooleanWhether public inbound traffic is allowed (required for gateway access)
allow_internetbooleanWhether outbound internet is allowed
ingressstring[]Allowed inbound source IPs (managed automatically via private links)
ingress_portsnumber[]Allowed inbound ports
egressstring[]Allowed outbound hosts/CIDRs
egress_portsnumber[]Allowed outbound ports
outbound_ipstring|nullDedicated outbound IP if assigned
outbound_mode"managed"|"custom""managed" (default) routes through the pool IP; "custom" routes through your own proxy. See Custom outbound proxy
outbound_proxyobject|nullPresent only when outbound_mode === "custom". Shape: { host, port, protocol, has_credentials } — the password itself is never returned
private_linksobject[]Linked workspaces with resolved IPs (see Private Links)

The ip field is the workspace's internal network address. You'll need this when making direct workspace-to-workspace calls. You can also get it from the raw token endpoint, which returns both the IP and the connection token in one call.


Update network

Partially update network policy. Only the fields you provide are changed.

await ws.network.update('ws_a1b2c3d4', {
  allow_internet: true,
  ingress_ports: [80, 443, 3000],
  egress: ['api.github.com', 'registry.npmjs.org'],
  public_access: false,
});
PATCH /workspace/:workspaceId/network
{
  "allow_internet": true,
  "ingress_ports": [80, 443, 3000],
  "egress": ["api.github.com", "registry.npmjs.org"],
  "public_access": false
}
curl -X PATCH "https://api.oblien.com/workspace/ws_a1b2c3d4/network" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "allow_internet": true,
    "ingress_ports": [80, 443, 3000],
    "egress": ["api.github.com"]
  }'

Parameters

All fields are optional:

ParameterTypeDescription
allow_internetbooleanEnable/disable outbound internet. Setting to false clears egress
ingress_portsnumber[]Allowed inbound ports (1–65535, max 50 entries)
egressstring[]Allowed outbound hosts/CIDRs (max 50 entries, printable ASCII)
public_accessbooleanAllow public inbound traffic (required for gateway access)
private_link_idsstring[]Workspace IDs to link - resolved to private IPs and added to firewall ingress (see Private Links)
outbound_mode"managed"|"custom"Set to "custom" to route through your own proxy (requires custom_proxy), "managed" to revert to the pool IP. See Custom outbound proxy
custom_proxyobjectRequired when outbound_mode: "custom". See Custom outbound proxy for shape

Validation rules

  • Port numbers must be between 1 and 65535
  • Maximum 50 entries per list (ingress, egress, ingress_ports)
  • Hosts must be printable ASCII characters
  • Setting allow_internet: false forces egress: []
  • public_access: true adds the host keyword to the ingress list

Private links allow workspace-to-workspace communication over the internal network. By default, every workspace is network-dark - no inbound traffic is allowed from any source, including other workspaces in the same account. Private links explicitly whitelist specific workspaces.

When you set private_link_ids on a workspace, the platform resolves each workspace ID to its internal IP and adds it to the target's firewall ingress. The linked workspaces can then reach the target over the private 10.x.x.x network.

// Allow ws_caller to reach ws_target
await ws.network.update('ws_target', {
  private_link_ids: ['ws_caller'],
});
// Allow multiple workspaces
await ws.network.update('ws_target', {
  private_link_ids: ['ws_caller_1', 'ws_caller_2', 'ws_orchestrator'],
});
PATCH /workspace/:workspaceId/network
{
  "private_link_ids": ["ws_caller_1", "ws_caller_2"]
}
curl -X PATCH "https://api.oblien.com/workspace/ws_target/network" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "private_link_ids": ["ws_caller_1", "ws_caller_2"] }'

How it works

  1. You provide workspace IDs (not raw IPs) in private_link_ids
  2. The platform resolves each ID to its internal 10.x.x.x IP
  3. Resolved IPs are added to the target workspace's firewall ingress list
  4. The public_access state is preserved - private links don't affect gateway access
  5. Only workspaces that successfully resolve to an IP are linked

Response

The update returns the resolved link IDs:

{
  "success": true,
  "private_link_ids": ["ws_caller_1", "ws_caller_2"]
}

Viewing linked workspaces

The GET /network response includes resolved private link details:

{
  "private_links": [
    {
      "id": "ws_caller_1",
      "ip": "10.40.0.5",
      "name": "my-agent",
      "image_logo": "node",
      "image_color": "#3C873A"
    },
    {
      "id": "ws_caller_2",
      "ip": "10.40.0.12",
      "name": "orchestrator",
      "image_logo": "python",
      "image_color": "#306998"
    }
  ]
}

Set private_link_ids to an empty array to remove all links:

await ws.network.update('ws_target', {
  private_link_ids: [],
});

Private links are one-directional. Linking ws_caller → ws_target allows the caller to reach the target, but the target cannot reach the caller unless you also create a link in the other direction.

Private links are required for direct workspace-to-workspace access to the Runtime API. Without a link, the target's firewall blocks the connection even if the caller knows the IP and has a valid token.


List available outbound IPs

Get all Zones IPs in the pool that can be assigned to a workspace as a dedicated outbound IP.

GET /zone/mine
curl "https://api.oblien.com/zone/mine" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "proxies": [
    { "ip": "203.0.113.50", "country_code": "US" },
    { "ip": "198.51.100.12", "country_code": "DE" }
  ]
}

Use any ip value from this list when calling Apply outbound IP below.


Apply outbound IP

Assign a dedicated outbound IP address to the workspace. All outbound traffic will route through this IP. Useful for whitelisting in third-party firewalls.

POST /workspace/:workspaceId/network/ip
{
  "ip": "203.0.113.50"
}
curl -X POST "https://api.oblien.com/workspace/ws_a1b2c3d4/network/ip" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "ip": "203.0.113.50" }'

Parameters

ParameterTypeRequiredDescription
ipstringYesOutbound IP from GET /zone/mine

Response

{
  "success": true,
  "ip": "203.0.113.50",
  "country_code": "US"
}

Errors

CodeStatusCause
ip_required400ip field missing from request body
invalid_zone_ip400IP is not in the available pool
zone_missing_host422Zone configuration is incomplete
pool_empty503Egress zone temporarily unavailable

Custom outbound proxy (BYOP)

Bring your own HTTP/HTTPS proxy and route this workspace's outbound traffic through it. Use this when you need outbound traffic to exit from an IP you control — for SOC2 audits, IP allowlists managed by a third party, or carrier-grade proxy providers.

Custom outbound proxy is a Scale plan feature. Workspaces on Free, Hobby, or Pro plans will receive a plan_required (HTTP 402) error. Upgrade in the billing dashboard to enable it.

How it works

When you set a custom proxy, the platform installs nftables DNAT rules on the host so that every outbound TCP connection from the workspace is redirected to your proxy. The workspace doesn't need to be proxy-aware — there is no HTTPS_PROXY env var to set, no library configuration, no SDK changes inside the workspace. The redirection is transparent at the kernel level.

Before applying, the platform runs a live CONNECT oblien.com:443 preflight against your proxy. If the preflight fails (unreachable, auth rejected, wrong protocol), the request is rejected and your previous outbound configuration is preserved — bad proxies cannot break outbound traffic.

Set custom proxy

await ws.network.setCustomProxy('ws_a1b2c3d4', {
  host: '203.0.113.10',
  port: 3128,
  protocol: 'http',
  username: 'my-user',
  password: 'my-pass',
});

Or use the generic update if you want to combine BYOP with other network changes:

await ws.network.update('ws_a1b2c3d4', {
  outbound_mode: 'custom',
  custom_proxy: {
    host: '203.0.113.10',
    port: 3128,
    protocol: 'http',
  },
  ingress_ports: [443],
});
PATCH /workspace/:workspaceId/network
{
  "outbound_mode": "custom",
  "custom_proxy": {
    "host": "203.0.113.10",
    "port": 3128,
    "protocol": "http",
    "username": "my-user",
    "password": "my-pass"
  }
}
curl -X PATCH "https://api.oblien.com/workspace/ws_a1b2c3d4/network" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "outbound_mode": "custom",
    "custom_proxy": {
      "host": "203.0.113.10",
      "port": 3128,
      "protocol": "http",
      "username": "my-user",
      "password": "my-pass"
    }
  }'

Clear custom proxy (revert to managed)

await ws.network.clearCustomProxy('ws_a1b2c3d4');
PATCH /workspace/:workspaceId/network
{ "outbound_mode": "managed" }
curl -X PATCH "https://api.oblien.com/workspace/ws_a1b2c3d4/network" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "outbound_mode": "managed" }'

Parameters

ParameterTypeRequiredDescription
hoststringYesProxy IPv4 address. Hostnames are not accepted — pre-resolve to an IP. Reserved/private ranges (RFC1918, loopback, link-local, multicast, etc.) are rejected
portnumberYesProxy port, 1–65535
protocol"http"|"https"NoProxy protocol. Defaults to "http". Both speak HTTP CONNECT"https" adds TLS to the workspace↔proxy hop
usernamestringNoUsername for Proxy-Authorization. If set, password is required
passwordstringNoPassword for Proxy-Authorization. Required if username is set. Printable ASCII only, no : @ / ? or whitespace

Reading back the proxy

GET /workspace/:workspaceId/network returns the proxy as outbound_proxy:

{
  "outbound_mode": "custom",
  "outbound_proxy": {
    "host": "203.0.113.10",
    "port": 3128,
    "protocol": "http",
    "has_credentials": true
  }
}

The password itself is never returned — only has_credentials: true|false so the dashboard can show a "saved" indicator without leaking the secret.

Errors

CodeStatusCause
plan_required402Workspace owner is not on the Scale (or Enterprise) plan
missing_custom_proxy400outbound_mode: "custom" was sent without a custom_proxy object
invalid_outbound_mode400outbound_mode was not "custom" or "managed"
invalid_proxy_host400host is not a valid IPv4
reserved_proxy_host400host is in a reserved/private range (e.g. 10.0.0.0/8, 192.168.0.0/16)
invalid_proxy_port400port is not an integer in 1–65535
invalid_proxy_protocol400protocol is not "http" or "https"
invalid_proxy_credentials400Credentials contain forbidden characters or username was set without password
preflight_protocol_mismatch400The proxy responded but does not speak the protocol you specified
preflight_auth_required401The proxy rejected your credentials
preflight_bad_status502The proxy returned a non-2xx status to CONNECT
preflight_unreachable502The proxy address is unreachable from the host
preflight_timeout504The proxy did not respond within the preflight window
preflight_closed502The proxy closed the connection without responding

Security notes

  • The host is validated against a list of 16 reserved IPv4 ranges before any kernel state is touched. You cannot point the proxy at the host's loopback or internal network.
  • Credentials are sent to your proxy as a base64-encoded Proxy-Authorization: Basic … header. The base64 alphabet (A-Za-z0-9+/=) means credentials cannot inject extra HTTP headers regardless of what characters you put in them — but we still reject :, @, /, ?, and whitespace in the validator for defense in depth.
  • nftables rules are built with typed netlink structs (host IP becomes 4 raw bytes, port becomes a uint16). There is no string-rendered rule path, so a malicious-looking IP cannot inject extra nftables statements.
  • When the workspace is deleted, the per-VM nftables chain and cached credentials are torn down as part of normal VM cleanup.

Learn more about networking concepts in Concepts: Networking.