@@ -13,17 +13,22 @@ Running multiple local projects at the same time is messy when everything wants
1313## System overview
1414
1515` 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.
16+ proxy, DNS helpers, and logging stack under ` ~/.hack/ ` .
17+
18+ - ** Caddy** (docker-proxy) routes ` *.hack ` based on container labels.
19+ - ** CoreDNS** resolves ` *.hack ` inside containers to the Caddy IP.
20+ - ** Alloy + Loki + Grafana** capture logs and provide history.
21+ - ** Schemas** are served by Caddy at ` https://schemas.hack ` .
1822
1923``` mermaid
20- flowchart LR
24+ graph LR
2125 CLI["hack CLI (Bun)"]
2226 Browser["Browser"]
2327
2428 subgraph Host["Developer machine"]
2529 subgraph Global["Global infra (~/.hack)"]
2630 Caddy["Caddy docker-proxy"]
31+ CoreDNS["CoreDNS"]
2732 Loki["Loki"]
2833 Grafana["Grafana"]
2934 Alloy["Alloy (Docker logs)"]
@@ -43,20 +48,46 @@ flowchart LR
4348 Services -->|"container logs"| Alloy
4449 Alloy --> Loki --> Grafana
4550
51+ CoreDNS -->|"*.hack → Caddy IP"| Services
4652 Caddy -->|"https://*.hack"| Browser
4753 Grafana -->|"https://logs.hack"| Browser
54+ Caddy -->|"https://schemas.hack"| Browser
4855```
4956
5057## Global vs project scope
5158
5259- 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)
60+ - Caddy proxy on 80/443 (routes via Docker labels)
61+ - CoreDNS for container DNS (` *.hack ` → Caddy)
62+ - macOS DNS helper: dnsmasq + ` /etc/resolver ` for ` *.hack ` → ` 127.0.0.1 `
63+ - Logging stack (Alloy → Loki → Grafana)
64+ - Schemas hosted under ` https://schemas.hack `
5565 - Networks: ` hack-dev ` (ingress) and ` hack-logging `
5666
5767- Project scope (` .hack ` )
58- - ` docker-compose.yml ` defines services and optional Caddy labels
68+ - ` docker-compose.yml ` defines services + Caddy labels
5969 - ` hack.config.json ` stores project name, dev host, log preferences, OAuth alias
70+ - Optional overrides:
71+ - ` .internal/compose.override.yml ` (internal DNS/TLS injection)
72+ - ` .branch/compose.<branch>.override.yml ` (branch builds)
73+
74+ ## Internal DNS + TLS (containers)
75+
76+ When ` internal.dns ` / ` internal.tls ` are enabled, ` hack up ` writes a Compose override that:
77+
78+ - sets each service’s DNS to the CoreDNS container
79+ - mounts Caddy’s local CA cert into each service
80+ - sets common SSL env vars (Node, curl, git, requests)
81+
82+ This lets containers use the same ` https://*.hack ` hostnames as the host machine.
83+
84+ ``` mermaid
85+ graph LR
86+ Service["Project container"] -->|"DNS query *.hack"| CoreDNS["CoreDNS (hack-dev)"]
87+ CoreDNS -->|"A record = Caddy IP"| Service
88+ Service -->|"HTTPS request"| Caddy["Caddy docker-proxy"]
89+ Caddy -->|"Routes by labels"| Upstream["Service upstream"]
90+ ```
6091
6192## Lifecycle (init → up → logs)
6293
@@ -87,35 +118,57 @@ sequenceDiagram
87118
88119## Logging pipeline
89120
121+ ` hack logs ` supports two backends:
122+
123+ - ** compose** : fast, direct ` docker compose logs `
124+ - ** loki** : searchable history + LogQL filters
125+
126+ NDJSON streaming (` hack logs --json ` ) emits ` start ` , ` log ` , and ` end ` events for MCP/TUI consumers.
127+
90128``` mermaid
91- flowchart LR
129+ graph LR
92130 Containers["Compose containers"] -->|"stdout/stderr"| Alloy
93131 Alloy -->|"push"| Loki
94132 Loki -->|"query"| CLI["hack logs --loki"]
133+ CLI -->|"NDJSON/pretty"| Terminal
95134 Loki -->|"Explore"| Grafana
96- CLI -->|"pretty output"| Terminal
97135```
98136
137+ ## Branch builds
138+
139+ ` --branch <name> ` generates a per-branch Compose override that:
140+
141+ - prefixes hostnames (e.g. ` api.myapp.hack ` → ` api.<branch>.myapp.hack ` )
142+ - prefixes the Compose project name
143+
144+ This enables parallel worktrees without port or hostname collisions.
145+
99146## Files and directories
100147
101148- ` ~/.hack/ `
102149 - ` caddy/docker-compose.yml `
150+ - ` caddy/Corefile ` (CoreDNS config)
103151 - ` logging/docker-compose.yml `
104152 - ` logging/alloy.alloy `
105153 - ` logging/loki.yaml `
106154 - ` logging/grafana/... `
107155 - ` schemas/hack.config.schema.json `
108156 - ` schemas/hack.branches.schema.json `
157+ - ` certs/ ` (mkcert output for non-Caddy services)
109158 - ` projects.json ` (best-effort registry)
110159
111160- ` <repo>/.hack/ `
112161 - ` docker-compose.yml `
113162 - ` hack.config.json `
114163 - ` hack.branches.json ` (optional)
164+ - ` .internal/compose.override.yml `
165+ - ` .branch/compose.<branch>.override.yml `
115166
116167## Key design choices
117168
118169- Docker Compose is the execution substrate for predictability and portability.
119170- 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.
171+ - CoreDNS gives containers the same ` *.hack ` namespace as the host.
172+ - Logs default to ` docker compose logs ` for speed; Loki is used for history and filtering.
173+ - Config lives alongside each repo in ` .hack/ ` to keep repos isolated and portable.
174+ - Schemas are generated from templates and served locally for editor validation.
0 commit comments