HWS: Routing & Deployments

SDKs

Two SDKs drive app-lb from code — deployments, scaling, exec and shells, builds and pulls, secrets, tokens, and every namespace plugin — one for TypeScript and one for Rust, released together and checked against the same wire contract.

Language Package Install
TypeScript / JavaScript @heyocomputer/hws on npm npm install @heyocomputer/hws
Rust hws on crates.io (docs.rs) cargo add hws --no-default-features

The Rust crate is also the heyctl CLI (cargo install hws installs the binary); --no-default-features leaves out the CLI and gives you only the library. Both are versioned together (currently 0.3.0) and tested against the response fixtures app-lb's own tests write, so a field app-lb sends that an SDK does not model fails a test rather than going missing. Their changelogs: TypeScript, Rust.

For the routes underneath, see the HTTP API and the API reference.

Connecting

Point a client at an app-lb's admin API with a credential: an app-token (applb_…), a federated Heyo bearer, or the operator's Basic auth. A token minted for one namespace (heyctl token mint … --namespace team-a) reaches that namespace and nothing else, which is the right credential for an application or an agent.

import { Hws } from "@heyocomputer/hws";

const lb = new Hws({ server: "https://admin.example.com", token: process.env.APP_LB_TOKEN });
console.log(await lb.whoami());   // tier, confinement, namespace
let lb = hws::Client::builder("https://admin.example.com")
    .token(std::env::var("APP_LB_TOKEN")?)
    .build()?;
println!("{:?}", lb.whoami().await?);

The Rust crate also has a blocking client, hws::blocking::Client, with the same methods.

Deployments

await lb.createDeployment({
  id: "web", namespace: "team-a",
  routes: [{ host: "web.example.com" }],
  vm: { image: "nginx-fc", port: 80 },
  scaling: { min_replicas: 0, max_replicas: 4 },
});
await lb.waitForReady("web");
const out = await lb.exec("web", "uname -a");

Everything a deployment does is here: deployment/deployments, replaceDeployment, patchScaling, startBuild/startPull and waitForJob, rollouts, exec and an interactive shell, deleteDeployment. Writes take the spec as a plain object and send it as is.

Plugins

Each namespace plugin with an API has a client, and every call carries the namespace's wall: a 409 with code: "plugin_not_installed" until the namespace installs it.

Plugin TypeScript Rust Calls
any namespacePlugins, installPlugin, uninstallPlugin namespace_plugins, install_plugin, uninstall_plugin What a namespace can install, and installing it with its configuration
any (operator) plugins, plugin, setPlugin, pluginInstalls plugins, plugin, set_plugin, plugin_installs Enable and configure a plugin for the host
obs lb.obs(ns) lb.obs(ns) fleet, deployment, logs (paged), alerts, createAlert, deleteAlert
postgres lb.postgres(ns) lb.postgres(ns) databases, createDatabase, deleteDatabase
artifacts lb.artifacts(ns) lb.artifacts(ns) info, tags, tag, setTag, deleteTag, manifest, repo, setRepo
auth-heyo, auth-google lb.setAuth(id, gate), lb.clearAuth(id) lb.set_auth(id, &Gate), lb.clear_auth(id) Put a deployment behind the provider the plugin declared, or take it out (next release, after 0.3.0)
// A database for the app, its URL handed to the deployment as a secret.
await lb.installPlugin("team-a", "postgres");
await lb.postgres("team-a").createDatabase({ name: "app" });   // writes secret pg-app

// Heyo sign-in in front of the app, its health check left open.
await lb.setAuth("web", { provider: "heyo", publicPaths: [{ path: "/healthz" }] });

// Its errors from the last hour.
const page = await lb.obs("team-a").logs("web", { level: "error", window: "1h" });
use hws::{Gate, NewDatabase};

lb.install_plugin("team-a", "postgres", None).await?;
lb.postgres("team-a").create_database(&NewDatabase::new("app")).await?;
lb.set_auth("web", &Gate::provider("heyo").public("/healthz")).await?;

Errors

Failures are typed: NotFoundError / Error::NotFound, ForbiddenError / Error::Forbidden, ConflictError / Error::Conflict (with app-lb's code, such as plugin_not_installed), UnauthorizedError, invalid requests, and transport errors. Each carries app-lb's message, which says what to do next.

Through Heyo Cloud

A namespace on Heyo Cloud is reached through cloud's door, https://<cloud>/namespaces/<ns>/lb, with a Heyo API key instead of an app-token. The cloud SDKs hand you an hws client already pointed there:

Language Package Accessor
TypeScript @heyocomputer/sdk Namespace.connect("team-a").workloads() returns an Hws
Rust heyo-sdk Namespace::connect("team-a", opts)?.workloads()? returns an hws::Client

workloads() ships in @heyocomputer/sdk 0.2.0 and heyo-sdk 0.1.16 (next release; the versions on the registries today predate it). The door exposes deployments, secrets, auth providers, and the obs, postgres, artifacts, auth-heyo and auth-google plugins; see Through Heyo Cloud.