rototo
API

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

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:

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:

Prefer Workspace for embedded applications because it loads once, enforces lint on load, and keeps context validation close to resolution.