Documentation | Console | Example Gallery
Go library for Tilebox, a lightweight space data management and orchestration software - on ground and in orbit.
Run the following command to add the library to your project:
go get github.com/tilebox/tilebox-goFor Tilebox datasets type generation, you will need to install tilebox-generate command-line tool.
For examples on how to use the library, see the examples directory.
Accounts, Datasets, and Workflows constructors require a *connect.Client. Create it with client.New();
the defaults are https://api.tilebox.com, TILEBOX_API_KEY, SDK metadata, and binary Connect protobuf.
HTTPS negotiates HTTP/2 with HTTP/1.1 fallback. Plain HTTP and Unix sockets use HTTP/1.1 by default.
Reuse one RPC client across domain clients to share its connection pool and interceptor chain.
A CLI can configure TLS, a proxy, and retries once for all three APIs without implementing its own retry loop:
import (
"crypto/tls"
"net/http"
"net/url"
"time"
"github.com/tilebox/tilebox-go/accounts/v1"
"github.com/tilebox/tilebox-go/client"
"github.com/tilebox/tilebox-go/datasets/v1"
"github.com/tilebox/tilebox-go/workflows/v1"
)
transport := client.NewHTTPTransport()
transport.TLSClientConfig = &tls.Config{MinVersion: tls.VersionTLS12}
proxyURL, err := url.Parse("http://localhost:8080")
if err != nil {
return err
}
transport.Proxy = http.ProxyURL(proxyURL)
rpcClient := client.New(
client.WithHTTPTransport(transport),
client.WithRetryPolicy(client.RetryPolicy{
MaxRetries: 2,
MinWait: 100 * time.Millisecond,
MaxWait: 5 * time.Second,
}),
)
datasetsClient := datasets.NewClient(rpcClient)
workflowsClient := workflows.NewClient(rpcClient)
accountsClient := accounts.NewClient(rpcClient)NewHTTPTransport creates an independent pool without changing http.DefaultTransport. Configure it before use.
client.WithHTTPTransport uses the supplied transport unchanged while preserving SDK retries. For Unix sockets,
a custom transport must provide its own socket dialer.
Responses have no size limit by default. To bound memory use, set a per-message limit, measured after decompression:
import (
"connectrpc.com/connect/v2/connecthttp"
"github.com/tilebox/tilebox-go/client"
"github.com/tilebox/tilebox-go/datasets/v1"
)
rpcClient := client.New(client.WithTransportOptions(connecthttp.WithReadMaxBytes(64 << 20)))
datasetsClient := datasets.NewClient(rpcClient)URL, API key, metadata, HTTP client, native transport, interceptor, and retry options now belong only to client.
client.WithTransportOptions accepts connecthttp.Option values for protocol, compression, and message limits.
client.WithInterceptors accepts native connect.ClientInterceptor functions in outermost-first order. Both replace the
former WithConnectClientOptions API. The SDK applies its configured API key and client metadata after custom
interceptors. An empty API key leaves caller-provided authentication intact.
For per-call headers, use connect.NewClientContext(ctx) and its CallInfo.RequestHeader(). Read response headers
and trailers from the same CallInfo after the call. Use a separate client context for each concurrent call.
The default HTTP client retries replayable requests on transient transport failures, HTTP 429 (except Connect
resource_exhausted), and HTTP 5xx, with five total attempts. Retries reuse Connect's request body without a second
payload buffer. Streaming request bodies are not retried. A lost response can still mean
the server processed the request; retries do not guarantee exactly-once execution. Use call deadlines to bound retries.
The default policy permits four retries, starts with MinWait of 20 ms, and caps exponential equal-jitter waits at
10 seconds. Unix sockets use the same retry policy as HTTP. client.WithRetryPolicy(client.RetryPolicy{}) disables
retries. Default clients do not follow redirects. client.WithHTTPClient replaces the entire HTTP client, including
retries, redirects, and timeouts; WithHTTPTransport and WithRetryPolicy then have no effect.
client.WithTransport accepts a native connect.Transport and bypasses all HTTP setup, including URL, retry policy,
and transport options. Authentication, metadata, and client interceptors still apply.
Domain clients retain WithDisableTracing; WithDisableLogging has been removed. Constructors do not install
telemetry exporters. To export to Tilebox, call workflows.ConfigureTileboxTelemetry(ctx, apiURL, apiKey) before
constructing domain clients, which capture the global tracer provider. Console logging remains a separate choice.
Storage notifications use the same RPC client as other Workflows operations:
workflowsClient := workflows.NewClient(rpcClient)
err := workflowsClient.StorageLocations.NotifyObjectCreated(ctx, subscriptionID, "path/to/object", eventTime)Notifications share the configured transport, authentication, metadata, interceptors, and retry policy, including
support for in-process transports. A zero eventTime omits the timestamp. The server must implement the
StorageLocationService.NotifyObjectCreated RPC; the SDK no longer calls the JSON notification endpoint.
There is no separate notification timeout. Use a context deadline to bound the whole call, including retries.
Retries may create duplicate jobs if a response is lost.
Fields marked queryable in a dataset schema can be filtered with fluent Boolean, string, and numeric expressions. Multiple
expressions passed to WithFilters are combined with each other and with temporal and spatial filters using logical
AND.
package main
import (
"context"
"log/slog"
"time"
"github.com/google/uuid"
"github.com/tilebox/tilebox-go/client"
"github.com/tilebox/tilebox-go/datasets/v1"
"github.com/tilebox/tilebox-go/query"
)
func main() {
ctx := context.Background()
client := datasets.NewClient(client.New())
datasetID := uuid.MustParse("019c0123-4567-7890-abcd-ef0123456789")
start := time.Date(2026, time.July, 1, 0, 0, 0, 0, time.UTC)
end := start.Add(24 * time.Hour)
datapoints, err := datasets.Collect(client.Datapoints.Query(ctx,
datasetID,
datasets.WithTemporalExtent(query.NewTimeInterval(start, end)),
datasets.WithFilters(
query.Field("eo_cloud_cover").LessThan(20.0),
query.Field("granule_name").Equal("S2A_GRANULE"),
query.Or(
query.Field("quality").GreaterThanOrEqual(80),
query.Field("quality").IsNull(),
),
),
))
if err != nil {
slog.ErrorContext(ctx, "Failed to query datapoints", slog.Any("error", err))
return
}
slog.InfoContext(ctx, "Found datapoints", slog.Int("count", len(datapoints)))
}Comparisons with absent fields evaluate to unknown and therefore do not match. Use IsNull, as above, when absent values
should be included explicitly. NotEqual also excludes absent values.
Here we define a simple task that logs "Hello World!":
package helloworld
import (
"context"
"log/slog"
"github.com/tilebox/tilebox-go/workflows/v1"
)
type HelloTask struct {
Name string // You can add any fields you need to the task struct.
}
// The Execute method isn't needed to submit a task but is required to run a task.
func (t *HelloTask) Execute(ctx context.Context) error {
slog.InfoContext(ctx, "Hello World!", slog.String("Name", t.Name))
return nil
}
// The Identifier method is optional and will be generated if not provided.
func (t *HelloTask) Identifier() workflows.TaskIdentifier {
return workflows.NewTaskIdentifier("hello-world", "v1.0")
}Here we create a Workflows client and submit a job with a single task:
package main
import (
"context"
"log/slog"
"os"
"github.com/tilebox/tilebox-go/client"
"github.com/tilebox/tilebox-go/workflows/v1"
)
type HelloTask struct {
Name string
}
func main() {
ctx := context.Background()
workflows.ConfigureConsoleLogging(slog.LevelInfo)
workflows.ConfigureTileboxTelemetry(ctx, "https://api.tilebox.com", os.Getenv("TILEBOX_API_KEY"))
client := workflows.NewClient(client.New())
job, err := client.Jobs.Submit(ctx, "hello-world",
[]workflows.Task{
&HelloTask{
Name: "Tilebox",
},
},
)
if err != nil {
slog.ErrorContext(ctx, "Failed to submit job", slog.Any("error", err))
return
}
slog.InfoContext(ctx, "Job submitted", slog.String("job_id", job.ID.String()))
}workflows.ConfigureConsoleLogging adds console output without replacing the Tilebox exporter, so logs are written to both destinations when both are configured.
Here we create a TaskRunner and run a worker that is capable of executing HelloTask tasks:
package main
import (
"context"
"log/slog"
"os"
"github.com/tilebox/tilebox-go/client"
"github.com/tilebox/tilebox-go/workflows/v1"
)
type HelloTask struct {
Name string
}
// The Execute method is required to run a task.
func (t *HelloTask) Execute(ctx context.Context) error {
slog.InfoContext(ctx, "Hello World!", slog.String("Name", t.Name))
return nil
}
func main() {
ctx := context.Background()
workflows.ConfigureConsoleLogging(slog.LevelInfo)
workflows.ConfigureTileboxTelemetry(ctx, "https://api.tilebox.com", os.Getenv("TILEBOX_API_KEY"))
client := workflows.NewClient(client.New())
runner, err := client.NewTaskRunner(ctx)
if err != nil {
slog.ErrorContext(ctx, "failed to create task runner", slog.Any("error", err))
return
}
err = runner.RegisterTasks(&HelloTask{})
if err != nil {
slog.ErrorContext(ctx, "failed to register tasks", slog.Any("error", err))
return
}
if err := runner.RunForever(ctx); err != nil {
slog.ErrorContext(ctx, "task runner stopped", slog.Any("error", err))
}
}After a job has been executed, we can query runner logs associated with it.
package main
import (
"context"
"log/slog"
"github.com/google/uuid"
"github.com/tilebox/tilebox-go/client"
"github.com/tilebox/tilebox-go/workflows/v1"
"github.com/tilebox/tilebox-go/workflows/v1/job"
)
func main() {
ctx := context.Background()
client := workflows.NewClient(client.New())
jobID := uuid.MustParse("019e070c-63ba-1c7e-5f1d-65be3e22d52a")
for logRecord, err := range client.Jobs.QueryLogs(ctx, jobID,
job.WithLimit(100),
job.WithSortDirection(job.Ascending),
) {
if err != nil {
slog.ErrorContext(ctx, "failed to query job logs", slog.Any("error", err))
return
}
slog.InfoContext(ctx, "job log", slog.String("body", logRecord.Body))
}
}Distributed under the MIT License (The MIT License).