> ## Documentation Index
> Fetch the complete documentation index at: https://docs.use.observer/llms.txt
> Use this file to discover all available pages before exploring further.

# Bun and distroless: design choices

> Why the agent runs on Bun and ships in a distroless image.

The agent's runtime choices are deliberate. They shape the image
size, the operator surface, and the security posture in production.

## 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:sqlite` module replaces
  `better-sqlite3`. Loses the build dependency on `python`,
  `make`, and `g++`. The container image shrinks accordingly.
* **Native fetch.** `axios` is gone. One fewer dependency,
  one fewer attack surface.
* **Native scheduling.** `setInterval` is enough; `node-cron` is
  not in the dependency graph.
* **Automatic `.env` loading.** `dotenv` is gone for the agent's
  needs.

The trade-off is that Bun is younger than Node and the
ecosystem's edge cases sometimes show up. The agent's
dependencies are deliberately narrow to limit exposure.

## Distroless

The runtime image is based on `oven/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.

Operational consequence: do not `kubectl exec -it` into the
container expecting a shell. Diagnose through the dashboard,
through logs, and through restart-and-observe.

> [!NOTE]
>
> **Health check pattern**
>
> Distroless 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 is `src/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](https://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](https://bun.com/docs/bundler/executables#runtime-arguments-via-bun_options).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.