Install
Omniglass ships as a single self-contained binary with the operator console compiled in, so
one file is the whole app: every run mode (server, node run, migrate, bootstrap, token)
is the same binary with a different first argument. You can download that binary directly, or
run the container image instead.
Download a binary
Section titled “Download a binary”Each GitHub Release carries an archive per platform:
| OS | Architectures | Archive |
|---|---|---|
| Linux | amd64, arm64 | .tar.gz |
| macOS | amd64 (Intel), arm64 (Apple silicon) | .tar.gz |
| Windows | amd64 | .zip |
Download the archive for your platform, verify it against checksums.txt, extract the
omniglass binary, and put it on your PATH:
VERSION=0.1.0 # the release tag, without the leading vBASE=https://github.com/hyperscaleav/omniglass/releases/download/v$VERSION
curl -sSLO $BASE/omniglass_${VERSION}_linux_amd64.tar.gzcurl -sSLO $BASE/checksums.txtsha256sum --check --ignore-missing checksums.txt
tar xzf omniglass_${VERSION}_linux_amd64.tar.gzsudo install omniglass /usr/local/bin/omniglass --versionEach archive also ships an SBOM (.sbom.json) alongside its checksum.
macOS: first run
Section titled “macOS: first run”The macOS binaries are not yet notarized (#58), so Gatekeeper quarantines a freshly downloaded binary. Clear the quarantine attribute once:
xattr -d com.apple.quarantine omniglassRun it
Section titled “Run it”The binary is the server, the collection node, and the CLI in one. To stand up the API and
console you need Postgres and the same environment the image uses; see
Container image for the OMNIGLASS_DSN and
migrate/server/bootstrap/token flow, which is identical for the binary. To drive a
running server from the command line, see the CLI.
Run a simulator
Section titled “Run a simulator”The same binary simulates devices: omniglass sim run, beside a collection node, serves every
component placed on that node whose product it has a profile for, each on its own loopback address
(simulation, and the operator’s walk-through in
simulate a fleet). It must share the node’s loopback: the same host,
or the same container or pod network namespace. With Docker, run it in the node container’s
namespace:
docker run -d --name omniglass-sim --network container:omniglass-node \ -e OMNIGLASS_SERVER -e OMNIGLASS_NODE_NAME -e OMNIGLASS_NODE_TOKEN \ ghcr.io/hyperscaleav/omniglass sim runA sidecar in the node’s pod is the Helm chart’s to offer (#892). It runs unprivileged, and each privilege it is given buys one thing:
- Ports below 1024 (SNMP 161, HTTP 80, SSH 22). In a container, Docker already lets any user bind
them; in Kubernetes, set the safe sysctl
net.ipv4.ip_unprivileged_port_start=0on the pod. A bare binary needssudo setcap cap_net_bind_service=+ep ./omniglass. Without any of these, lift the ports with--port-offset 10000and aim the endpoints at the lifted ports, as the dev fleet does. - Ping. The node’s pinger is unprivileged: the kernel must let its group send ICMP echo through
net.ipv4.ping_group_range(with Docker,--sysctl net.ipv4.ping_group_range="0 2147483647"). - Impairments and truthful ping need
--impairmentsandNET_ADMIN(docker run --cap-add NET_ADMIN), because they rewrite the firewall and traffic shaping of the network namespace the simulator runs in. Grant it only in a namespace of the simulator’s own (a container or pod), never on a workstation’s host namespace: there the same rules would apply to the whole machine’s loopback. Without it, every loopback address answers ping and impairments are reported unavailable; collection still works.
On macOS, loopback answers only 127.0.0.1 until an address is aliased, so a bare binary needs an
alias for each simulated address it serves (the dev fleet uses 127.60.0.0/16), for example
for i in $(seq 1 20); do sudo ifconfig lo0 alias 127.60.0.$i up; done. Impairments are Linux only.
Seed a different fleet
Section titled “Seed a different fleet”make dev and the Docker stack below seed the example fleet, but omniglass seed-dev --scenario <name> can stand up other fleets: a fresh install with nothing in it, the example fleet having a bad
day, or the example fleet at enterprise size. The list, with what each one holds, is the command’s own help, rendered in the
CLI reference.
Every scenario except the fresh install is the example fleet plus a generated expansion of it, so a
scenario never drifts from the fleet it grows from, and like the example fleet every one is
idempotent. Pick one with SEED_SCENARIO for make dev, or STACK_SCENARIO for the Docker stack
(on a fresh install the stack is the server and its owner alone, since its node and simulated
devices come from the example fleet):
SEED_SCENARIO=degraded make devSTACK_SCENARIO=large make dev-sim-containerThe large fleet copies the example fleet’s self-contained rooms (a room whose devices serve only the systems in it) under replica sites, and simulates only the example fleet’s own devices, so the collector is not load-tested by accident (a device the example fleet does not declare is never wired by the seed, whatever the scenario). It takes a couple of minutes to seed.
The whole loop in Docker
Section titled “The whole loop in Docker”From a checkout, with nothing but Docker, make dev-sim-container runs every piece of the above as
containers on a private network of their own: Postgres, the server (seeded with the example fleet and
its simulated components), the node edge-hq enrolled with a one-time token, and the simulator with
--impairments in the node container’s network namespace. Only the simulator holds NET_ADMIN, and it
reaches only that namespace; only the node takes the ping sysctl. In a container the simulator binds the
real ports, so the fleet is seeded unlifted (omniglass seed-dev --sim-port-offset 0). It prints the
console’s address, on a 127.0.0.1 port Docker picks so two copies never collide (sign in as
dev/dev), and tears everything down on Ctrl-C. Breaking a component on purpose works there as in
simulate a fleet.
It does not attach to a make dev already running on the host: that server’s bus listens on the
host’s loopback only, so a containerized node could reach it only on the host’s network, where the
simulator’s NET_ADMIN would rewrite the host’s own firewall. The stack carries its own server instead.
The same stack is what demos are recorded against, the terminal demos and the console walkthroughs
alike: make walkthrough-stack holds it
up without publishing anything and prints how a Playwright container on its network reaches it, and
make test-walkthrough-stack proves it (the node collects, and an impairment set through the API
takes a component’s reachability down and its repair brings it back). The recipe is
stack_walkthrough_up in web/e2e/lib/stack.sh.
Recording a demo
Section titled “Recording a demo”A demo is a recording of a feature working, by one of two recorders: a terminal demo for CLI and API
work, a walkthrough for a console flow. make demo ID=<id> records one demo of either kind
(ID="<a> <b>" several, no ID every demo), each recorder on a stack of its own; when a PR needs
which, and how to write and review each, are in demos.
A terminal demo is a VHS tape, scripts/demo/<id>.tape.
VHS runs from its own image, ghcr.io/charmbracelet/vhs at v0.12.1, pinned by digest in
web/e2e/tape/Dockerfile;
the image bundles what VHS drives (ttyd for the terminal, chromium to render it, ffmpeg to encode the
GIF and MP4) and the fonts the house settings name, and publishes a multi-platform index, so it runs
on amd64 and arm64 hosts alike. The recorder adds jq, the prelude every tape sources, and this
tree’s omniglass binary, copied out of the stack’s image, so a tape types the CLI the stack serves.
Nothing is installed on the host. make demo writes scripts/demo/out/<id>/: the GIF, the MP4 and
demo.json, the manifest a passing recording carries. A terminal demo is a test: a command it types
that exits non-zero, a wait in it that runs out, or a recording over its budget (MAX_GIF_BYTES and
MAX_SECONDS in
web/e2e/tapelib.mjs)
fails the run, naming the tape and the cause. make test-demos proves each of those failures.
A walkthrough is a script, web/e2e/walkthroughs/<id>.yaml (the id is lowercase letters, digits and
hyphens): a title, an optional description, and steps, each one kind of action or a wait for a
visible result, with a caption shown while it runs. The step kinds, their fields and the rules are
defined, with an annotated example, at the top of
web/e2e/walkthroughlib.mjs.
The format is validated strictly: an unknown key or kind fails, naming the file and the step, and so does a
script that ends on an action rather than a wait for its result.
title: Read a simulated component's statussteps: - goto: /simulation caption: The Simulation page shows the simulated fleet, live. - click: '[data-testid="sim-row"] >> nth=0' caption: Open a simulated component. - wait: { selector: '[data-testid="sim-status"]' } caption: Its status says where it is served, and how often collection has reached it. poster: trueThe recording’s ffmpeg image is pinned by digest to an amd64-only image (its tag publishes no multi-platform index), so recording on an arm64 host needs amd64 emulation registered with Docker.
make walkthroughs records every script on the walkthrough stack (make walkthroughs ID=<id> records
one) into web/e2e/walkthroughs/out/<id>/: an mp4 opening on a title card, with a cursor that glides to
each target and ripples where it clicks, a highlight on the target and a caption bar; preview.webp, an
animated preview encoded from the mp4 by the same pinned ffmpeg (a lower frame rate and width, looping),
which is what the PR comment and a docs page play inline; poster.png; and a still per step. A walkthrough
is a test: a step whose result never appears fails the run, naming the step, its kind and what it waited
for, and keeps the stills taken so far; so does a recording over the budget, whose limits on length, mp4
size and preview size are MAX_SECONDS, MAX_BYTES and MAX_PREVIEW_BYTES in
web/e2e/walkthroughlib.mjs. make test-walkthroughs proves both failures, an over-budget preview among them.
When a PR needs a walkthrough, how to review its stills, and how a docs page embeds one are in
demos.