Plugins are app-lb's optional capabilities: telemetry, git, CI, Postgres databases, the artifact store, secrets pages and sign-in providers, switched on at runtime and used by namespaces through the admin API, the dashboard, heyctl and the SDKs.
Every plugin is compiled into app-lb; none are loaded dynamically. Whether each one runs, and with what configuration, is a record on disk that the dashboard's Plugins page, heyctl plugins and the SDKs edit. The HTTP routes are in the API reference.
Enable, then install
A plugin is switched on in two halves:
- The operator enables it for the whole host, with a configuration that points it at the service behind it (app-obs, ci, the git remote, pg-fc, an artifact store). Credentials never go in the configuration: they live in app-lb's secret store, and the configuration references them as
{"secret": "…", "key": "…"}. - Each namespace installs it. An auto-installing plugin installs itself into every namespace (existing ones when it is switched on, new ones as they appear) unless the operator sets
"auto_install": false. The others are opt-in: a namespace administrator installs them. A namespace that uninstalls an auto-installing plugin stays uninstalled.
A plugin does nothing for a namespace unless both are true. Calls to one that is not installed answer 409 with "code": "plugin_not_installed" and the install command in the message; while the operator has it off, "code": "plugin_disabled".
# Operator, on the app-lb host
heyctl create secret app-obs --from-env api_token=APP_OBS_API_TOKEN
echo '{"url": "http://127.0.0.1:9600", "api_token": {"secret": "app-obs", "key": "api_token"}}' \
| heyctl plugins set obs -f -
heyctl plugins enable obs
heyctl plugins describe obs # configuration, live status, last error
# A namespace administrator
heyctl plugins list -n team-a # what team-a can install, and what it has
heyctl plugins install postgres -n team-a
heyctl plugins install auth-google -n team-a -f google.json # with per-namespace configuration
heyctl plugins uninstall postgres -n team-a
The same from code, with @heyocomputer/hws or hws:
await lb.setPlugin("obs", true, { url: "http://127.0.0.1:9600", api_token: { secret: "app-obs", key: "api_token" } });
await lb.installPlugin("team-a", "postgres");
const installed = await lb.namespacePlugins("team-a");
Installing needs admin over the whole namespace: the operator, a fleet admin token, a namespace token with no deployments list, or a federated namespace:<ns>:admin grant. Enabling and configuring is the operator's.
The plugins
| Plugin | Name | Installs | What a namespace gets | SDK |
|---|---|---|---|---|
obs |
Observability | automatically | Logs, metrics and error alerts for every VM, collected by app-obs, and a dashboard | lb.obs(ns) |
remote |
Git | automatically | Git repositories on the region's git remote, repo tokens, and a repository browser | install only |
secrets |
Secrets | automatically | A page for the namespace's own secrets: key names, never values | lb.secrets(ns) and friends |
artifacts |
Artifacts | automatically | Its <ns>/ corner of the global artifact store: tags, manifests, repository visibility |
lb.artifacts(ns) |
auth-heyo |
Heyo sign-in | automatically, enabled by default | The auth provider heyo, so a deployment requires Heyo sign-in with one call |
lb.setAuth(id, { provider: "heyo" }) (after 0.3.0) |
postgres |
Postgres | opt-in | Dedicated databases on pg-fc, each with its own role, credentials written to the secret pg-<name> |
lb.postgres(ns) |
auth-google |
Google sign-in | opt-in | The auth provider google, with the namespace's own OAuth client |
lb.setAuth(id, { provider: "google" }) (after 0.3.0) |
ci |
CI | opt-in | Repositories, runs and submit tokens on the region's ci | install only |
pgfc |
pg-fc databases | fleet only | An operator console for pg-fc pools | — |
vapi |
vapi inference | fleet only | An operator console for vapi inference gateways | — |
obs, remote, secrets, postgres and ci also have a dashboard, framed by app-lb at /namespaces/<ns>/plugin-console/<id>. The namespace dashboard's Plugins button lists them.
obs
Operator configuration: url (app-obs's API listener, required), api_token (a reference to APP_OBS_API_TOKEN), poll_secs, auto_install (default true). No per-namespace configuration.
Installing starts collection for every deployment in the namespace; uninstalling stops it, and what was collected ages out with app-obs's retention. Reads go through app-lb with the namespace's own token: fleet overview, per-deployment metrics, paged logs and error alerts. See the obs API.
const obs = lb.obs("team-a");
const fleet = await obs.fleet({ window: "1h" });
const errors = await obs.logs("web", { level: "error", limit: 50 });
await obs.createAlert({ deployment: "web", threshold: 5, webhook_url: "https://hooks.example.com/x" });
remote
Operator configuration: url (the git remote, e.g. https://git.us5.heyo.work, required), api_token (a reference to the remote's REMOTE_PLUGIN_API_TOKEN, required), poll_secs, auto_install (default true).
The namespace's repositories, created, browsed and tokened in the git remote's own pages, framed in app-lb. The git remote also accepts the namespace's app-lb token directly at POST /api/repos/<ns> and POST /api/tokens, which is how a program creates a repository and a scoped push token.
secrets
Operator configuration: auto_install (default true) only; the store is app-lb's own.
A page where a namespace administrator creates, rotates and deletes the namespace's secrets, which deployments read with env_from and builds with auth. It lists key names and never shows a value. The SDKs manage the same secrets with putSecret / patchSecret / deleteSecret.
artifacts
Operator configuration: url and api_key (a reference to the store's ART_API_KEY), both optional and defaulting to APP_LB_ARTIFACT_STORE and APP_LB_ARTIFACT_STORE_API_KEY, so on a host already configured that way {} adopts that store and its key. auto_install defaults to true.
While it is enabled its store is the global store: where a deployment's storeless artifact block pulls from. Disabling it falls back to the environment. A namespace reaches only its own <ns>/ tags and repositories (and blobs and manifests by digest); publishing needs admin over the whole namespace. See namespace artifacts.
const store = lb.artifacts("team-a");
const v2 = await store.tag("team-a/web:v2");
await store.setTag("team-a/web:latest", v2.digest); // promote
await store.setRepo("team-a/web", { public: true }); // publish on the hub
Pushing an image itself is heyctl artifact push.
postgres
Operator configuration: url (pg-fc's dashboard listener, required), user, password (a reference to its dashboard password), pg_host (the pooler's public name, for connection strings), pg_port (default 6432), sslmode (default require), max_per_namespace (default 10), poll_secs. Opt-in per namespace, with no per-namespace configuration.
Each database is a dedicated pg-fc database named ns_<namespace>_<name> with its own role. The password comes back once, and is also written to the namespace secret pg-<name> (url, host, port, database, user, password) unless asked not to. Deleting revokes the credential; the data stays on pg-fc until an operator purges it, and the secret is left for the namespace to delete.
const pg = lb.postgres("team-a");
const db = await pg.createDatabase({ name: "app" }); // db.url, once; also in secret pg-app
await pg.databases();
await pg.deleteDatabase("app");
auth-heyo and auth-google
Installing either declares an auth provider in the namespace, and uninstalling removes it. A deployment then requires sign-in by inheriting it, with auth.provider_ref, heyctl set auth <id> --provider-ref heyo, or setAuth / set_auth from the SDKs.
auth-heyodeclaresheyo: Heyo gate tokens verified against the auth service's published key set, holding nothing secret. It is enabled by default on any host withAPP_LB_AUTH_URL, and installs itself everywhere. Operator configuration, all optional:jwks_url,login_url,cookie,auto_install. Per namespace,nameandrequire, for example{"require": {"accountId": ["acct_7f3c"]}}to admit one account only.auth-googledeclaresgoogle, with the namespace's own OAuth client. Opt-in. Per namespace:client_id,client_secret(a reference to a secret in the namespace),allowed_domainsand/orallowed_emails, and optionallyname,cookie_domainandapp_token: trueto admit app-tokens as well.
await lb.installPlugin("team-a", "auth-google", {
client_id: "1234.apps.googleusercontent.com",
client_secret: { secret: "google", key: "client_secret" },
allowed_domains: ["example.com"],
});
await lb.setAuth("intranet", { provider: "google", publicPaths: [{ path: "/healthz" }] });
The plugins never replace or remove a provider somebody declared by hand: installing over one of the same name answers 409 (install with {"name": "…"} instead), and the automatic install skips that namespace. Uninstalling while a deployment still inherits the provider answers 409 and names the deployments.
ci
Operator configuration: url (ci's listener, required), api_token (a reference to ci's plugin token, required), tenant_network (the heyvm network tenant jobs run on, required), poll_secs, auto_install (default false).
A namespace that installs it registers repositories, mints submit tokens and follows its runs on the region's ci. Tenant jobs read the namespace's secrets ci, ci.<repo> and ci.<repo>.<env>.
pgfc and vapi
Operator consoles, not installed by namespaces: pgfc monitors and configures pg-fc pools (configuration nodes), and vapi watches and drives vapi inference gateways (configuration gateways).
Through Heyo Cloud
A namespace managed by Heyo Cloud reaches app-lb through cloud's door, https://<cloud>/namespaces/<ns>/lb, with a Heyo API key. The door forwards only what it names. For plugins, that is the namespace's plugin list, installing and uninstalling obs, postgres, artifacts, auth-heyo and auth-google, the obs and postgres APIs, and the artifacts API without blob uploads. The cloud SDKs' Namespace.workloads() returns an @heyocomputer/hws / hws client pointed at that door. See SDKs.