Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
281 changes: 70 additions & 211 deletions src/content/docs/concepts/networking.mdx
Original file line number Diff line number Diff line change
@@ -1,266 +1,125 @@
---
title: Networking
description: HTTP access, URLs, port forwarding, and network configuration
draft: true
description: How traffic reaches a Sprite and how a Sprite reaches out. The URL and proxy that get you in, and the egress policy that governs what goes out
publishedDate: 2026-07-08
author: kcmartin
---

import { Tabs, TabItem } from '@astrojs/starlight/components';
import { Snippet, LinkCard, CardGrid } from '@/components/react';
import { Callout, LinkCard, CardGrid } from '@/components/react';

Sprites run your code in the cloud. This page is about how to talk to that code via HTTPS URLs and port forwarding that make remote services feel local. You get a public endpoint to hit your app over the Internet, and the ability to proxy ports straight to your laptop so you can test, debug, and connect tools like you're running everything on localhost.
A Sprite runs your code in the cloud, but it's meant to feel close. Traffic gets in two ways, an always-on HTTPS URL and a proxy that maps a remote port onto your laptop. A network policy governs what the Sprite can reach on its way out. This page is the model for both directions: how you reach in, and what a Sprite can reach going out. The exact commands live on the pages linked throughout.

## Sprite URLs
## Reaching a Sprite

Every Sprite has a unique URL for HTTPS access, for example:
Inbound traffic arrives one of two ways, and which you pick depends on whether you want a public endpoint or a local one.

```bash
sprite url
```

```output
https://my-sprite-abc123.sprites.app
```

If your code is listening on a port (say, 3000 or 8080), that URL routes traffic to it. This means you can:

- Access web applications running in your Sprite
- Test a dev server in the cloud
- Make API requests to services
- Connect services to each other via HTTP

### URL Authentication

By default, Sprite URLs are private and they require a valid token. You can make them public if you want to share a demo, open up a webhook, or quickly put something onto the Internet:
- **The Sprite URL** is how you reach an app over the Internet: a webhook target, a shared demo, an API. Every Sprite has one at `https://<sprite-name>-<org-id>.sprites.app/` (the org ID is a short generated identifier; `sprite info` prints your exact URL), and it routes HTTPS to the Sprite's HTTP service. It's always on: no CLI required.
- **`sprite proxy`** is how you make a Sprite feel local: it maps a remote port onto your machine, so you can point a database client or a browser at a service running in the Sprite.

<Tabs>
<TabItem label="CLI">
```bash
# Make URL public (no authentication required) - good for webhooks, public APIs, demos
sprite url update --auth public

# Require sprite authentication (default) - good for internal services, development
sprite url update --auth default
```
</TabItem>

<TabItem label="JavaScript">
```javascript
// Get sprite info including URL
const info = await client.getSprite('my-sprite');
console.log(info.url);
```
</TabItem>
</Tabs>

Updating URL settings is available via the CLI, Go SDK, or REST API (the JS SDK does not expose a helper yet).

## Port Forwarding

Here's the trick that makes Sprites feel local: port forwarding.

Now your laptop's localhost:3000 forwards to the Sprite's port 3000. You can open a browser, curl it, or connect with tools that expect a local port.
# Show the Sprite's URL and its auth setting
sprite info

Try these examples:

<Tabs>
<TabItem label="CLI">
```bash
# Forward local port 3000 to sprite port 3000
sprite proxy 3000

# Forward multiple ports
sprite proxy 3000 8080 5432

# Now access locally
curl http://localhost:3000
```
</TabItem>

<TabItem label="Go">
```go
// Forward single port
session, err := client.ProxyPort(ctx, "my-sprite", 3000, 3000)
if err != nil {
log.Fatal(err)
}
defer session.Close()

// localhost:3000 now forwards to sprite:3000
fmt.Println("Proxy active at localhost:3000")

// Forward multiple ports
sessions, err := client.ProxyPorts(ctx, "my-sprite", []sprites.PortMapping{
{LocalPort: 3000, RemotePort: 3000},
{LocalPort: 8080, RemotePort: 80},
{LocalPort: 5432, RemotePort: 5432},
})
defer func() {
for _, s := range sessions {
s.Close()
}
}()
```
</TabItem>

<TabItem label="Elixir">
```elixir
# Forward single port
{:ok, session} = Sprites.proxy_port(sprite, 3000, 3000)

# localhost:3000 now forwards to sprite:3000
IO.puts("Proxy active at localhost:3000")

# Forward multiple ports
mappings = [
%Sprites.Proxy.PortMapping{local_port: 3000, remote_port: 3000},
%Sprites.Proxy.PortMapping{local_port: 8080, remote_port: 80},
%Sprites.Proxy.PortMapping{local_port: 5432, remote_port: 5432}
]
{:ok, sessions} = Sprites.proxy_ports(sprite, mappings)

# Stop proxy when done
Sprites.Proxy.Session.stop(session)
# Map the Sprite's port 5432 onto localhost:5432 for a psql client
sprite proxy 5432
```
</TabItem>
</Tabs>

Port forwarding works for TCP services — web servers, databases, message brokers, whatever. It's just sockets.
| | Sprite URL | `sprite proxy` |
|---|---|---|
| Protocol | HTTP(S) only | Any TCP |
| Reach | Public Internet | Your machine only |
| Needs the CLI running | No | Yes |
| Ports | One (the HTTP service) | As many as you forward |

## Real-World Examples
`sprite proxy` also remaps ports (`sprite proxy 3001:3000`) and tunnels stdin and stdout for things like SSH (`sprite proxy -W :22`). And you often don't reach for it at all: when you run `sprite exec` and your command opens a listening port, the CLI forwards that port to your laptop automatically. For the full command surface and the SSH-over-proxy setup, see [Working with Sprites](/working-with-sprites#networking-urls-and-port-forwarding) and the [CLI Commands reference](/cli/commands#networking).

### Starting a Web Server
The HTTP service the URL routes to, including how it wakes on an incoming request and how to move it off the default port, is covered in [Services](/concepts/services).

Run a web server and access it via the Sprite URL:
## URL authentication

```bash
# Start a simple HTTP server
sprite exec -detachable "python -m http.server 8080"

# Get the URL
sprite url
```

```output
https://my-sprite-abc123.sprites.app
```

```bash
# Access via browser or curl (after making public)
curl https://my-sprite-abc123.sprites.app:8080/
```
A Sprite URL is private by default. It's reachable only by members of your org, through the browser or with an org token, so standing up a service doesn't put it on the open Internet by accident.

### Development Server

Let's say you've got a frontend or backend dev server that watches files and hot reloads.
Make it public when you actually want that behavior, for example, a webhook that needs to be hit without a token, a demo you're sharing, or putting something quick on the Internet:

```bash
# Start dev server in detachable session
sprite exec -detachable "cd /home/sprite/app && npm run dev"

# Forward the port locally
sprite proxy 3000
# Anyone with the URL can reach it
sprite config update --url-auth public

# Open in browser
open http://localhost:3000
# Back to org-only (the default)
sprite config update --url-auth sprite
```
If your server starts dynamically (e.g. via a watcher), Sprites can emit events when a process binds a port. You can hook into those if you want to script around startup behavior.

### Database Access

Running a database inside a Sprite is weirdly nice. You can spin up Postgres, forward its port, and connect with your usual tools:

```bash
# Start PostgreSQL (if installed)
sprite exec -detachable "pg_ctl start"

# Forward port locally
sprite proxy 5432
<Callout type="warning" title="A public URL is on the open Internet">
Public means no authentication: anyone with the URL reaches whatever the HTTP service serves. Don't make a URL public if it exposes secrets, tokens, or internal endpoints, and put your own auth in front of anything real. Flip it back to `sprite` when you're done.
</Callout>

# Connect with local tools
psql -h localhost -p 5432 -U postgres
```
## Reaching out

### Multiple Services
By default, a Sprite's outbound is unrestricted: it can resolve and reach any domain. Egress can be tightened with a **network policy**, a DNS-based allowlist that decides which domains a Sprite is allowed to reach. Applying one is opt-in and done from outside the Sprite. Once a policy is in force, a request to an allowed domain works normally, while a request to one that isn't gets a DNS `REFUSED` and fails fast rather than hanging.

Sprites can run multiple processes. You can forward all the ports you care about:
The policy is a set of rules, read-only inside the Sprite at `/.sprite/policy/network.json`:

```bash
# Start multiple services
sprite exec -detachable "cd /home/sprite/api && npm start" # Port 3000
sprite exec -detachable "cd /home/sprite/worker && npm start" # Port 3001
sprite exec -detachable "redis-server" # Port 6379

# Forward all ports
sprite proxy 3000 3001 6379
```json
{
"rules": [
{ "include": "defaults" },
{ "domain": "example.com", "action": "allow" },
{ "domain": "*.example.com", "action": "allow" },
{ "domain": "blocked.com", "action": "deny" }
]
}
```

## Network Behavior
- **`{ "include": "defaults" }`** pulls in the common development domains, GitHub, npm, PyPI, Docker Hub, and the major AI APIs among them, so package installs and model calls work without listing every host yourself.
- **Domain rules** match an exact host (`example.com`), a subdomain wildcard (`*.example.com`), or everything (`*`). More specific rules win: an exact match beats a subdomain wildcard, which beats the global wildcard.
- **`{ "rules": [] }`** means no enforcement. The Sprite runs unrestricted.

Sprites have full network access by default:
When a policy is enforced, a few behaviors follow from its DNS-based design:

- **Outbound**: All protocols and ports. You can fetch packages, call APIs and more
- **Inbound**: Only via Sprite URL or port forwarding
- **DNS**: Standard resolution works
- **Raw IP connections are blocked** unless the IP was resolved from an allowed domain. You can't route around the allowlist by dialing an address directly.
- **Private IPs are always blocked**, so a Sprite can't reach into private network ranges.
- **Changes reload live.** When a policy tightens, existing connections to newly-blocked domains are dropped rather than left open.

The default environment includes common network tools, and you can install additional ones as needed. You can run tools like `netcat`, `curl`, or `nmap` or `wget`. Nothing is artificially restricted and this isn't a locked-down environment.
The policy is read-only from inside: a Sprite can't rewrite its own egress rules. Changes are made from outside through the [Sprites API](https://sprites.dev/api). To test what the current policy allows, resolve a domain and watch for `REFUSED`:

Example network tool installation:
```bash
sprite exec "apt-get update && apt-get install -y nmap netcat"
dig github.com # an allowed domain resolves
dig blocked.com # a denied domain returns REFUSED
```

## Troubleshooting

**Not seeing your app on the URL?** Make sure it's listening on `0.0.0.0`, not `localhost`. The router can't see loopback-only services.

**Forwarded port not responding?** Check the app is actually running, and that you forwarded the right port. Use `sprite ps` to see running processes.

**Getting a 403 on your Sprite URL?** It's probably set to private. Make it public with `sprite url --public` or authenticate with a token.

**Dynamic apps not ready right away?** If your service binds ports after startup, you can use port open events from the SDK to wait for readiness.

## Security Notes
For calling external APIs without handing the Sprite a long-lived credential, [Connectors](/concepts/connectors) route the request through a gateway that holds the token for you. That's a separate mechanism from the egress policy: the policy decides what a Sprite may reach, Connectors decide what it may reach *as*.

By default, your Sprite isn't publicly accessible. That's on purpose. You control what gets exposed — either by forwarding a port or making the Sprite's URL public.
That's the shape of networking on Sprites: the URL and proxy control how you reach a Sprite, the egress policy controls how far it reaches back. You're in control of both, so it's worth opening each only as far as your work actually needs.

A few things to keep in mind:

- **Only expose what you actually need** - Run services on specific ports
- **Use app-level auth** - If you're building anything real, implement your own authentication
- **Forwarded ports are reachable from your machine** — Not the wider Internet.
- **Temporary exposure** - Make public only when needed.

We don't add firewall rules or block inbound traffic to forwarded ports, but we also don't auto-protect what you expose. You're in control, which is powerful — and dangerous, if you're not paying attention. Keep it minimal and secure.

## Related Documentation
## Related documentation

<CardGrid client:load>
<LinkCard
href="/working-with-sprites"
href="/working-with-sprites#networking-urls-and-port-forwarding"
title="Working with Sprites"
description="Beyond the basics guide to using Sprites"
description="Port forwarding, URL auth, and SSH over the proxy in practice"
icon="book-open"
client:load
/>
<LinkCard
href="/cli/commands"
title="CLI Commands"
description="Complete command reference"
icon="terminal"
href="/concepts/services"
title="Services"
description="The HTTP service the URL routes to, and waking on a request"
icon="cog"
client:load
/>
<LinkCard
href="/reference/configuration"
title="Configuration"
description="Network settings and options"
icon="settings"
href="/concepts/connectors"
title="Connectors"
description="Reach external APIs with credentials brokered for you"
icon="arrow-up-right"
client:load
/>
<LinkCard
href="/concepts/lifecycle"
title="Lifecycle"
description="Hibernation and persistence behavior"
icon="layers"
href="/cli/commands#networking"
title="CLI Commands"
description="Full reference for the URL and proxy commands"
icon="terminal"
client:load
/>
</CardGrid>
1 change: 1 addition & 0 deletions src/lib/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export const sidebarConfig: SidebarGroup[] = [
{ label: 'Checkpoints', slug: 'concepts/checkpoints' },
{ label: 'Connectors', slug: 'concepts/connectors' },
{ label: 'Lifecycle and Persistence', slug: 'concepts/lifecycle' },
{ label: 'Networking', slug: 'concepts/networking' },
{ label: 'Services', slug: 'concepts/services' },
],
},
Expand Down
1 change: 1 addition & 0 deletions styles/config/vocabularies/fly-terms/accept.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
alignItems
allowlist
Alertmanager
Ansible
Anthropic's
Expand Down
Loading