|
| 1 | +# Architecture |
| 2 | + |
| 3 | +## Why this exists (plain English) |
| 4 | + |
| 5 | +Running multiple local projects at the same time is messy when everything wants the same ports and |
| 6 | +"localhost". This CLI gives each repo its own isolated network and stable HTTPS hostnames so you can: |
| 7 | + |
| 8 | +- run many apps concurrently without port juggling |
| 9 | +- keep service defaults (Postgres on 5432, Redis on 6379) inside each project |
| 10 | +- access every app via predictable `https://<project>.hack` |
| 11 | +- get fast local logs plus searchable history |
| 12 | + |
| 13 | +## System overview |
| 14 | + |
| 15 | +`hack` is a Bun CLI that writes per-project Compose files under `.hack/` and manages a machine-wide |
| 16 | +proxy + logging stack under `~/.hack/`. Caddy routes `*.hack` based on container labels and the |
| 17 | +logging stack (Alloy + Loki + Grafana) captures Docker Compose logs. |
| 18 | + |
| 19 | +```mermaid |
| 20 | +flowchart LR |
| 21 | + CLI["hack CLI (Bun)"] |
| 22 | + Browser["Browser"] |
| 23 | +
|
| 24 | + subgraph Host["Developer machine"] |
| 25 | + subgraph Global["Global infra (~/.hack)"] |
| 26 | + Caddy["Caddy docker-proxy"] |
| 27 | + Loki["Loki"] |
| 28 | + Grafana["Grafana"] |
| 29 | + Alloy["Alloy (Docker logs)"] |
| 30 | + end |
| 31 | +
|
| 32 | + subgraph Project["Project repo (.hack)"] |
| 33 | + Compose["docker-compose.yml"] |
| 34 | + Services["Service containers"] |
| 35 | + end |
| 36 | + end |
| 37 | +
|
| 38 | + CLI -->|"hack global install/up"| Global |
| 39 | + CLI -->|"hack init/up"| Compose |
| 40 | + Compose -->|"docker compose"| Services |
| 41 | +
|
| 42 | + Services -->|"Docker labels"| Caddy |
| 43 | + Services -->|"container logs"| Alloy |
| 44 | + Alloy --> Loki --> Grafana |
| 45 | +
|
| 46 | + Caddy -->|"https://*.hack"| Browser |
| 47 | + Grafana -->|"https://logs.hack"| Browser |
| 48 | +``` |
| 49 | + |
| 50 | +## Global vs project scope |
| 51 | + |
| 52 | +- Global scope (`~/.hack`) |
| 53 | + - Caddy proxy listens on 80/443 and routes `https://*.hack` via Docker labels |
| 54 | + - Logging stack captures all Compose logs (Alloy → Loki → Grafana) |
| 55 | + - Networks: `hack-dev` (ingress) and `hack-logging` |
| 56 | + |
| 57 | +- Project scope (`.hack`) |
| 58 | + - `docker-compose.yml` defines services and optional Caddy labels |
| 59 | + - `hack.config.json` stores project name, dev host, log preferences, OAuth alias |
| 60 | + |
| 61 | +## Lifecycle (init → up → logs) |
| 62 | + |
| 63 | +```mermaid |
| 64 | +sequenceDiagram |
| 65 | + participant User |
| 66 | + participant CLI as hack |
| 67 | + participant Docker |
| 68 | + participant Caddy |
| 69 | + participant Loki |
| 70 | +
|
| 71 | + User->>CLI: hack init |
| 72 | + CLI->>Docker: create .hack/docker-compose.yml |
| 73 | + CLI-->>User: wrote .hack/ files |
| 74 | +
|
| 75 | + User->>CLI: hack up |
| 76 | + CLI->>Docker: docker compose up |
| 77 | + Docker->>Caddy: read labels for routing |
| 78 | + Docker->>Loki: logs via Alloy |
| 79 | +
|
| 80 | + User->>CLI: hack logs |
| 81 | + alt compose backend |
| 82 | + CLI->>Docker: docker compose logs |
| 83 | + else loki backend |
| 84 | + CLI->>Loki: query/tail LogQL |
| 85 | + end |
| 86 | +``` |
| 87 | + |
| 88 | +## Logging pipeline |
| 89 | + |
| 90 | +```mermaid |
| 91 | +flowchart LR |
| 92 | + Containers["Compose containers"] -->|"stdout/stderr"| Alloy |
| 93 | + Alloy -->|"push"| Loki |
| 94 | + Loki -->|"query"| CLI["hack logs --loki"] |
| 95 | + Loki -->|"Explore"| Grafana |
| 96 | + CLI -->|"pretty output"| Terminal |
| 97 | +``` |
| 98 | + |
| 99 | +## Files and directories |
| 100 | + |
| 101 | +- `~/.hack/` |
| 102 | + - `caddy/docker-compose.yml` |
| 103 | + - `logging/docker-compose.yml` |
| 104 | + - `logging/alloy.alloy` |
| 105 | + - `logging/loki.yaml` |
| 106 | + - `logging/grafana/...` |
| 107 | + - `schemas/hack.config.schema.json` |
| 108 | + - `schemas/hack.branches.schema.json` |
| 109 | + - `projects.json` (best-effort registry) |
| 110 | + |
| 111 | +- `<repo>/.hack/` |
| 112 | + - `docker-compose.yml` |
| 113 | + - `hack.config.json` |
| 114 | + - `hack.branches.json` (optional) |
| 115 | + |
| 116 | +## Key design choices |
| 117 | + |
| 118 | +- Docker Compose is the execution substrate for predictability and portability. |
| 119 | +- Caddy routes by container label so there is no per-repo reverse proxy config. |
| 120 | +- Logs default to `docker compose logs` for speed, with Loki for history and filtering. |
| 121 | +- Config lives alongside each repo in `.hack/` to keep repos isolated and portable. |
| 122 | + |
| 123 | +## Extension points (future-friendly) |
| 124 | + |
| 125 | +- Log backends could be abstracted (`compose` / `loki` today). |
| 126 | +- A structured JSON output mode would enable UI and MCP integration. |
| 127 | +- A lightweight daemon could subscribe to Docker events for near-real-time UI updates. |
0 commit comments