silicon-remind-client#
A stateless Rust client for the public Silicon Remind API. It has no dependency on the backend crate or database. The CLI is a separate consumer of this package; there is no CLI-only server capability.
Use from a Rust application#
While developing in this repository:
[dependencies]
silicon-remind-client = { path = "../silicon-remind/crates/client" }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
Use a registry version only after that version has actually been published. Rust 1.98 or newer is required. Constructing the client performs no network call. The service URL is a pathless HTTPS origin; literal loopback HTTP is accepted for local development. Redirects are not followed, so a redirect cannot move the request's credentials to another service. Requests time out after 30 seconds, connection establishment after five seconds, and response bodies are bounded to 16 MiB.
use silicon_remind_client::{Client, Mutation, Secret, models};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let anonymous = Client::new("https://backend.remind.teamofsilicons.com")?
.auto_update(false);
// Your caller supplies this single-use SLT from IAM, not a password or OTP.
let slt = Secret::new(std::env::var("REMIND_SLT")?);
let session = anonymous.login(&slt, &Mutation::new()).await?;
let client = anonymous.with_session(session.access_token, "tos")?;
let identity = client.me().await?;
println!("Signed in as {:?}", identity.public_id);
let reminder = client.create_reminder(&models::CreateScheduleRequest {
text: "Review the build results".to_owned(),
kind: models::ScheduleKind::Recurring,
cron: "*/15 * * * *".to_owned(),
timezone: "UTC".to_owned(),
}, &Mutation::new()).await?;
println!("Created {}", reminder.id);
Ok(())
}
Webhook subscriptions are optional; use configure_webhook or
subscribe_webhook when a receiver should receive deliveries. A Carbon may log
in and read reminders but cannot create one.
Use anonymous.iam().await? before login to discover the server's app_id for
obtaining an IAM SLT. client.login_status().await? verifies the attached session
and returns LoginStatus { authenticated: true, identity: Some(identity) } on
success. HTTP 401 returns an unauthenticated status with no identity; all other
failures remain errors. The client does not refresh automatically. These methods
respect with_test_environment like other public operations. The CLI's
SILICON_HOME setting does not affect this stateless package.
State and secrets#
Client is immutable configuration: with_session and with_test_environment
return new clients. It does not save credentials, refresh tokens automatically,
cache IAM authority, or select a user's organization for them. Store session
state in the embedding application. Clone the returned Session if both your
state store and a constructed client need its access token.
Secret redacts Debug output and exposes its value only through expose().
Its Serialize implementation deliberately emits the value for requests and
caller-owned secure persistence. Do not serialize sessions into ordinary logs.
Session contains rotating access/refresh tokens, expiry seconds, actor and org.
After refresh, replace both saved tokens atomically. A fresh Mutation means a
new logical exchange; reuse its key when retrying the same exchange.
Mutation::new() creates a unique request key. Mutation::with_key adopts a
16–255-character visible ASCII key for retries. Reuse only for identical inputs
on the same operation, actor and environment. The client sends each API call
once; retry decisions belong to the caller.
Method reference#
| Method | Input / result |
|---|---|
health(ready) |
Liveness or readiness Health |
iam() |
Public IamInfo: app ID, IAM URL and optional IAM sandbox UUID; no session needed |
login_status() |
LoginStatus with verified identity, or authenticated: false for HTTP 401 |
login(slt, mutation) |
Session from IAM SLT |
refresh(refresh_token, mutation) |
Successor Session |
logout(token, mutation) |
Revocation; refresh token revokes its family |
me() |
Current Identity |
create_reminder(input, mutation) |
CreateScheduleRequest → ScheduleResponse |
reminders(filters) |
ListSchedules → Page<ScheduleResponse> |
reminder(id) |
Visible ScheduleResponse |
update_reminder(id, patch, mutation) |
Partial replacement → updated reminder |
set_status(ids, status, mutation) |
Atomic pause/resume → StatusBatch |
archive_reminder(id) |
Archive owned reminder |
executions(id, paging) |
Page<ExecutionResponse> |
configure_webhook(destination) |
Owner endpoint and signing secret → receipt |
webhook() |
Configured URL and version, without secret |
disable_webhook() |
Disable owner destination |
subscribe_webhook(destination) |
Add an independent subscription |
webhooks() |
List active subscriptions |
unsubscribe_webhook(id) |
Disable one subscription |
silicons(after, limit) |
Page<Silicon> for the selected org |
create_environment(input) |
EnvironmentCreated with root key |
environments(include_deleted, after, limit) |
Page<TestEnvironment> |
environment(id) |
Environment metadata |
environment_key(id) |
Active root key |
rotate_environment_key(id) |
New root key |
delete_environment(id) |
Begin 30-day recovery window |
restore_environment(id) |
Restore and return fresh key |
current_environment() |
Root-key-only sandbox metadata |
configure_environment_iam(&secret) |
Install/rotate a sandbox's IAM test app secret |
clean_environment() |
Root-key-only clear of the selected sandbox |
ListSchedules::default() selects current reminders. Set section to Archived
for retained history. A page's next_cursor is opaque; pass it unchanged with
the same filters into the next call. The execution API uses the same cursor/limit
pattern. Environment and Silicon directory pages use UUID after cursors.
PatchScheduleRequest uses Option fields. None omits a property and retains
the current value; Some replaces it. Clearing required fields is not supported.
The server enforces cron, timezone, text and lifecycle rules. A single reminder
can be paused through update_reminder; the batch method handles 1–100 UUIDs
atomically. Archived reminders cannot be edited.
Test environments#
use silicon_remind_client::{Client, Secret, Mutation, models};
async fn example() -> silicon_remind_client::Result<()> {
let base = Client::new("http://127.0.0.1:8086")?.auto_update(false);
let key = Secret::new(std::env::var("REMIND_TEST_APP_SECRET")?);
let sandbox = base.with_test_environment(key)?;
let environment = sandbox.current_environment().await?;
let session = sandbox.login(&Secret::new("slt_from_test_IAM"), &Mutation::new()).await?;
let signed_in = sandbox.with_session(session.access_token, "test-org")?;
let reminders = signed_in.reminders(&models::ListSchedules::default()).await?;
println!("{}: {} reminders", environment.name, reminders.items.len());
Ok(())
}
with_test_environment clears any previously attached bearer and organization,
preventing accidental production credentials from being carried into a sandbox.
Attach the test session afterward. Environment IDs are selectors for your state
store; the IAM application app_secret selects its sandbox without a root key.
Legacy 32-character Remind keys remain accepted.
Legacy management methods require a production org session and reject a test-scoped
client locally. current_environment and clean_environment reject a client
without a test key locally. All ordinary reminder methods use the same paths.
Errors#
Match Error::Api { status, code, request_id, retry_after, .. } for server failures.
401 is missing/expired/mismatched authority; 403 is a permission denial; 404
also hides other organizations' resources; 409 describes a state or idempotency
conflict. test_reminder_limit applies only to legacy manually paired sandboxes. Invalid is local input
validation, Transport means no usable HTTP exchange, Decode means an
incompatible response, and ResponseTooLarge bounds memory consumption.
For an ambiguous create/update response, repeat the exact request with the same
Mutation. Do not retry validation errors unchanged. For backpressure, honor
retry_after when supplied and choose a bounded retry policy.
Automatic dependency maintenance#
By default, after an API call finishes, the package may check crates.io if its
process-local last attempt is at least an hour old. Concurrent calls share a
single check. A discovered update runs cargo update -p silicon-remind-client --precise <version> against the enclosing Cargo project. This changes the
lockfile; compiled code changes only after the next build. Idle clients do not
run a timer or daemon. A restart resets the package's in-memory hourly throttle.
Disable maintenance with .auto_update(false) or
SILICON_REMIND_CLIENT_AUTO_UPDATE=false. Set
SILICON_REMIND_CLIENT_MANIFEST=/absolute/path/Cargo.toml when the process working
directory does not identify the intended consuming project. Missing manifests,
unpublished crates, registry failures and Cargo failures do not change API
results. An explicit updates::maintain call returns an UpdateStatus if a host
application wants to display maintenance progress. The CLI disables package
maintenance and manages its own executable update after each command instead.
Contract discovery and application-selected sandboxes#
client.versions().await? reads /api/versions. Every API request offers wire version 1. with_test_environment(Secret::new(app_secret)) accepts the IAM sandbox application secret and clears existing bearer authority; call current_environment() to discover its ID/name, then login with a test SLT or public identity ID. Ordinary operations enforce that identity’s permissions. See testing and version policy.
Bug reports and telemetry#
client.report(&BugReportRequest { message, pr }, &Mutation::new()).await? returns a durable receipt. client.report_status(id).await? reads your own receipt's current state. Both Carbons and Silicons may report; report limits and normal IAM organization isolation apply.
Telemetry is on by default. Use client.with_telemetry(false) to disable SDK and associated server request telemetry. The client stays stateless: events pass through the authenticated Remind API, and the backend uses the official Space Station Rust package. No Space Station key is distributed to consumers. client.track(&TelemetryEvent { ... }).await is available to CLI/daemon integrations; it is a bounded best-effort call and never makes the application operation fail. See diagnostics.