Skip to content

Dependencies

Every dependency needs a reason. If one below stops earning its line, it should go. The bar is: would writing this ourselves be worse than carrying it?

Two things are deliberately not here, and both were close calls, see Deliberate omissions.

Go

Module Why
github.com/tailscale/tailcat Built-in encrypted private transport for agents and, through zoomies gateway, for providers on a home network; NAT traversal and relay fallback without installing and supervising a separate tunnel process. Pinned in go.mod, which is the one place its version is written; requires Go 1.27.1.
tailscale.com Tailcat’s network and key types, plus a local DERP test relay to verify real tunnel behaviour without hosted infrastructure.
modernc.org/sqlite Pure-Go SQLite. No cgo means the binary is static, cross-compiles to five platforms from one machine, and runs on distroless.
github.com/go-chi/chi/v5 Router. net/http's 1.22 mux nearly suffices, but chi's middleware chaining and sub-router mounting keep the API's role and audit middleware readable. Small, stable, no reflection.
github.com/google/go-github/v88 The GitHub REST client. Hand-rolling the Actions endpoints, their pagination and their error shapes would be a permanent maintenance tax for no gain.
github.com/bradleyfalzon/ghinstallation/v2 GitHub App installation tokens: JWT signing, token caching, refresh. It is 400 lines we would otherwise get subtly wrong.
golang.org/x/crypto argon2 for password hashing. The algorithm is not something to implement.
golang.org/x/oauth2 OIDC's authorisation-code flow.
github.com/coreos/go-oidc/v3 OIDC discovery and ID-token verification, including JWKS rotation: single sign-on, and the GitHub Actions tokens Zoomies-only AI Context uploads carry. Optional features, well-maintained library.
github.com/prometheus/client_golang The metrics endpoint. The exposition format has enough edge cases that hand-writing it is a false economy.
github.com/prometheus/client_model The metric type a test reads a value back out of (dto.Metric), so a timing can be asserted on without parsing the text format. Only a test imports it, and client_golang already requires it, so it adds nothing to the build.
gopkg.in/yaml.v3 zoomies.yaml. Strict decoding turns a misspelled key into an error naming the line.
github.com/charmbracelet/huh The installer's prompts, select, input, confirm, with validation. It is what makes zoomies init feel like a product rather than a script. It brings bubbletea with it as its runtime, which is why that appears in go.mod as indirect and has no row of its own.
github.com/charmbracelet/lipgloss Styling for the installer and the CLI's table output.
golang.org/x/term Terminal detection, so the CLI and installer degrade to plain output when piped.
golang.org/x/sys The Windows half of the agent: the service control dispatcher a process started by the service manager must answer, the job object that lets killing a runner kill what it started, and the process and disk queries POSIX answers with a signal and a statfs. It was already in the tree as a transitive dependency; making it direct added no module.

Standard library for everything else: net/http, log/slog, database/sql, crypto/*, embed, os/exec, context, testing.

npm

Runtime (shipped to the browser):

Package Why
svelte The framework. Runes, no virtual DOM, and a compiled output small enough to hit the 200 KB shell budget.
@tanstack/svelte-table Headless data-grid logic (sorting, filtering, column visibility, row selection) with our own markup. Reimplementing this correctly, including keyboard navigation over a virtualised body, is weeks.
@xterm/xterm + addon-fit, addon-search, addon-webgl The log viewer. 100k lines without jank is a hard requirement, and xterm's WebGL renderer is the reason it is achievable.
@lucide/svelte Icons. Imported individually so tree-shaking keeps only what is used.
@fontsource-variable/inter, @fontsource/jetbrains-mono Self-hosted fonts. An air-gapped install must not make a third-party font request, and neither should anyone else's browser.

Build and test only:

Package Why
vite, @sveltejs/vite-plugin-svelte Build and dev server.
tailwindcss, @tailwindcss/vite Utility CSS. v4's CSS-first @theme reads our design tokens directly, so there is exactly one source of truth for a colour.
typescript, svelte-check Types, and type checking inside .svelte files.
@types/node Node's own types, for the handful of files that run under Node rather than in the browser: vite.config.ts, the Playwright specs and the test harness. Without it those files typecheck against a DOM that has no process.
openapi-typescript Generates the API client's types from api/openapi.yaml. The UI cannot drift from the API without the build failing.
@playwright/test UI tests against the real binary.
eslint, typescript-eslint, eslint-plugin-svelte, @eslint/js, globals Linting.
prettier, prettier-plugin-svelte Formatting.

Managed AI Context workflows

Package Why
repomix 1.18.1 Scans and packs repository source for AI context. The setup template embeds its integrity-locked npm dependency tree; Actions installs it in a temporary tool directory with lifecycle scripts disabled. It is not a controller or browser dependency. Source bytes come from the pinned Git commit so Repomix boundary trimming cannot change line numbers.

Runner images

Runner images also carry gawk to extract the runner's masked build console lines and discard verbose worker noise before it reaches the live relay, without adding a second runtime or buffering a build's output. It is gawk on the apt images too, in place of the mawk they ship as awk: mawk reads a pipe in large blocks, so a quiet build's lines would not reach the relay until it ended.

The docs site

docs/requirements.txt, pinned. These never reach a user's machine; they build zoomies.sh and nothing else.

Package Why
mkdocs Turns docs/ into the site, so the website is the repository's own documentation rather than a second copy of it. Pinned below 2.0 deliberately; the reason is in docs/requirements.txt.
mkdocs-material The theme. It carries the search, the light/dark palettes, the tabbed blocks the quick start uses, and the Mermaid integration below.
pymdown-extensions The fenced-block, tabbed and admonition syntax the pages are written in, including the custom mermaid fence.
Mermaid Diagrams. Material fetches it from a CDN in the reader's browser, only on a page that has one, and colours it from the palette above, so a diagram lives in the Markdown beside what it explains, renders on GitHub as well, and there is no exported image to go stale.
pillow Encodes the screenshots in docs/screenshots/ as lossless WebP, at under half the size of the PNGs Playwright takes. Only make screenshots needs it, so it is not in docs/requirements.txt and the site builds without it; the browser can write lossless WebP itself but compresses it six times worse.

Coverage reporting

Action Why
codecov/codecov-action Publishes the Go coverage report so coverage is visible in Codecov and pull requests. Pinned to a release commit; GitHub OIDC authenticates uploads without a stored secret.

The Codecov GitHub App must have access to eyupio/zoomies. CI uploads coverage.out after successful Go tests and fails visibly if the upload fails. Fork and Dependabot pull requests keep the tests and coverage summary but skip the upload because their restricted permissions do not provide the OIDC token.

make cover measures with -coverpkg=./..., and that flag is load-bearing rather than a detail. Without it Go credits a package only for the lines its own _test.go files run, and this repository deliberately tests several things through the layer above them; the controller's migration service is driven entirely by internal/api's handler tests. Those files reported 0%, which is worse than a low number: it is a dashboard pointing the next contributor at the best-covered file in the tree.

Deliberate omissions

github.com/docker/docker. The official client drags in a very large dependency tree (containerd, gRPC, OpenTelemetry, swarm types) for what amounts to a dozen JSON endpoints over a unix socket. internal/backend/dockerapi.go is a few hundred lines of net/http against the documented Engine API, and it buys Podman support almost free, because podman.sock speaks the same protocol. The one genuinely fiddly part, demultiplexing the log stream's 8-byte frame headers, is unit-tested.

A Proxmox VE client. The Go clients for it (luthermonson/go-proxmox, Telmate/proxmox-api-go) model most of a hypervisor for what Zoomies asks of one: clone a template, start it, write a file into the guest, destroy it, and poll a task. That is a dozen endpoints, and internal/provider/proxmox reaches them with the same hand-rolled net/http approach as the Engine API client above. The deciding argument is not the dependency tree, though: it is that the one classification the whole design rests on (whether a deadline or a reset happened before or after the request body went out, which is the difference between "nothing happened" and "we do not know") can only be made inside the transport, and no third-party client exposes it. A client that reports both as one error would have Zoomies retry a create that already worked, and rent a second machine nobody is tracking.

An S3 SDK. The backup remotes need four verbs (put an object, get it, list a prefix, delete a key) and signature version 4, which is forty lines of HMAC with a published definition. internal/backup/s3.go is that, against the same hand-rolled net/http approach as the two clients above, and it speaks to every implementation for the same reason a hand-rolled client speaks to Podman: ListObjectsV2 and SigV4 are what they all agree on. aws-sdk-go-v2 would bring its own configuration, credential-provider and middleware stacks (and its own release cadence and vulnerability surface) so that a controller can write one file a night; minio-go is smaller and still an order of magnitude more client than four requests deserve. The one part with an external definition, the canonical request's encoding, is unit-tested against that definition rather than against itself.

A charting library. The Overview needs sparklines and utilisation bars. Those are inline SVG paths of a few dozen lines each. The smallest credible charting library is larger than the entire rest of the app shell.

A client-side router. Seventeen routes, two of them with a parameter segment, do not need a routing library. web/src/lib/router.ts is small enough to read in one sitting and does exactly what the History API already offers.

A state-management library. Svelte 5 runes are the state management library.

Reviewing a new dependency

  1. Could the standard library, or fifty lines of our own, do it?
  2. Is it maintained, and does it have a release in the last year?
  3. What does it pull in transitively? (go mod graph, npm ls --all.)
  4. If it ships to the browser, what does it cost against the shell budget?
  5. Add a line to the right table above, in the same voice: why, not what.

A pull request that adds a dependency without a line here should be asked for one.