Architecture
Design decisions and technical implementation
Gibil is a thin orchestration layer between you and Hetzner Cloud. There's no custom infrastructure, no proprietary runtime, no vendor lock-in. It's a CLI that makes the right API calls in the right order.
Design decisions
Raw fetch over Hetzner SDK
Zero extra dependencies, full control over the HTTP layer, easy to mock in tests. The entire Hetzner integration is one file with a private request<T>() method.
CloudProvider interface
The CloudProvider interface is the abstraction boundary. Today it wraps Hetzner. Tomorrow it can wrap Fly.io, DigitalOcean, or AWS — implement the same interface, swap the provider.
Cloud-init over SSH provisioning
The server configures itself on boot. Gibil generates a cloud-init script with your runtime, services, and tasks — then hands it to Hetzner. No SSH connection needed during setup, no fragile multi-step provisioning over the network.
Auth is optional
The CLI works with just a Hetzner token. Gibil's auth layer (Supabase + Stripe) adds metering and billing for the managed service, but it's entirely optional. BYOH users never touch it.
InstanceStore with dependency injection
Instance metadata is stored as JSON files in ~/.gibil/instances/. The store accepts a baseDir parameter — production uses ~/.gibil, tests use a temp directory. No global state, no singletons.
Tech stack
| Component | Technology | Why |
|---|---|---|
| CLI framework | Commander.js | Standard, well-documented |
| Build | tsup | Fast, zero-config ESM bundling |
| Language | TypeScript (strict) | Type safety without ceremony |
| SSH | ssh2 | Native Node.js SSH, no shelling out |
| Config | YAML (.gibil.yml) | Human-readable, version-controllable |
| Tests | Vitest | Fast, ESM-native, good DX |
| Cloud | Hetzner Cloud API | Cheapest full VMs, clean API |
| Auth/billing | Supabase + Stripe | Optional, no extra hosting needed |
Project structure
src/
cli/
commands/ # One file per command (create, ssh, run, destroy, ...)
index.ts # Entry point — registers all commands with Commander
providers/ # CloudProvider implementations (Hetzner)
config/ # YAML parser + cloud-init script generator
ssh/ # Key generation + remote command execution
types/ # Shared TypeScript types (index.ts)
utils/ # Logger, store, auth, paths, validate, randomEach command is a single file with a registerXCommand(program) function. The provider is injected, not imported directly. Utilities are small and single-purpose.
Next steps
- How It Works — the server lifecycle
- CLI Reference — command documentation