Bun
The agent runs on Bun rather than Node.js. The decisions Bun makes for us:- Native TypeScript execution. Source files run directly, with no transpile step in the build pipeline.
- Embedded SQLite. The
bun:sqlitemodule replacesbetter-sqlite3. Loses the build dependency onpython,make, andg++. The container image shrinks accordingly. - Native fetch.
axiosis gone. One fewer dependency, one fewer attack surface. - Native scheduling.
setIntervalis enough;node-cronis not in the dependency graph. - Automatic
.envloading.dotenvis gone for the agent’s needs.
Distroless
The runtime image is based onoven/bun:1.3.6-distroless. The trade-offs:
- No shell.
sh,bash,busybox,curl,wget, and every other utility are absent. An attacker who reaches the container has no shell to drop into. - No package manager. No
apk,apt, or anything that can install code at runtime. - Smaller surface. The image contains the Bun binary, libc, the agent’s source and its runtime dependencies. No shell or package manager.
kubectl exec -it into the
container expecting a shell. Diagnose through the dashboard,
through logs, and through restart-and-observe.
Health check patternDistroless containers cannot run a shell-based health check
(
HEALTHCHECK instruction with curl + sh). The agent relies
on liveness derived from container exit and the cloud’s
heartbeat-based agent.offline detection rather than a
container-local health probe.Single-file source
The runtime entry issrc/index.ts. The rest of the agent is a
set of ESM modules with no bundler step; Bun runs the source
directly. The image carries the source verbatim, runs it under
Bun, and that is the entire chain from git clone to running
process.
Image size
The Bun distroless base is most of the runtime image. Pull time on a fresh node is dominated by the base layer; tag-based caching makes subsequent pulls nearly instant.Standalone binary
bun build --compile produces a single-file executable per
platform. The binary embeds the Bun runtime and every dependency,
so a release-binary install reduces to “download, chmod, run” with
no runtime install on the host.
Per-tag CI publishes five binaries to
github.com/useobserver/agent/releases:
linux-x64, linux-arm64, darwin-x64, darwin-arm64, and
windows-x64.exe, plus a SHA256SUMS.txt file for verification
(SHA256SUMS in releases before 1.6.0).
The binary path is the lightest install but offers fewer
guardrails than the container path. Use the container image when
you want isolation (separate user namespace), a single upgrade
mechanism shared with other services, or tag-based version
pinning at the container-runtime layer. Use the binary on
constrained or air-gapped hosts that should not run Docker at all.
Binaries are produced from the same source as the container. The
build entry is src/index.ts either way. Runtime flags can be
forwarded to the embedded Bun via BUN_OPTIONS; see
bun.com/docs/bundler/executables.
