Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/superfly/sprites-go/llms.txt

Use this file to discover all available pages before exploring further.

Port forwarding lets your local machine communicate directly with services running inside a Sprite over a transparent TCP tunnel. The SDK establishes a local listener and proxies each connection through a WebSocket transport to the Sprite, so you can use any standard networking tool against localhost while the actual service runs remotely.

Forward a single port

ProxyPort creates one forwarding session. Pass the context, the local port to bind, and the remote port on the Sprite.
session, err := sprite.ProxyPort(ctx, 3000, 3000)
if err != nil {
    log.Fatal(err)
}
defer session.Close()

// localhost:3000 now proxies to the Sprite's port 3000
The returned *ProxySession keeps the listener alive until you call Close() or the parent context is cancelled.

Forward multiple ports

ProxyPorts accepts a slice of PortMapping values and returns a slice of sessions in the same order. If any mapping fails, all sessions created so far are cleaned up before the error is returned.
sessions, err := sprite.ProxyPorts(ctx, []sprites.PortMapping{
    {LocalPort: 3000, RemotePort: 3000},
    {LocalPort: 8080, RemotePort: 80},
    {LocalPort: 5432, RemotePort: 5432},
})
if err != nil {
    log.Fatal(err)
}
defer func() {
    for _, s := range sessions {
        s.Close()
    }
}()

PortMapping struct

PortMapping describes one local-to-remote binding:
type PortMapping struct {
    LocalPort  int
    RemotePort int
    RemoteHost string // Optional: defaults to "localhost" inside the Sprite
}
Set RemoteHost when the target service listens on a specific IP address or hostname inside the Sprite’s network, for example "10.0.0.1" or "fdf::1".

Managing a ProxySession

A *ProxySession exposes three methods for lifecycle control:
MethodDescription
Close() errorStops accepting new connections and closes the local listener
Wait()Blocks until the session is closed
LocalAddr() net.AddrReturns the bound local address
session, err := sprite.ProxyPort(ctx, 8080, 80)
if err != nil {
    log.Fatal(err)
}

// Inspect the actual bound address (useful when LocalPort is 0)
fmt.Println("Listening on", session.LocalAddr())

// Block the current goroutine until the session ends
session.Wait()
Pass 0 as LocalPort to let the OS assign a free port. Use session.LocalAddr() afterwards to find the assigned port.

Auto-forwarding with port notifications

When you run a command inside a Sprite, you can set a TextMessageHandler on the Cmd to receive PortNotificationMessage events whenever a process opens or closes a port. Use this to automatically start and stop forwarding sessions.
import (
    "encoding/json"
    "sync"
)

var (
    proxies = make(map[int]*sprites.ProxySession)
    mu      sync.Mutex
)

cmd := sprite.Command("npm", "start")

cmd.TextMessageHandler = func(data []byte) {
    var notification sprites.PortNotificationMessage
    if err := json.Unmarshal(data, &notification); err != nil {
        return
    }

    switch notification.Type {
    case "port_opened":
        fmt.Printf("Port %d opened on %s (PID %d)\n",
            notification.Port, notification.Address, notification.PID)

        session, err := sprite.ProxyPorts(ctx, []sprites.PortMapping{
            {
                LocalPort:  notification.Port,
                RemotePort: notification.Port,
                RemoteHost: notification.Address,
            },
        })
        if err != nil {
            log.Printf("Failed to create proxy for port %d: %v", notification.Port, err)
            return
        }

        mu.Lock()
        proxies[notification.Port] = session[0]
        mu.Unlock()

        fmt.Printf("Forwarding localhost:%d -> %s:%d\n",
            notification.Port, notification.Address, notification.Port)

    case "port_closed":
        fmt.Printf("Port %d closed (PID %d)\n", notification.Port, notification.PID)

        mu.Lock()
        if session, ok := proxies[notification.Port]; ok {
            session.Close()
            delete(proxies, notification.Port)
        }
        mu.Unlock()
    }
}

err := cmd.Run()

// Clean up any sessions still open after the command exits
mu.Lock()
for port, session := range proxies {
    session.Close()
    delete(proxies, port)
}
mu.Unlock()

PortNotificationMessage fields

type PortNotificationMessage struct {
    Type    string // "port_opened" or "port_closed"
    Port    int    // Port number
    Address string // Bind address, e.g. "127.0.0.1" or "0.0.0.0"
    PID     int    // Process ID that opened or closed the port
}
Use notification.Address as the RemoteHost in your PortMapping. This ensures the proxy connects to the exact address the process is listening on, which matters when a service binds to a non-loopback interface.

Open a raw connection

ProxySocket returns a net.Conn instead of a listener. Use this when you need a single direct connection to an arbitrary address inside the Sprite rather than a persistent forwarding session.
conn, err := sprite.ProxySocket(ctx, "tcp", "localhost:6379")
if err != nil {
    log.Fatal(err)
}
defer conn.Close()

// conn is a standard net.Conn — read and write as usual
The only supported network type is "tcp". Passing any other value returns an error.

Managing multiple sessions with ProxyManager

ProxyManager collects sessions so you can close or wait on all of them together without tracking the slice yourself.
manager := sprites.NewProxyManager()

session1, err := sprite.ProxyPort(ctx, 3000, 3000)
if err != nil {
    log.Fatal(err)
}
manager.AddSession(session1)

session2, err := sprite.ProxyPort(ctx, 5432, 5432)
if err != nil {
    log.Fatal(err)
}
manager.AddSession(session2)

// Later, close everything at once
manager.CloseAll()

// Or block until all sessions are done
manager.WaitAll()
MethodDescription
NewProxyManager()Creates an empty manager
AddSession(session)Registers a session with the manager
CloseAll()Closes every registered session
WaitAll()Blocks until every registered session has closed

Transport details

All proxy connections use the WebSocket protocol. When the Sprite supports the control feature, the SDK reuses a pool of persistent WebSocket connections to reduce connection setup overhead. Older Sprites fall back to opening a new WebSocket per TCP connection.
Because connections are tunneled over WebSocket, raw TCP keepalive settings on your local socket do not propagate to the remote side.

Build docs developers (and LLMs) love