rototo Rust SDK
Use Workspace for embedded applications. Loading a workspace parses and lints
it by default, so applications only receive a workspace that is ready to resolve.
use rototo::{Environment, ResolveContext, Workspace};
let workspace = Workspace::load("./workspace").await?;
let env = Environment::new("prod");
let context = ResolveContext::from_json(serde_json::json!({
"account": {
"plan": "enterprise"
}
}))?;
let qualifier = workspace
.resolve_qualifier("enterprise-accounts", &context)
.await?;
let variable = workspace
.resolve_variable("payment-review-queue", &env, &context)
.await?;
Environment is only the environment name. Environment-owned properties are not
part of the SDK contract; application/runtime facts belong in ResolveContext.
Workspace Loading
Workspace::load(source).awaitparses, lints, and prepares the workspace.Workspace::load_with_options(source, LoadOptions::new().with_lint(LintMode::Skip)).awaitis available for tools that need to inspect broken workspaces.workspace.inspection()exposes environments, qualifiers, and variables.workspace.lint().awaitreruns workspace lint and returns structured diagnostics.
source can be a local path, file:// URI, git+file://, git+https://,
git+ssh://, or https:// archive URL. Git sources support #ref:subdir;
archive URLs support #:subdir.
Periodic Workspace Refresh
Applications that want the SDK to keep a workspace fresh in the background can
use RefreshingWorkspace:
use std::time::Duration;
use rototo::{RefreshOptions, RefreshingWorkspace};
let workspace = RefreshingWorkspace::load(
"git+https://github.com/example/config.git#main:rototo",
RefreshOptions::new().with_period(Duration::from_secs(60)),
).await?;
The initial load must succeed. After that, refresh failures are non-fatal: resolution continues using the last successfully loaded workspace, and refresh status records the failure.
let status = workspace.status().await;
if let Some(error) = status.last_error {
tracing::warn!(%error, "workspace refresh is failing");
}
refresh_now().await runs the same refresh path on demand and returns whether
the source was unchanged, refreshed, or immutable.
Git sources check the remote ref before cloning when a ref is provided. Sources
pinned to a full commit SHA are treated as immutable: the SDK logs a warning and
does not run periodic refresh for them. HTTPS archive sources use ETag or
Last-Modified when available; otherwise the SDK falls back to loading and only
publishes the new workspace if it fully validates.
Resolve Context Contract
Workspaces can declare a JSON Schema for application-provided resolve context:
[context]
schema = "schemas/context.schema.json"
The linter validates this contract:
- the schema file exists, parses, and is a valid JSON Schema
- qualifier context attributes are declared by the schema
Resolution validates each ResolveContext against the schema before evaluating
qualifiers or variables. Use resolve_*_with_options and
ResolveOptions { validate_context: false } only for tooling that deliberately
needs to bypass the application contract.
External Variable Values
Variable values can be declared inline under [variable.values] or in TOML
files next to the variable file. For variables/banner.toml, rototo also loads
variables/banner-values/*.toml; each file stem is the value key.
Each file can use a single top-level value key:
value = "Welcome back."
Object values use a [value] table:
[value]
queue = "priority-review"
timeout_ms = 3000
Custom Lua lint can validate each expanded value by defining
lint_value(value). The argument contains name, value, and variable.
Lower-Level APIs
The crate still exposes the function APIs used by the CLI:
inspect_workspace(path).awaitlint_workspace(path).awaitlist_qualifiers(workspace).awaitread_qualifier(workspace, id).awaitresolve_qualifier(workspace, id, context).awaitresolve_variable(workspace, id, environment, context).await
Prefer Workspace for embedded applications because it loads once, enforces
lint on load, and keeps context validation close to resolution.