Releases: OmnyGrid/omnyserver
Release list
v0.16.0 — configure the Hub's AI for web clients
Configure the Hub's AI once, and every browser gets :ai — no key in the client.
omnyserver ai config --provider anthropic --key - # prompts, writes ~/.omnyserver/ai.yaml
omnyserver hub start --shell … # proxies :ai for web clientsWhy
The dashboard's in-terminal :ai and :ide proxy their provider calls through the Hub so no browser holds an API key — but omnyserver's shell broker served no AI: it built the OmnyShell broker without an aiProxy, so a web client's fetchHubAiConfig always answered "unavailable".
Added
omnyserver ai config | show | test— configure the AI provider the Hub proxies.configwrites provider/model/key/mode/language to<OMNYSERVER_HOME>/ai.yaml(mode 600;--key -reads from a hidden prompt);showprints it with the key masked;testvalidates the key + models with a live provider request. Keys may also come fromANTHROPIC_API_KEY/OPENAI_API_KEY/GEMINI_API_KEY.hub start --ai-config(default<OMNYSERVER_HOME>/ai.yaml) — loaded and threaded into the shell broker under--shell. The browser learns the provider/model and forwards requests for the Hub to sign; the key never leaves the Hub. Startup reports whether AI is configured.
Reuse
AiConfig, AiConfigIo, HttpProxyService, providerFor, validateModels are public in the omnyshell package omnyserver already depends on — this is CLI glue plus one broker wiring. Adds http (for ai test).
Full notes: CHANGELOG.md
v0.15.1 — a node that fails says why
A node that cannot start should say why, not go dark.
omnyserver node start --hub wss://hub:8081 --id web-01 --principal p --token t
Connecting to wss://hub:8081/node …
hub rejected the connection (forbidden): principal p may not register node web-01
(the node.register action needs the "node" role)Fixed — node start was silent when a connection failed
The signal was never missing. The node runtime already receives the Hub's rejection — the exact reason, principal … may not register node … — but its logger defaulted to a no-op that OmnyServer never replaced. So a node that authenticated and was then refused registration (a grant with the wrong role) retried forever without a word. Finding a one-line misconfiguration took a live packet trace.
The runtime now logs through the CLI:
- A failure and its cause are printed once — repeats within a short window are collapsed, so a node that keeps retrying a rejection it cannot fix says why once, not once per backoff. It stays up and recovers automatically when you fix the grant.
Connecting to <url> …prints before it blocks, and a terminal auth failure exits witherror: <reason>instead of an unhandled exception and a stack trace.--verbose(-v) shows the full lifecycle — every attempt, handshake and heartbeat.
Fixed — a wedged capability probe could freeze registration
Capability detection shells out to docker, nvidia-smi, clinfo and the like, and the scanner waits for every probe before a node can register — with no timeout on any of them. One stuck command (a hung GPU driver, a wedged Docker daemon) would freeze the whole node, silently. Each probe now has a deadline; a timed-out probe is killed and the capability treated as absent.
Changed
- The Hub logs a refused node registration on its own side too (
refused registration: … needs the "node" role), sojournalctlshows it, not only the audit trail. - Requires omnyhub ^1.7.0, which delivers a registration rejection to the node as a typed error the moment it arrives. Before it, a refused node waited out the register timeout (10s) on every attempt; now it fails — and logs why — at once.
Full notes: CHANGELOG.md
v0.15.0 — run it as a service, and answer only to a credential
Two things a Hub needs before you can walk away from it: something to keep it running, and the habit of asking who is calling.
omnyserver service install hub --tls-dir /etc/letsencrypt/live/hub.example.com
omnyserver service install node --hub wss://hub:8443 --id worker-01 --token …
omnyserver service status hubFixed — the HTTP API was unauthenticated without --api-token
If you run a Hub without --api-token, upgrade.
HttpApiServer.tokenAuthenticator() returned null when no API token was configured, and a service registered with a null authenticator is not authenticated at all. A Hub started with grants but no API token — a reasonable thing to do, since --grant is what the flag exists for — served the whole API to anyone who could reach the port:
$ omnyserver hub start --cert … --key … --grant alice:tok:admin # no --api-token
$ curl -k https://hub:8443/api/v1/nodes # no credentials at all
[] 200
List the fleet, read the audit log, run formulas on every node, issue credentials. --grant looked like it secured the API and did not: grants are only ever consulted from inside the authenticator that was not there, which is why whoami answered {"principal":"anonymous","authenticated":false} even when handed a valid grant token. doc/security.md described this as a feature — "for a loopback-only Hub" — but nothing enforced loopback and the Hub binds 0.0.0.0 by default.
The Hub is controlled with the --api-token, or with a grant's (principal, token) pair, and with nothing else. It now enforces that whether or not an API token is set. A Hub with neither authenticates nobody rather than everybody, and says so at startup instead of looking healthy while answering no one.
/healthz and /metrics stay open on purpose: a load balancer and a Prometheus scraper carry no bearer token, and gating them would make the Hub look dead to the things that check whether it is alive. Neither exposes fleet data.
Anyone relying on the old behaviour was relying on an open API. Pass --api-token, or authenticate with a grant (--token + --principal).
Added — omnyserver service …
hub start runs in the foreground and dies with your terminal, which is fine right up until it is the thing your fleet talks to. The new service command group installs the Hub or a Node agent as a native OS service — a systemd unit on Linux, a launchd job on macOS, a scheduled task on Windows — so it starts at boot and comes back if it falls over.
There is no separate daemon and no config file, because the CLI installs itself. service install takes the same flags as hub start / node start, reconstructs the command line, and bakes this executable plus those flags into the service definition. What the service runs at boot is what you would have typed. Paths are absolutized on the way in, so the unit does not care which directory you installed from.
Nine verbs: install, reinstall, reconfigure, uninstall, start, stop, restart, status, info. --dry-run prints the generated unit or plist without touching the system; --system installs machine-wide. service reconfigure re-applies changed flags to a running service, and service reinstall refreshes the binary while keeping the config it was installed with — how a fleet picks up a new release.
Also new: --cors-origin '*', which used to be accepted and then silently match nothing, since it was compared as a literal origin string. It is a real widening — any page may call the API — but not an open door: the Hub sends no allow-credentials, so a browser attaches nothing ambient and a caller still needs a token it was given. The Hub says so at startup.
Changed — hub start now persists by default
To <OMNYSERVER_HOME>/hub, i.e. ~/.omnyserver/hub. It used to keep everything in memory unless you passed --data-dir. --ephemeral opts out.
A Hub with optional persistence silently forgets the fleet, the audit trail, and every credential grant add ever issued — on every restart. Survivable when you are watching it in a terminal. Supervised by an init system, with Restart=always, it is a trap: the Hub comes back up empty and healthy.
An explicit --data-dir still means what it always meant; only the default moved. HubConfig(dataDir: null) is untouched, so the Dart API still defaults to in-memory — this is a CLI default, not a library one.
Full notes: CHANGELOG.md
v0.14.0 — the dashboard, and the API to drive it
A browser you can run a fleet from — and the API, CLI and Hub features it needed, which turn out to be the ones the product was already half-built for.
omnyserver_web is a static, installable PWA that operates a Hub with no backend of its own. It is the REST API, seen.
Six things were already built and wired to nothing
Most of this release is not new machinery. It is connecting what was already there:
MetricRepositorypersisted a sample on every heartbeat, and nothing ever read one back. NowGET /nodes/{id}/metrics,node metrics --since 1h, and a chart.DesiredState/StateReconciler— the product's thesis — existed in the domain, reachable from nothing. Now declare, diff, drift and reconcile, fromstate set|show|diff|reconcileand a declared-vs-actual panel.NodeAgent.sendLogsshipped batches the Hub decoded and threw away. Nownode logs -f.NodeDescriptor.labelsexisted with no flag that could set one. Nownode start --label env=prod,nodes list --label, and selectors that target them.PresetRepositoryhad no endpoint. Now a preset library: save once, apply by id, stop shipping preset files around.- The Hub had no persistence at all — no
--data-dir. Grants, presets, desired state and alerts now survive a restart.
New
- Grant-verified API auth.
--principal+--tokenare checked against the Hub's grant table and resolve to real roles, rather than being an attribution header. Aviewerlink is now safe to hand out; mutations needoperatororadmin. - Live events over SSE —
GET /events/stream,events --follow. The feed is a console, not a poll. - Async operations. An install that outlives a request no longer times out in the browser:
--asyncreturns an operation id,ops list|showtracks it. - Fleet selectors — act on one node, on a label, or on all of them, with a per-node result matrix instead of a single verdict.
- Alerts that hold a condition before they mean anything (disk over 90%, a node offline past its grace), announced once and resolved once.
- Live grant management — an operator can be added or revoked without restarting the Hub.
- CORS, so a browser can actually read the Hub's answers — including its errors.
Every endpoint landed with its CLI command in the same change, so the CLI never falls behind the UI.
Requires
omnyhub1.6.0 — CORS and Server-Sent Events.omnyshell_web1.15.0 — the terminal, imported rather than rebuilt, so a node's shell opens in the dashboard.
Full notes: CHANGELOG.md
omnyserver_web v0.2.1 — installable
Live at → https://omnygrid.github.io/omnyserver/
The manifest shipped with "icons": []. That is not a cosmetic gap: a PWA with no icon is one the browser will not offer to install, so "installable PWA" was a claim the app could not honour. It can now.
The mark is OmnyGrid's — the prompt chevron, the grid, the cursor — on the app's own theme colour. The grid is the part that means something here, because it is the fleet, and it is also what tells this icon apart from OmnyShell's on a home screen, which is the chevron alone.
any at 192 and 512 carry the rounded tile themselves, since that purpose is shown as-is. maskable at both sizes bleeds to the edges with the mark held inside the central 80% safe zone, so a platform cropping to a circle or a squircle clips background rather than the logo. Plus an apple-touch icon and a favicon.
Full notes: omnyserver_web/CHANGELOG.md
omnyserver_web v0.2.0 — the dashboard
Live at → https://omnygrid.github.io/omnyserver/
The dashboard: a browser you can run a fleet from. A static, installable PWA that operates an OmnyServer Hub with no backend of its own — it is the Hub's REST API, seen. Served from GitHub Pages, and equally servable from the Hub itself.
Tagged separately from the server (v0.14.0) because it ships from the same repo but versions on its own — a Pages deploy is gated on a bump to this version, so a server-only release does not redeploy it.
The fleet
Log in against a Hub URL with a grant, validated up front by whoami, so a bad token fails at the door rather than on the first thing you try to do — and the roles it returns decide what the UI offers. Then: every node, online and off, searchable and filterable by label; per-node detail with live CPU, memory, per-device storage and a process table; a metrics chart drawn from samples the Hub had been persisting on every heartbeat since long before anything read them back; and an activity feed that is a console over SSE rather than a poll.
Control
Restart, shutdown, update, run a formula, apply a preset — behind confirmation and behind the role that permits it. Act on one node, on a label, or on all of them, with a per-node result matrix. Declare desired state and see the drift. Long operations run asynchronously, so an install that outlives a request no longer times out in the browser.
The shell
Open a shell on any node, in the dashboard. The terminal — xterm view, PTY bridge, mobile fit engine, accessory bar — is imported from omnyshell_web rather than rebuilt, and drives the OmnyShell broker the Hub hosts on the same port. One Hub, one certificate, one login.
Requires
omnyserver 0.14.0 · omnyshell_web 1.15.0 · omnyhub 1.6.0
Full notes: omnyserver_web/CHANGELOG.md
v0.4.0 — Renewable TLS directory
Certificates that renew themselves. The Hub can now take its TLS material from a LetsEncrypt-style directory and reload it when it is renewed — matching omnyshell hub start --tls-dir.
omnyserver hub start --tls-dir /etc/letsencrypt/live/hub.example.com \
--api-token api-secret --grant node-account:node-token:nodeAdded
hub start --tls-dir <dir>: readsfullchain.pem+privkey.pemfrom the directory instead of--cert/--key. The files are re-checked periodically and, when a renewal changes them, the Hub rebinds its listener with the fresh certificate — no restart, with established connections draining on the old listener. Passing--tls-dirtogether with--cert/--keyis an error, and the Hub still refuses to start without one of the two (no insecure mode).HubConfig.tlsDirectoryandHubConfig.tlsReloadInterval, the embedded equivalent. Exactly one ofsecurityContextortlsDirectorymust be given.
Changed
HubConfig.securityContextis now nullable and no longerrequired(atlsDirectorymay take its place). Existing code that passes asecurityContextis unaffected.
Full changelog: v0.3.1...v0.4.0
v0.3.1
Dependency maintenance — no API or behaviour changes.
Changed
- Widened the dependency floors to the current releases:
omnyhub1.5.1,omnyshell1.56.1,sqlite32.9.4,meta1.19.0 andpath1.9.1.
Full Changelog: v0.3.0...v0.3.1
v0.3.0
One Hub, two kinds of node. An OmnyServer Hub can now also serve OmnyShell nodes — same port, same certificate, same credentials — and one node process can be both an OmnyServer agent and an OmnyShell node.
# Hub: three surfaces on one TLS port
omnyserver hub start --cert … --key … --shell
# /node → OmnyServer node control channel
# /shell → OmnyShell broker
# /api/v1 → REST API
# Node: one process, both roles, one service unit
omnyserver node start --hub wss://hub:8443 --id worker-01 --token … --with-shell
# …or attach a standalone OmnyShell node/service to the same Hub:
omnyshell node start --hub wss://hub:8443/shell --id worker-01 --token …
omnyshell exec worker-01 'uptime' --hub wss://hub:8443/shell …Added
--shell/--shell-pathonhub startmount an OmnyShell broker on the Hub's listener (ShellHub, exported fromomnyserver_hub.dart). It shares the Hub's--granttoken table, so one credential set serves both fleets and there is nothing extra to provision. Authorization stays OmnyShell's:adminmay open a session on any node, other roles only on nodes whoseallow-roleslabel names them.--with-shell/--shell-path/--shell-labelonnode startalso run an OmnyShell node in the same process — one binary, one service unit, one supervision target — with the same PTY backendomnyshell node startuses.HubConfig.shellMount(default/shell), and aconnectionAuthenticatorparameter onOmnyServerHub.registerService().
Fixed
OmnyServer's node handshake was imposed on every WebSocket mount. It was registered hub-wide, and omnyhub resolves a route's connection authenticator as route.connectionAuthenticator ?? hubWide — a route's null means inherit, not none. Any co-hosted WebSocket service would therefore have had OmnyServer's handshake run against it: a peer speaking its own protocol would have had its frames eaten by a handshake meant for someone else and been rejected after a 10-second timeout. The handshake now lives on the node channel's route alone.
Latent before this release (nothing else was mounted) — but it is what made co-hosting possible. Verified: with the hub-wide authenticator restored, 6 of the 10 new tests fail and the suite takes 3 minutes of timeouts instead of under a second.
run-hub.sh also silently dropped extra flags, so ./run-hub.sh --shell did nothing.
Changed
- Depends on
omnyshell ^1.56.0.
111 tests (up from 101), green on Linux, macOS and Windows. Verified end to end against the real binaries: the omnyshell CLI ran a command on a node brokered by an OmnyServer Hub, on the same port serving the REST API. Full notes in CHANGELOG.md.
v0.2.1
Fixed
A node's status was unavailable for a full heartbeat interval after it registered.
Heartbeats are periodic, so the status snapshot they carry is one interval away. On the default 15-second cadence the Hub reported no status at all for a node that had just come up — GET /api/v1/nodes/{id}/status answered 404 for ~18 seconds:
registered. polling /api/v1/nodes/worker-01/status every 2s
t+2s → 404
...
t+16s → 404
t+18s → 200 ← 18 seconds of nothing
The agent now pushes a snapshot on becoming ready, on every (re)registration, so status is live immediately (t+1s → 200).
Introduced in 0.2.0: the pre-omnyhub agent sent an eager first heartbeat for exactly this reason, and omnyhub's NodeRuntime beats only on its timer. The test suite could not see it — the harness uses a 200 ms cadence, so a beat always landed before the assertion. The new regression test uses a 30 s cadence, which fails without the fix and passes with it.
Changed
run-hub.shstill passed--api-port, removed in 0.2.0 when the REST API moved onto the Hub's TLS port. It now uses--node-path. The README told people to run it, so it was broken.- README refreshed for the single-port model — the quick-start could not run as written (
nodes listwith no--api/--ca/--token) — plus the full badge set and an OmnyGrid ecosystem section.
101 tests, green on Linux, macOS and Windows. Verified end-to-end against the real CLI. Full notes in CHANGELOG.md.