Skip to content
These docs cover Strata 0.9.0-preview.1.It is a preview, and the API can still change before 1.0.

Use Strata Cloud

Strata Cloud is a hosted checkpoint store with a console where people answer approvals. The client ships in Datalium.Strata.Cloud and datalium-strata-cloud. On this page you create the Cloud store, run a graph against it, and run the decision worker, which resumes the threads people decide in the console.

  • The packages from Installation. In .NET, Datalium.Strata.Hosting brings the Cloud client. In Rust, add datalium-strata-cloud yourself.
  • Your beta endpoint and an API key. Keep the key in a secret store or the environment, never in a graph’s state or a run’s metadata.
  • A graph with an interrupt boundary. Pause for approval shows how to add one.

StrataCloudOptions says where Cloud is and how to talk to it. The store and the decisions client share one set of options.

using Datalium.Strata.Cloud;
var cloud = new StrataCloudOptions
{
BaseUrl = new Uri(Environment.GetEnvironmentVariable("STRATA_CLOUD_URL")!), // <your beta endpoint>
ApiKey = Environment.GetEnvironmentVariable("STRATA_CLOUD_KEY")!,
RequestTimeout = TimeSpan.FromSeconds(10),
};
using var store = new StrataCloudCheckpointStore(cloud);

The store is async only: its synchronous Save and Load throw NotSupportedException. The engine’s async runs use the async methods.

The options:

C# Rust Default What it sets
BaseUrl (required) new(base_url, …) — The endpoint, without /v1
ApiKey (required) new(…, api_key) — Sent as Authorization: Bearer
ConnectTimeout with_connect_timeout 3 s How long a connection may take to open
RequestTimeout with_request_timeout 30 s How long one attempt may take
Retry with_retry 4 attempts How saves and loads are retried
GzipThresholdBytes with_gzip_threshold 64 KiB Larger request bodies are sent gzipped

The Cloud store is a checkpoint store like any other, so you pass it to a run. A thread that pauses at an interrupt boundary waits for a decision in the console.

try
{
var result = await graph.RunAsync("refund-42", new Refund("ada", 120m, "new"), store);
Console.WriteLine(result.GetType().Name); // Interrupted: the thread waits for a decision
}
catch (StrataCloudEntitlementException)
{
// Your plan's limit refused a new thread. Threads already running carry on.
}
catch (StrataCloudException error)
{
Console.WriteLine($"{error.Kind} after {error.Attempts} attempts (HTTP {error.StatusCode}, {error.Code})");
}

By default the store makes up to 4 attempts. It retries a network error, a timeout, and 408, 429, 500, 502, 503 and 504 answers, plus 409 on a save, waiting at least as long as Cloud’s Retry-After. It never retries 401, 403 or any other 4xx. When the retries run out, StrataCloudException.Kind (C#) or the CloudError message in the run’s error (Rust) says why.

These work as they do on a self-hosted store:

  • Thread leases. A run with a lease is fenced on Cloud. A lease’s time to live must be between 1 second and 1 hour. See Thread leases.
  • Listing threads. The store implements the query port, so WakeDueAsync / wake_due wakes due timers and approval timeouts, and the wake driver runs against Cloud.
  • Recovery. RecoverAsync / recover and RecoverStaleAsync / recover_stale work, and so does the recovery driver. See Durable execution.

These don’t:

  • Retention. Cloud doesn’t take purges from the client. The store has no retention port, so AddRetention refuses it when the host starts, and Rust’s Host::retain doesn’t accept it.
  • The strata CLI. It can’t read the Cloud store.
  • Group commit, delta checkpoints and value offload. These are options of the SQLite, Postgres and Redis stores. The Cloud store has none of them.

Known limitations tracks these.

A reviewer approves or rejects a waiting thread in the console. The decision worker reads those decisions and resumes each thread for you.

Each round, the worker reads the decision feed, holding the request open for up to 25 seconds while it is empty. It handles each new decision in order, one at a time:

  1. It checks that the thread is its own: the thread id prefix you set, your own check (Owns / with_owns), and the graph’s identity.
  2. It resumes the thread with the decision’s signal, pinned to the checkpoint the decision answered. A decision made before the thread moved on can’t approve a later boundary.
  3. It reports the outcome: Resumed, Failed, AlreadyHandled (another worker got there first) or NotMine.

The worker remembers the last 10,000 decisions it handled. It waits out an unavailable feed and stops with an error that no wait can fix, such as a 401.

using var decisions = new StrataCloudDecisions(cloud);
var worker = new DecisionWorker<Refund>(graph, store, decisions, new DecisionWorkerOptions<Refund>
{
ThreadIdPrefix = "refund-",
OnHandled = (handled, ct) =>
{
Console.WriteLine($"{handled.Decision.ThreadId}: {handled.Outcome} (decided by {handled.Decision.DecidedBy})");
return ValueTask.CompletedTask;
},
});
await worker.RunAsync(stoppingToken); // polls and resumes until the token is cancelled

PollAsync(wait) runs one round and returns what it handled, if you drive the loop yourself.

Each CloudDecision carries the thread id, the sequence it answered, the engine’s HumanSignal, and who decided: DecidedBy, DecidedByEmail and Notes (decided_by, decided_by_email and notes in Rust). The console verifies DecidedBy. The signal’s reviewer token is the reviewer’s own choice, so check DecidedBy when only some people may approve.

To read the feed without the worker, call StrataCloudDecisions.GetAsync / Decisions::get. Each call is one request and isn’t retried. Cloud offers a decision until something is saved to its thread, and the resume’s save is what acknowledges it.

builder.Services.AddStrata()
.AddCloudCheckpoints() // Strata:Cloud
.AddGraph(_ => Refunds.Build())
.AddWakeDriver<Refund>()
.AddRecoveryDriver<Refund>()
.AddDecisionWorker<Refund>(_ => new DecisionWorkerOptions<Refund> { ThreadIdPrefix = "refund-" });
appsettings.json
{
"Strata": {
"Cloud": { "BaseUrl": "<your beta endpoint>", "RequestTimeout": "00:00:10" },
"Runs": { "Lease": { "Owner": "refunds-1", "Ttl": "00:00:30" } }
}
}

AddCloudCheckpoints() registers the store and the decisions client from Strata:Cloud: BaseUrl, ApiKey, ConnectTimeout and RequestTimeout. Set ApiKey from a secret store or the environment variable Strata__Cloud__ApiKey, not in the file. Nothing is sent to Cloud when the host starts. AddDecisionWorker<TState>() runs the worker as a hosted service and starts it again after a wait if it stops on an error. The recovery driver needs the lease, or Strata:Recovery:StaleAfter, as on any store.

Hosting covers the rest of the host: settings, health checks, telemetry and shutdown.