Silicon DM Docs
DM / Optional Rust relay runtime and updates
DM 0.6 adds groups. Release status

Optional Rust relay runtime and updates

Current 0.5 guidance: start using DM, sandbox entry, and shared transport / contracts. These replace older manual-pairing and per-profile connection instructions below; the standalone protocol remains compatible.

Enable silicon-dm-client's runtime feature to use the same relay implementation as the CLI. The normal Client remains stateless; choosing LocalRuntime explicitly opts into local files, background work, and a loopback HTTP listener. No IAM application secret belongs in either component.

[dependencies]
silicon-dm-client = { version = "0.4", features = ["runtime"] }

For development against this checkout, use a path dependency. The runtime feature also builds the dm-relay executable. Install it with cargo install silicon-dm-client --features runtime --bin dm-relay. For local development, cargo build -p silicon-dm-client --features runtime --bin dm-relay produces the executable in target/debug.

Login and launch

use silicon_dm_client::runtime::{DaemonCommand, LocalRuntime, LoginOptions};

let runtime = LocalRuntime::new(private_state_directory)?;
let callback = "http://127.0.0.1:9000/events".parse()?;
let options = LoginOptions {
    profile: "assistant",
    base_url: "https://backend.dm.teamofsilicons.com",
    short_lived_token: &slt,
    webhook_url: None,
    testing_environment_id: None,
    idempotency_key: &stable_login_key,
};
let status = runtime.login(&options, &DaemonCommand::default()).await?;
runtime.webhook("assistant", None, Some(&callback))?;
let identity = runtime.login_status("assistant", None).await?;
// Later, detach callbacks while retaining the login and queues:
// runtime.webhook("assistant", None, None)?;
let relay = runtime.client()?;
let daemon_status = relay.status().await?;

private_state_directory must be an absolute directory path. The runtime creates it privately (0700 on Unix), with credential, lock, database, and log files restricted to the owner. Its state is compatible with the CLI's SILICON_DM_HOME layout. LocalRuntime::from_environment() uses this exact state override first, then the configured home, then $SILICON_HOME/.silicon-dm or $HOME/.silicon-dm. SILICON_HOME must be an existing absolute directory. Different runtime directories are independent; their listeners must use different ports. Configure a port before first launch:

runtime.store().update(|config| {
    config.relay_port = 19782;
    Ok(())
})?;

The callback URL is optional during login. Configure it afterward through runtime.webhook(profile, test, Some(&url)); None removes it. Configured URLs are validated locally and saved with the actor's tokens and stable device ID. Only the SLT goes to the backend. A profile cannot be reassigned to a different actor, organization, or backend, which protects its existing queues. Profile names accept 1–64 ASCII letters, digits, underscores, or hyphens. Reuse the original login key and SLT after an uncertain response. If process launch fails after login, the saved profile remains; retry start without obtaining another SLT.

DaemonCommand::default() launches dm-relay run from PATH. Supply an absolute executable path in DaemonCommand when it is not installed on PATH. start_with passes the selected directory as SILICON_DM_HOME; it does not change the embedding application's process environment. The CLI uses this API with its own dm daemon run entry point. A detached process survives the launching command.

For a testing profile, first insert a runtime::store::TestKey into the store's testing_keys under the DM environment UUID, then use that UUID in LoginOptions. Its backend URL must match the login URL. Obtain the SLT from the paired IAM test environment; a production token is not a test credential.

Hosting in an existing application

An application that manages its own service lifecycle can await runtime.run() instead of starting another executable. Configure its profiles first through the store or a previous login. Run one host per state directory. The runtime enforces its daemon lock and binds only numeric loopback.

Stop the selected host with runtime.client()?.stop().await?. Ctrl-C also stops it. Dropping or cancelling the run future cancels its workers and account connections; durable inboxes and outboxes remain on disk. Multiple hosts with different directories and ports can run within one Tokio application.

runtime.client() returns the stateless RelayClient. Its submit_value preserves the complete original JSON, including extension fields, in the durable ACK and result. Its submit accepts a typed RelayRequest. Use a stable request UUID and the original operation idempotency key when retrying. An ACK confirms local persistence; inspect result(request_id) for the backend outcome. See relay protocol for request shapes.

Each incoming delivery is persisted before the backend transport ACK. With no configured webhook, callbacks stay pending without being marked delivered; configuring one resumes them. Unhooking retains authentication and queued work. The runtime posts it to the profile's callback until HTTP success and an exact {"type":"ack","data":{"acknowledged":true,"delivery_id":"received UUID"}} response. Recipient message acceptance queues Delivered; Read remains explicit. A logged-out profile keeps its pending work but no longer dispatches it. Sandbox generation changes prevent old queued mutations from reaching a cleaned environment. An in-flight callback cannot revive an archived delivery or enqueue its receipt after the runtime adopts a new generation. A callback already transmitted before the reset may still reach its recipient; the local archive prevents further retries.

Use runtime.logout(profile, testing_environment_id, stable_key).await? to revoke the selected family and disable its mapping. Login, refresh, and logout serialize per profile across processes. A refresh preserves concurrently changed callback settings. Direct store writers that replace a family during logout are protected by a token comparison; the result reports newer_login_retained.

Relay status reports authentication_required when loading a profile fails with 401 or 403. Log in again with a fresh IAM SLT for that profile; existing queues and its device ID are retained. Connection diagnostics expose the failing stage and error code without tokens or raw transport URLs. An initial WebSocket handshake 401 first triggers token refresh, allowing a still-valid family to recover from upstream access-token revocation without waiting for its recorded expiry or requiring another login.

Hourly dependency updates

UpdatePolicy is caller-owned and serializable. It defaults to enabled and tracks the last check; it contains no global state. After each application command finishes, invoke the updater with the Cargo project it may change:

use silicon_dm_client::runtime::updates::{self, DependencyTarget, UpdatePolicy};

let mut policy = UpdatePolicy::default(); // Restore from your store on startup.
let target = DependencyTarget { manifest_path: absolute_cargo_manifest };
let outcome = updates::after_command(&mut policy, &target).await;
// Persist policy even if the registry or build failed; the attempted check
// already advances its timestamp to avoid hammering the registry.

The updater skips disabled policies and checks less than an hour apart. To opt out, set policy.enabled = false and persist it; set it back to true to opt in. If an update exists, it runs cargo update for silicon-dm-client at the exact available stable version, then rebuilds the explicit manifest with cargo build --release --locked. Cargo still enforces the project's version constraints. A build failure is returned; the currently running application is unchanged, while the dependency lockfile may already have been updated. A successful rebuild reports restart_required: true.

Call claim_due(now) separately when you need to persist the timestamp before starting a network check, and then use the stateless check_update() API for inspection without executing Cargo. Concurrent callers should serialize their policy updates in their own store.

CLI installed-executable updates are also exposed by runtime::updates::{command,automatic} with an explicit store and CLI version. That path only replaces a dm executable installed in Cargo's bin directory; it leaves custom and checkout builds intact. Neither updater publishes a crate. Live installation remains dependent on an actual published release.

RelayClient::request_status(request_id) retrieves only the request ID and state. Use it for polling large queued requests; result(request_id) retains the full original request and terminal response/error contract.

Team of Silicons · Edit this page

Usage guides first. Protocol details when you need them.