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.

Services are named, long-running processes managed by the Sprite runtime. Unlike ad-hoc commands run with sprite.Command, a service has a persistent definition — including the command, arguments, HTTP port, and startup dependencies — that survives restarts and can be controlled independently. The SDK provides functions to create, start, stop, signal, and stream logs from services.

Service concepts

A service definition is described by ServiceRequest:
type ServiceRequest struct {
    Cmd      string   // The executable to run
    Args     []string // Arguments passed to the executable
    Needs    []string // Names of other services that must be running first
    HTTPPort *int     // Port to monitor for HTTP readiness (optional)
}
At runtime, the Sprite tracks each service’s state in ServiceState:
type ServiceState struct {
    Name          string
    Status        string    // "stopped", "starting", "running", "stopping", "failed"
    PID           int
    StartedAt     time.Time
    Error         string
    RestartCount  int
    NextRestartAt time.Time
}
GetService and ListServices return ServiceWithState, which embeds both the definition and the runtime state.

Listing services

ListServices returns all services defined on the Sprite along with their current runtime state.
services, err := sprite.ListServices(ctx)
if err != nil {
    log.Fatal(err)
}

for _, svc := range services {
    status := "unknown"
    if svc.State != nil {
        status = svc.State.Status
    }
    fmt.Printf("%s: %s\n", svc.Name, status)
}

Getting a specific service

svc, err := sprite.GetService(ctx, "web")
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Status: %s, PID: %d\n", svc.State.Status, svc.State.PID)

Creating a service

CreateService registers the service definition on the Sprite and returns a *ServiceStream that emits log events while the server processes the request. Call ProcessAll or Next to drain the stream — you must consume or close the stream before it is garbage-collected.
port := 8080
stream, err := sprite.CreateService(ctx, "web", &sprites.ServiceRequest{
    Cmd:      "node",
    Args:     []string{"server.js"},
    HTTPPort: &port,
})
if err != nil {
    log.Fatal(err)
}

err = stream.ProcessAll(func(event *sprites.ServiceLogEvent) error {
    fmt.Printf("[%s] %s\n", event.Type, event.Data)
    return nil
})
if err != nil {
    log.Fatal(err)
}
Calling CreateService a second time with the same name updates the service definition. The service must not be running when you update it.

Service with dependencies

Use Needs to declare that one service must be running before another starts. The Sprite runtime starts dependencies in the correct order.
stream, err := sprite.CreateService(ctx, "worker", &sprites.ServiceRequest{
    Cmd:   "/app/worker",
    Args:  []string{"--queue", "default"},
    Needs: []string{"redis", "postgres"},
})

Starting a service

StartService starts the named service and returns a *ServiceStream. Stream events flow until the service is running (or fails to start).
stream, err := sprite.StartService(ctx, "web")
if err != nil {
    log.Fatal(err)
}
defer stream.Close()

err = stream.ProcessAll(func(event *sprites.ServiceLogEvent) error {
    switch event.Type {
    case "stdout", "stderr":
        fmt.Print(event.Data)
    case "started":
        fmt.Println("Service is running")
    case "error":
        return fmt.Errorf("service error: %s", event.Data)
    }
    return nil
})

Stopping a service

StopService sends a stop signal to the service and returns a stream of events until the process exits.
stream, err := sprite.StopService(ctx, "web")
if err != nil {
    log.Fatal(err)
}

err = stream.ProcessAll(func(event *sprites.ServiceLogEvent) error {
    fmt.Printf("[%s] %s\n", event.Type, event.Data)
    return nil
})

Sending a signal

SignalService sends a POSIX signal to a running service by name. Pass the signal as a string, for example "HUP" or "USR1". Valid signal names match those used by cmd.Signal: INT, TERM, HUP, KILL, QUIT, USR1, USR2.
err := sprite.SignalService(ctx, "web", "HUP")
if err != nil {
    log.Fatal(err)
}
SignalService returns an error if the service is not running. Check the service state with GetService first if the running status is uncertain.

Deleting a service

DeleteService removes the service definition from the Sprite. The service must be stopped first.
err := sprite.DeleteService(ctx, "web")
if err != nil {
    log.Fatal(err)
}

Working with ServiceStream

ServiceStream is returned by CreateService, StartService, and StopService. It delivers a sequence of ServiceLogEvent values parsed from the server’s newline-delimited JSON stream.

Reading events one at a time

for {
    event, err := stream.Next()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("type=%s data=%q\n", event.Type, event.Data)
}
stream.Close()

Processing all events with a handler

ProcessAll calls your handler for every event and closes the stream when it returns.
err := stream.ProcessAll(func(event *sprites.ServiceLogEvent) error {
    if event.Type == "exit" && event.ExitCode != nil {
        fmt.Printf("Process exited with code %d\n", *event.ExitCode)
    }
    return nil
})
Returning a non-nil error from the handler stops processing and propagates the error from ProcessAll.

ServiceLogEvent fields

type ServiceLogEvent struct {
    Type      string            // See event types below
    Data      string            // Log line content (for stdout/stderr/error)
    ExitCode  *int              // Set when Type is "exit"
    Timestamp int64             // Unix timestamp in milliseconds
    LogFiles  map[string]string // Paths to persisted log files (when present)
}
Event types:
TypeWhen emitted
stdoutA line written to the process’s standard output
stderrA line written to the process’s standard error
startedThe service process has started successfully
stoppingA stop request has been sent to the process
stoppedThe process has stopped cleanly
exitThe process exited; check ExitCode
errorAn error occurred in the service runtime
completeThe stream is finished; no more events will follow

Complete example: create, start, and stream logs

The following example registers a web service, starts it, and streams its output until it is running.
package main

import (
    "context"
    "fmt"
    "io"
    "log"

    sprites "github.com/superfly/sprites-go"
)

func main() {
    client := sprites.New("your-auth-token")
    sprite := client.Sprite("my-sprite")
    ctx := context.Background()

    // Create the service definition
    port := 8080
    createStream, err := sprite.CreateService(ctx, "web", &sprites.ServiceRequest{
        Cmd:      "node",
        Args:     []string{"server.js"},
        HTTPPort: &port,
    })
    if err != nil {
        log.Fatal("create:", err)
    }
    if err := createStream.ProcessAll(func(e *sprites.ServiceLogEvent) error {
        fmt.Printf("create [%s] %s\n", e.Type, e.Data)
        return nil
    }); err != nil {
        log.Fatal("create stream:", err)
    }

    // Start the service and stream logs
    startStream, err := sprite.StartService(ctx, "web")
    if err != nil {
        log.Fatal("start:", err)
    }

    for {
        event, err := startStream.Next()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal("stream:", err)
        }

        switch event.Type {
        case "stdout":
            fmt.Print("[out] ", event.Data)
        case "stderr":
            fmt.Print("[err] ", event.Data)
        case "started":
            fmt.Println("Service is running")
        case "error":
            log.Fatal("service error:", event.Data)
        }
    }
    startStream.Close()

    // Check the running state
    svc, err := sprite.GetService(ctx, "web")
    if err != nil {
        log.Fatal("get:", err)
    }
    fmt.Printf("PID %d, restarts: %d\n", svc.State.PID, svc.State.RestartCount)
}

Build docs developers (and LLMs) love