---
title: Lease networking
description: What a lease may reach out to, what may reach in, and the short-lived tokens that open the data plane.
type: how-to
product: sandboxes
summary: Egress policy, exposed ports and lease tokens
updated: 2026-09-28
order: 3
---

A lease has two network questions — what the guest may reach, and what may
reach the guest — and one answer to both: an explicit policy on the lease,
plus short-lived tokens for the data plane.

## Egress policy

`spec.network` is the outbound policy, and its default is permissive:

<PropertyTable
	properties={[
		{
			name: 'egress',
			type: 'EgressPolicy',
			description: '`allow`, `deny` or `allowlist`. Default ALLOW: the guest reaches the internet.',
		},
		{
			name: 'allowed_domains',
			type: 'string[]',
			description: 'The hosts reachable when `egress` is ALLOWLIST: exact names, or one leading `*.` wildcard label, so `api.openai.com` and `*.github.com` are both valid.',
		},
		{
			name: 'allowed_cidrs',
			type: 'string[]',
			description: 'The public CIDRs reachable when `egress` is ALLOWLIST.',
		},
	]}
/>

An allowlist is written in names where names are what you know: `*.github.com`
covers the subdomains of one label, and both halves are still evaluated by the
Cell rather than by a helper in the guest.

`status.network` reports the policy the lease is actually running under, and
it is the field to read when a connection fails rather than the field you
wrote. It carries `blocked_cidrs` as well — the ranges that are blocked under
every policy, including `allow`. A host that resolves into one of them is
unreachable by design, not by misconfiguration.

## Change the policy on a running lease

`set_network` replaces the outbound policy of a live lease:

```bash
sylphx sandboxes leases set-network orgs/acme/projects/shop/envs/production/leases/lease
```

```bash
curl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/leases/lease:setNetwork" \
  -H "Authorization: Bearer $SYLPHX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"network":{}}'
```

```ts
const response = await sylphx.sandboxes.leases.setNetwork({
  name: 'orgs/acme/projects/shop/envs/production/leases/lease',
  network: {},
})
```

The replacement is whole: the policy you send is the policy the lease has, so
a field you leave out goes back to its default rather than staying as it was.
Tighten a lease for one step and widen it for the next without recreating the
machine, and read the result back from `status.network`.

Two things about it are deliberate. The lease's generation does not change,
so a stream or an action in flight is not invalidated by a network change.
And the Cell applies the new policy before the call returns, which means a
successful `set_network` is a statement about the machine rather than a
request that will be honoured shortly. The change is recorded as a
`network_changed` event on the lease's stream.

## Expose a guest port

A guest port is exposed either when the lease is created or later:

<PropertyTable
	properties={[
		{
			name: 'port',
			type: 'int32',
			description: 'The guest port to expose. Required.',
		},
		{
			name: 'visibility',
			type: 'PortVisibility',
			description: 'Default PRIVATE: reaching it takes a lease token or an Access key holding `sandboxes:exec`. A `public` port takes neither.',
		},
	]}
/>

`spec.ports` sets them at grant, and `open_port` and `close_port` change them
on a running lease — both need `sandboxes:exec` rather than
`sandboxes:write`, because a port is data plane:

```bash
sylphx sandboxes leases open-port orgs/acme/projects/shop/envs/production/leases/lease
```

```bash
curl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/leases/lease:openPort" \
  -H "Authorization: Bearer $SYLPHX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"port":{"port":1}}'
```

```ts
const response = await sylphx.sandboxes.leases.openPort({
  name: 'orgs/acme/projects/shop/envs/production/leases/lease',
  port: { port: 1 },
})
```

The opened port comes back in `status` with its own `uri`, which is the
address to hand to whatever consumes it. Closing is the port number alone:

```bash
sylphx sandboxes leases close-port orgs/acme/projects/shop/envs/production/leases/lease
```

A server inside the lease that should be reachable only by your own code wants
the default visibility and a token. A server that should be reachable by
anyone wants `public`, and wants it knowingly: a public port is a listener on
the internet, and the lease's own isolation is the only other thing in front
of it.

## Lease tokens

A lease token is the key to the data plane. It is minted per caller and per
purpose, so the whole point is that it is narrow:

```bash
sylphx sandboxes leases mint-token orgs/acme/projects/shop/envs/production/leases/lease \
  --scopes guest
```

```bash
curl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/leases/lease:mintToken" \
  -H "Authorization: Bearer $SYLPHX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scopes":["guest"]}'
```

```ts
const response = await sylphx.sandboxes.leases.mintToken({
  name: 'orgs/acme/projects/shop/envs/production/leases/lease',
  scopes: ['guest'],
})
```

`scopes` is required, and each one admits a different door:

- `guest` — the guest daemon at `status.endpoints.guest_uri`: streaming
  `exec` and PTYs, and the guest's own `/files`.
- `ports` — the `uri` of a private port.
- `cdp` — the Chrome DevTools Protocol of a browser lease, at
  `status.endpoints.cdp_uri`.
- `computer` — computer use: actions and the live stream.

`ttl` bounds the token: at most 1 hour, and never past the lease's own
`expire_time`. The token answers with the `generation` it is bound to, which
is the lease's generation — so a token minted before a pause or a resume does
not open the machine that comes after it. It is returned once and never
again, and `expire_time` says when it stops verifying.

The shape of the whole thing: the API key or Access principal mints tokens,
the tokens are what the machine's own callers carry, and the lease's end
takes them all with it.

<RelatedDocs
	links={[
		{
			href: '/docs/api/leases',
			label: 'The leases collection',
			description: 'Every method, its scope and its examples.',
		},
		{
			href: '/docs/cli/leases',
			label: 'The CLI reference',
			description: 'Every verb and flag of sylphx sandboxes leases.',
		},
		{
			href: '/docs/platform/keys-and-scopes',
			label: 'Keys and scopes',
			description: 'The scopes a key holds, and what each one admits.',
		},
		{
			href: '/docs/sandboxes/computer-use',
			label: 'Computer use',
			description: 'Streams, control and the tokens that open them.',
		},
	]}
/>
