Skip to main content

hws/
lib.rs

1//! The Heyo Web Services SDK: a client for the
2//! [app-lb](https://github.com/Heyo-Computer/hws) admin API.
3//!
4//! app-lb is a load balancer for heyvm Firecracker/KVM microVMs, and the door
5//! to everything a Heyo namespace runs. This crate drives it: create and
6//! replace workloads ([`Client::create_deployment`], [`Client::start_rollout`]),
7//! scale pools, build and pull their images, run commands inside a VM, attach
8//! an interactive shell, and read their metrics and logs ([`Client::obs`]).
9//!
10//! Every call works with a namespace-scoped token — the credential a tenant is
11//! handed — and is narrowed by the server to that namespace.
12//!
13//! It is also the library behind the `heyctl` CLI, which is the point — the
14//! CLI is this crate's first consumer, so a field the client stops understanding
15//! becomes a compile error rather than a silently blank column at somebody's
16//! terminal.
17//!
18//! ```no_run
19//! # async fn f() -> hws::Result<()> {
20//! use hws::{Client, ExecRequest};
21//!
22//! let lb = Client::builder("127.0.0.1:9090")
23//!     .token(std::env::var("APP_LB_TOKEN").unwrap())
24//!     .build()?;
25//!
26//! let out = lb.exec("sb-7f3a9c", &ExecRequest::new("uname -a")).await?;
27//! println!("{}", out.stdout);
28//! # Ok(()) }
29//! ```
30//!
31//! # Authentication
32//!
33//! Prefer an **app-token**: scoped to particular deployments, revocable without
34//! a restart, and optionally expiring. Basic auth also works and is unscoped —
35//! it is the operator credential, and the one that mints tokens.
36//!
37//! ```no_run
38//! # async fn f() -> hws::Result<()> {
39//! use hws::{AdminScope, Client, NewToken};
40//!
41//! let admin = Client::builder("127.0.0.1:9090").basic("admin", "s3cret").build()?;
42//! let minted = admin.mint_token(
43//!     &NewToken::new("agent-runner")
44//!         .admin(AdminScope::Admin)
45//!         .for_deployments(["sb-7f3a9c"]),
46//! ).await?;
47//!
48//! // The secret is in the reply and nowhere else, ever.
49//! println!("{}", minted.token);
50//! # Ok(()) }
51//! ```
52//!
53//! A token scoped to specific deployments is refused the fleet-wide routes —
54//! including minting — so it cannot widen itself.
55//!
56//! # Blocking callers
57//!
58//! Everything here is async. Under the `blocking` feature,
59//! [`blocking::Client`] is the same surface with the `await`s taken out.
60//!
61//! # What this crate does *not* smooth over
62//!
63//! Three behaviours of app-lb are surprising enough that hiding them would be
64//! worse than surfacing them:
65//!
66//! - A non-zero exit code from [`Client::exec`] is `Ok`, not `Err`. The command
67//!   ran; it failed.
68//! - `exec`'s timeout does not kill anything. It bounds app-lb's call to the
69//!   daemon — on expiry you get [`Error::Upstream`] and the command **keeps
70//!   running in the guest**.
71//! - A shell socket has no resume. If it drops, the session is gone;
72//!   reconnecting gives a *new* shell. See [`shell`].
73
74#![cfg_attr(docsrs, feature(doc_cfg))]
75
76pub mod api;
77pub mod artifacts;
78pub mod error;
79pub mod gate;
80pub mod images;
81pub mod obs;
82pub mod postgres;
83pub mod shell;
84pub mod transport;
85pub mod types;
86pub mod wait;
87
88#[cfg(feature = "blocking")]
89#[cfg_attr(docsrs, doc(cfg(feature = "blocking")))]
90pub mod blocking;
91
92pub use api::{Client, ClientBuilder, ExecRequest, Gates, MetricsQuery, NewToken};
93
94/// The namespace an object belongs to when nothing says otherwise.
95///
96/// app-lb's own default, repeated here because the item routes for
97/// namespace-scoped objects — an auth provider is `(namespace, name)` — need a
98/// namespace in the *path*, so a client that omits `--namespace` still has to
99/// name one.
100pub const DEFAULT_NAMESPACE: &str = "default";
101pub use error::{Credential, Error, Result};
102pub use gate::{Gate, GatePath};
103pub use obs::{LogQuery, NewAlert, OBS_PLUGIN, ObsClient};
104pub use artifacts::{ARTIFACTS_PLUGIN, ArtifactsClient};
105pub use postgres::{NewDatabase, POSTGRES_PLUGIN, PostgresClient};
106pub use shell::{Shell, ShellEvent, ShellExit, ShellOptions};
107pub use transport::{Auth, Transport};
108pub use types::*;
109pub use wait::{JobProgress, PoolProgress};
110
111// -- CLI internals ----------------------------------------------------------
112//
113// Public only so `main.rs` can reach them across the lib/bin boundary. Not part
114// of the supported surface, and `doc(hidden)` so they do not appear as though
115// they were.
116
117/// Read-modify-write helpers over a deployment spec as a `serde_json::Value`.
118///
119/// Public because they are genuinely useful to a caller assembling a spec, and
120/// because writes go through `Value` by design — see [`api`].
121pub mod spec;
122
123#[cfg(feature = "config-file")]
124#[cfg_attr(docsrs, doc(cfg(feature = "config-file")))]
125pub mod config;
126
127#[cfg(feature = "artifact")]
128#[cfg_attr(docsrs, doc(cfg(feature = "artifact")))]
129pub mod artifact;
130
131#[cfg(feature = "cli")]
132#[doc(hidden)]
133pub mod cmd;
134#[cfg(feature = "cli")]
135#[doc(hidden)]
136pub mod output;