Documentation
¶
Overview ¶
Package restate holds the code shared by the Restate drivers: the Host, which serves one Restate service endpoint and talks to the Restate server.
Construction callbacks edit one fresh Builder, once in order; a supplied nil callback panics. Expanded nil or empty callback slices leave defaults unchanged. Publication copies owned settings and identity-key slices, not HTTP clients, loggers, or callback captures.
host := restate.New(func(host *restate.Builder) {
host.ClientOnly(true)
})
_ = host.Addr() // Client-only hosts never open a listener.
A module root never holds a driver. Each driver, such as arkoren.dev/driver/restate/restateschedule or arkoren.dev/driver/restate/restateworkflow, binds its services with Host.Bind before Host.Start runs.
The lifecycle has three steps. Host.Start builds the endpoint and opens the listener without serving. Host.Serve serves requests, registers the deployment when Builder.Register is set, and blocks until its context is done. Host.Stop shuts the endpoint down. Stop also releases a listener opened by Start when Serve never ran. It waits for active HTTP requests until its context expires, then closes remaining connections. Request cancellation is cooperative, not forced termination of application code. The HTTP client supplied at construction remains shared and is not closed by endpoint cleanup.
A host with Builder.ClientOnly, or with nothing bound, opens no listener and registers nothing; it only reaches the ingress and the admin API. Use it in processes that start work but do not run it.
Host.Query runs SQL against the introspection tables of the admin service. Builder.AdminAuthKey authenticates the admin calls, and Builder.RegisterForce and Builder.RegisterBreaking set the registration policy.
The endpoint verifies that each request is signed by the Restate server. Set Builder.IdentityKeys to the request-identity public keys ("publickeyv1_...") of the Restate server. A host that would listen without keys fails Host.Start with ErrNoIdentityKeys; Builder.AllowInsecure opts out for local development and tests. The server also sets ReadHeaderTimeout and IdleTimeout, but no ReadTimeout or WriteTimeout, because Restate keeps long-lived HTTP/2 streams open.
The endpoint speaks HTTP/2 without TLS (h2c) and HTTP/1.1. The Restate server must be able to reach Builder.AdvertisedURL, and the deployment must be registered, either by Builder.Register or by the operator.
Index ¶
- Variables
- type Builder
- func (host *Builder) Addr(addr string) *Builder
- func (host *Builder) AdminAuthKey(key string) *Builder
- func (host *Builder) AdminURL(url string) *Builder
- func (host *Builder) AdvertisedURL(url string) *Builder
- func (host *Builder) AllowInsecure(allow bool) *Builder
- func (host *Builder) AuthKey(key string) *Builder
- func (host *Builder) ClientOnly(clientOnly bool) *Builder
- func (host *Builder) HTTPClient(client *http.Client) *Builder
- func (host *Builder) IdentityKeys(keys ...string) *Builder
- func (host *Builder) IngressURL(url string) *Builder
- func (host *Builder) Logger(logger *slog.Logger) *Builder
- func (host *Builder) Register(register bool) *Builder
- func (host *Builder) RegisterBreaking(breaking bool) *Builder
- func (host *Builder) RegisterForce(force bool) *Builder
- type BuilderFunc
- type Config
- type Host
- func (host *Host) Addr() net.Addr
- func (host *Host) Admin(ctx context.Context, method, path string, body []byte) (*http.Response, error)
- func (host *Host) Bind(definition restatesdk.ServiceDefinition) error
- func (host *Host) Ingress() *ingress.Client
- func (host *Host) Ping(ctx context.Context) error
- func (host *Host) Query(ctx context.Context, sql string) ([]map[string]any, error)
- func (host *Host) Serve(ctx context.Context) error
- func (host *Host) Start(ctx context.Context) error
- func (host *Host) Stop(ctx context.Context) error
Constants ¶
This section is empty.
Variables ¶
var ErrClientOnly = errors.New("restate host is client only")
ErrClientOnly reports a Host.Bind on a host with Builder.ClientOnly. Such a host deliberately has no endpoint; use it for ingress/admin calls.
Example:
host := New(func(host *Builder) {
host.ClientOnly(true)
})
if err := host.Bind(restatesdk.NewService("Example")); !errors.Is(err, ErrClientOnly) {
panic(err)
}
var ErrNoIdentityKeys = errors.New("restate endpoint has no identity keys")
ErrNoIdentityKeys reports a Host.Start that would open a service endpoint with neither Builder.IdentityKeys nor Builder.AllowInsecure. Without keys anyone who reaches the listener can invoke the bound handlers.
Example:
if err := host.Start(ctx); errors.Is(err, ErrNoIdentityKeys) {
return err // Reconstruct with the server's request-identity public keys.
}
var ErrStarted = errors.New("restate host is started")
ErrStarted reports a Host.Bind or Host.Start after Host.Start. A host has one startup lifetime; construct another host rather than rebinding.
Example:
if err := host.Start(ctx); errors.Is(err, ErrStarted) {
return err
}
Functions ¶
This section is empty.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder edits the construction settings of New. Callbacks share one fresh draft and run once in order; a nil callback panics. Retaining a draft does not permit changes to the published component. Dependencies and native SDK internals retain their existing ownership.
Example:
component := New(func(host *Builder) {
host.Logger(slog.Default())
})
_ = component
func (*Builder) Addr ¶
Addr is the listen address of the service endpoint. The default is ":9080". It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.Addr(":9080")
})
_ = component
func (*Builder) AdminAuthKey ¶
AdminAuthKey is the bearer token sent to the admin API. Empty sends none. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.AdminAuthKey("example")
})
_ = component
func (*Builder) AdminURL ¶
AdminURL is the base URL of the Restate admin API. The default is "http://localhost:9070". It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.AdminURL("http://localhost:9070")
})
_ = component
func (*Builder) AdvertisedURL ¶
AdvertisedURL is the URL the Restate server uses to reach the endpoint. The default is "http://localhost" followed by Addr. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.AdvertisedURL("http://localhost:9080")
})
_ = component
func (*Builder) AllowInsecure ¶
AllowInsecure lets Host.Start open a service endpoint without IdentityKeys. Use it only for local development and tests: anyone who can reach the listener can invoke the bound handlers. The default is false. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.AllowInsecure(true)
})
_ = component
func (*Builder) AuthKey ¶
AuthKey is the bearer token sent to the ingress. Empty sends none. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.AuthKey("example")
})
_ = component
func (*Builder) ClientOnly ¶
ClientOnly makes the host a pure client of the Restate server: Host.Start opens no listener, Host.Serve registers nothing, and Host.Bind returns ErrClientOnly. A host with nothing bound behaves the same way. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.ClientOnly(true)
})
_ = component
func (*Builder) HTTPClient ¶
HTTPClient is used for ingress and admin calls. Nil uses a client with default settings. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.HTTPClient(http.DefaultClient)
})
_ = component
func (*Builder) IdentityKeys ¶
IdentityKeys are the Restate request-identity public keys ("publickeyv1_...") the endpoint accepts. The Restate server signs each request to the endpoint, and the endpoint rejects requests that are not signed by one of these keys. Take them from the Restate server configuration (request identity). Empty leaves the endpoint unauthenticated, which Host.Start refuses unless AllowInsecure is set. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.IdentityKeys("publickeyv1_example")
})
_ = component
func (*Builder) IngressURL ¶
IngressURL is the base URL of the Restate ingress. The default is "http://localhost:8080". It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.IngressURL("http://localhost:8080")
})
_ = component
func (*Builder) Logger ¶
Logger receives Host log lines. Nil uses slog.Default. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.Logger(slog.Default())
})
_ = component
func (*Builder) Register ¶
Register makes Host.Serve register the deployment with the admin API once the endpoint is serving. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.Register(true)
})
_ = component
func (*Builder) RegisterBreaking ¶
RegisterBreaking sends breaking=true with the registration, which accepts a deployment that removes services or handlers of an earlier deployment under the same URL. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.RegisterBreaking(true)
})
_ = component
func (*Builder) RegisterForce ¶
RegisterForce sends force=true with the registration, which overwrites a deployment that serves the same URL. Use it only when no invocation of the old deployment is in flight. It returns the same draft for subsequent settings.
Example:
component := New(func(host *Builder) {
host.RegisterForce(true)
})
_ = component
type BuilderFunc ¶
type BuilderFunc = func(*Builder)
BuilderFunc is an ordinary callback alias for host construction. New invokes callbacks directly once in order; nil entries panic and an empty slice omits edits. Retaining a draft does not retain mutation access to the host.
Example:
policies := []BuilderFunc{func(host *Builder) {
host.ClientOnly(true)
}}
_ = New(policies...)
type Config ¶
type Config struct {
// Addr is the listen address of the service endpoint. The default is
// ":9080".
//
// Example:
//
// fmt.Println(Config{Addr: ":9080"}.Addr)
Addr string
// IngressURL is the base URL of the Restate ingress. The default is
// "http://localhost:8080".
//
// Example:
//
// fmt.Println(Config{IngressURL: "http://localhost:8080"}.IngressURL)
IngressURL string
// AdminURL is the base URL of the Restate admin API. The default is
// "http://localhost:9070".
//
// Example:
//
// fmt.Println(Config{AdminURL: "http://localhost:9070"}.AdminURL)
AdminURL string
// AdvertisedURL is the URL the Restate server uses to reach the endpoint.
// The default is "http://localhost" followed by Addr.
//
// Example:
//
// fmt.Println(Config{AdvertisedURL: "http://localhost:9080"}.AdvertisedURL)
AdvertisedURL string
// Register makes [Host.Serve] register the deployment with the admin API
// once the endpoint is serving.
//
// Example:
//
// fmt.Println(Config{Register: true}.Register)
Register bool
// RegisterForce sends force=true with the registration, which overwrites
// a deployment that serves the same URL. Use it only when no invocation
// of the old deployment is in flight.
//
// Example:
//
// fmt.Println(Config{}.RegisterForce) // False: replacement is not forced.
RegisterForce bool
// RegisterBreaking sends breaking=true with the registration, which
// accepts a deployment that removes services or handlers of an earlier
// deployment under the same URL.
//
// Example:
//
// fmt.Println(Config{}.RegisterBreaking) // False: breaking removal is not allowed.
RegisterBreaking bool
// ClientOnly makes the host a pure client of the Restate server:
// [Host.Start] opens no listener, [Host.Serve] registers nothing, and
// [Host.Bind] returns [ErrClientOnly]. A host with nothing bound behaves
// the same way.
//
// Example:
//
// fmt.Println(Config{ClientOnly: true}.ClientOnly)
ClientOnly bool
// IdentityKeys are the Restate request-identity public keys
// ("publickeyv1_...") the endpoint accepts. The Restate server signs each
// request to the endpoint, and the endpoint rejects requests that are
// not signed by one of these keys. Take them from the Restate server
// configuration (request identity). Empty leaves the endpoint
// unauthenticated, which [Host.Start] refuses unless AllowInsecure is set.
// This reporting record does not validate native key syntax or own its slice.
//
// Example:
//
// fmt.Println(len(Config{IdentityKeys: []string{"publickeyv1_example"}}.IdentityKeys))
IdentityKeys []string
// AllowInsecure lets [Host.Start] open a service endpoint without
// IdentityKeys. Use it only for local development and tests: anyone who
// can reach the listener can invoke the bound handlers. The default is
// false.
//
// Example:
//
// fmt.Println(Config{}.AllowInsecure) // False: unsigned serving is not permitted.
AllowInsecure bool
// AuthKey is the bearer token sent to the ingress. Empty sends none.
// Ordinary Config formatting redacts it; direct field access does not.
//
// Example:
//
// slog.InfoContext(ctx, "settings", "config", Config{AuthKey: "example"})
AuthKey string
// AdminAuthKey is the bearer token sent to the admin API. Empty sends
// none.
// Ordinary Config formatting redacts it; direct field access does not.
//
// Example:
//
// slog.InfoContext(ctx, "settings", "config", Config{AdminAuthKey: "example"})
AdminAuthKey string
// HTTPClient is used for ingress and admin calls. Nil uses a client with
// default settings.
// The native client remains shared; Host.Stop does not close its transport.
//
// Example:
//
// record := Config{HTTPClient: http.DefaultClient}
// _ = record.HTTPClient
HTTPClient *http.Client
// Logger receives Host log lines. Nil uses [slog.Default].
//
// Example:
//
// record := Config{Logger: slog.Default()}
// record.Logger.InfoContext(ctx, "configured host")
Logger *slog.Logger
}
Config is a redacted reporting record for host settings, not a constructor input. Configure New through Builder. String, GoString, and LogValue redact nonempty AuthKey and AdminAuthKey fields; URL fields are unchanged.
Example:
record := Config{AuthKey: "secret"}
slog.Info("host settings", "config", record)
func (Config) GoString ¶
GoString formats config like Config.String for %#v. It redacts the same token fields, not arbitrary native object internals.
Example:
fmt.Printf("%#v\n", Config{AdminAuthKey: "example"})
func (Config) LogValue ¶
LogValue implements slog.LogValuer with the redacted form of config. Configure structured logging with the record, not its raw token fields.
Example:
slog.InfoContext(ctx, "host settings", "config", Config{AuthKey: "example"})
func (Config) String ¶
String formats config with its secrets replaced by "[REDACTED]" when set, so %v and %+v never print them. Native objects and URL fields retain their existing formatting; avoid putting secrets in URLs. It does not mutate the record or its shared dependencies.
Example:
fmt.Println(Config{AuthKey: "example"}.String())
type Host ¶
type Host struct {
// contains filtered or unexported fields
}
Host serves one Restate service endpoint and reaches the Restate ingress and admin API. Drivers bind service definitions with Host.Bind before Host.Start. The host owns its endpoint listener, not the shared HTTP client. Register Stop immediately after construction so startup rollback also releases the listener.
Example:
host := New(func(host *Builder) {
host.ClientOnly(true)
})
if err := host.Start(context.Background()); err != nil {
panic(err)
}
_ = host.Ingress()
func New ¶
func New(configure ...BuilderFunc) *Host
New returns a Host without performing I/O. Construction callbacks run once, synchronously in order on a fresh Builder; nil entries panic. Identity keys are copied at capture and publication; the HTTP client and logger stay shared.
Example:
host := New(func(host *Builder) {
host.ClientOnly(true)
})
_ = host.Addr() // Nil until a serving host starts.
func (*Host) Addr ¶
Addr returns the address the endpoint listens on, or nil before Host.Start. It reports the real port when Builder.Addr uses port 0. Client-only hosts also return nil; after Stop the address remains diagnostic information, not proof that the endpoint still accepts connections.
Example:
host := New()
if address := host.Addr(); address != nil {
fmt.Println(address.String())
}
func (*Host) Admin ¶
func (host *Host) Admin(ctx context.Context, method, path string, body []byte) (*http.Response, error)
Admin sends a request to the admin API, with the bearer token of Builder.AdminAuthKey when set. The path starts with a slash. A non-nil body is sent as JSON. The caller closes the response body. Non-2xx responses are returned unchanged for the caller to interpret. Without a context deadline, a 30-second timeout lasts until the response body closes.
Example:
response, err := host.Admin(ctx, http.MethodGet, "/health", nil)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return fmt.Errorf("admin health: status %d", response.StatusCode)
}
func (*Host) Bind ¶
func (host *Host) Bind(definition restatesdk.ServiceDefinition) error
Bind adds a service definition to the endpoint. It returns ErrStarted once Host.Start has run and ErrClientOnly on a client-only host. Native definitions remain shared; finish editing them before startup.
Example:
host := New()
if err := host.Bind(restatesdk.NewService("Example")); err != nil {
panic(err)
}
func (*Host) Ingress ¶
Ingress returns the Restate ingress client. It is available before Start. Configure the shared HTTP client before constructing the host; endpoint Stop does not close that client.
Example:
host := New() _ = host.Ingress() // Pass to SDK ingress.ServiceSend/ObjectSend/WorkflowSend.
func (*Host) Ping ¶
Ping checks that the Restate admin API is healthy. It does not prove ingress reachability or that this endpoint is registered. The caller bounds readiness I/O with ctx; requests without a deadline use the host's 30-second administrative timeout.
Example:
if err := host.Ping(ctx); err != nil {
return err
}
func (*Host) Query ¶
Query runs a SQL statement against the introspection API of the admin service and returns the rows, each keyed by column name. The statement reads the sys_* tables, for example sys_invocation. Responses are limited to 16 MiB; transport, status, and decoding failures are errors, not empty query results. Restrict admin credentials and never expose arbitrary query text to untrusted callers.
Example:
rows, err := host.Query(ctx, "SELECT id FROM sys_invocation LIMIT 10")
if err != nil {
return err
}
_ = rows
func (*Host) Serve ¶
Serve serves the endpoint, registers the deployment when Builder.Register is set, and blocks until ctx is done. It returns nil on a clean shutdown. A host that opened no listener registers nothing and only waits for ctx. Stop remains the listener's cleanup owner on registration failure or return; canceling Serve alone does not close active connections.
Example:
host := New()
ctx, cancel := context.WithCancel(context.Background())
cancel()
if err := host.Start(ctx); err != nil {
panic(err)
}
if err := host.Serve(ctx); err != nil {
panic(err)
}
func (*Host) Start ¶
Start builds the endpoint and opens the listener. It does not serve; call Host.Serve for that. A client-only host, or a host with no bound definitions, opens no listener. A host that would listen returns ErrNoIdentityKeys unless Builder.IdentityKeys is set or Builder.AllowInsecure is true. A successfully opened listener belongs to Stop even if Serve never runs.
Example:
host := New() // With no bound services this starts as a client only.
if err := host.Start(context.Background()); err != nil {
panic(err)
}
func (*Host) Stop ¶
Stop closes the listener, including a listener opened by Start but never served, and waits for active HTTP requests until ctx expires. If requests remain when ctx expires, it closes their connections and returns the shutdown error. Closing a connection cancels its request context; it cannot forcibly stop application code that ignores cancellation. The shared HTTP client remains caller-owned. Repeated calls are safe, and a host that never listened needs no cleanup.
Example:
host := New()
if err := host.Stop(context.Background()); err != nil {
panic(err)
}
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
itest
Package itest holds the shared setup of the Restate integration tests that run against the restate service of driver/restate/compose.yaml.
|
Package itest holds the shared setup of the Restate integration tests that run against the restate service of driver/restate/compose.yaml. |
|
protocoltest
Package protocoltest drives the public HTTP service-protocol seam of a Host without a Restate server.
|
Package protocoltest drives the public HTTP service-protocol seam of a Host without a Restate server. |
|
Package restateschedule implements schedule.Driver on Restate virtual objects.
|
Package restateschedule implements schedule.Driver on Restate virtual objects. |
|
Package restateworkflow implements workflow.Driver on Restate workflow services.
|
Package restateworkflow implements workflow.Driver on Restate workflow services. |
|
internal/execution
Package execution owns Restate attempts and suspension-safe runtime state.
|
Package execution owns Restate attempts and suspension-safe runtime state. |
|
internal/protocol
Package protocol owns Restate handler identities, raw JSON and propagation.
|
Package protocol owns Restate handler identities, raw JSON and propagation. |