Skip to content

Commit b379223

Browse files
committed
docs(readme): split docs and expand operational guidance
Create dedicated docs pages for developer workflow and API endpoints, and turn README into a concise landing page with quick start. Add Dockerfile usage example, extended redeployment pitfall explanation, and under-the-hood behavior summary. Update AGENTS documentation index to include the new docs pages.
1 parent dd39994 commit b379223

4 files changed

Lines changed: 377 additions & 186 deletions

File tree

AGENTS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ wait0 is an ultra-fast cache-first HTTP reverse proxy written in Go that serves
5858
## Documentation
5959
| Document | Path | Description |
6060
|----------|------|-------------|
61-
| README | README.md | Project landing page and operational guide |
61+
| README | README.md | Project landing page and quick start |
62+
| For Developers | docs/for-developers.md | Build, config, and operations guide |
63+
| API Endpoints | docs/api-endpoints.md | Endpoint and response reference |
6264
| Docker Hub notes | DOCKERHUB.md | Alias to README for Docker Hub presentation |
6365

6466
## Build & Development Commands

README.md

Lines changed: 60 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -1,174 +1,77 @@
11
# wait0
22

3-
- GitHub: https://github.com/devforth/wait0
4-
- Docker Hub: https://hub.docker.com/r/devforth/wait0
5-
6-
Extremely fast cache-first dynamic HTTP proxy cacher with revalidate-under-the-hood strategy (SWR + warm up).
7-
8-
For extreme performance on SSR like next.js/nuxt.js or any other "slow" server side rendering.
9-
10-
## Mantras
11-
12-
- For non-bypass URLs, always serves from instant cache, which revalidates in background
13-
- Only dynamic responses are cached which have one of headers `Cache-Control: no-cache` or `Cache-Control: no-store` or `Cache-Control: max-age=0` (or their combination)
14-
- Adds headers for debug, e.g. x-wait0: hit|miss|bypass|ignore-by-cookie|ignore-by-status header to response to indicate cache status
15-
- Never changes Cache-Control or any existing headers from origin
16-
- Only GET requests are cached
17-
- Only 2xx responses are cached, if non-2xx happens, cache is instantly invalidated
18-
- Simple, fast & lightweight, one file configuration
19-
- Bypass if some cookie exists (you define name) to prevent cookie-specific issues
20-
- No query/fragment in cache key to prevent cache thrashing, only path is used as cache key.
21-
- When wait0 starts - cache is empty (even disk one).
22-
- Optionally wait0 can warm up from sitemaps so first users get hits!
3+
> Ultra-fast cache-first reverse proxy for dynamic SSR workloads.
234
5+
`wait0` serves cached HTML instantly and revalidates in the background.
6+
It is designed for Next.js/Nuxt.js and other dynamic origins where latency and origin offload matter.
247

8+
- GitHub: https://github.com/devforth/wait0
9+
- Docker Hub: https://hub.docker.com/r/devforth/wait0
2510

26-
## Usage
27-
28-
Create Dockerfile:
29-
30-
```yaml
31-
FROM devforth/wait0:latest
32-
ADD wait0.yaml /wait0.yaml
33-
EXPOSE 8082
34-
```
35-
36-
In Compose file:
11+
## Why wait0
3712

38-
```yaml
39-
services:
40-
wait0:
41-
build: .
42-
ports:
43-
- "8082:8082"
44-
```
13+
- Instant cached responses with background revalidation (SWR-like flow)
14+
- Built for dynamic pages, not static asset caching
15+
- RAM + LevelDB cache tiers for low latency and persistence
16+
- Async invalidation API with bearer auth and tag support
17+
- Warmup and sitemap discovery to reduce cold-start misses
18+
- Explicit response marker `X-Wait0` (`hit`, `miss`, `bypass`, etc.)
4519

46-
Or simply attach `wait0.yaml` via volume if you have config on server:
20+
## Quick Start
4721

48-
```yaml
49-
services:
50-
wait0:
51-
image: devforth/wait0:latest
52-
ports:
53-
- "8082:8082"
54-
volumes:
55-
- ./wait0.yaml:/wait0.yaml:ro
56-
```
57-
58-
Config `wait0.yaml`:
22+
1. Create `wait0.yaml`:
5923

6024
```yaml
6125
storage:
62-
# request path is cached as RAM->disk->origin, stops at first hit
6326
ram:
64-
max: '100m' # buffer for LRU cache, in fact RSS might be higher due to Go overhead
27+
max: '100m'
6528
disk:
6629
max: '1g'
6730

6831
server:
69-
# wait0 listens on this port and proxies to origin
7032
port: 8082
7133
origin: 'http://localhost:8080'
72-
invalidation:
73-
enabled: true
74-
queue_size: 128
75-
worker_concurrency: 4
76-
max_body_bytes: 1048576
77-
max_paths_per_request: 1024
78-
max_tags_per_request: 1024
79-
# if true, reject requests above max_paths/max_tags with 400 status code.
80-
# if false, process them and emit a warning log.
81-
hard_limits: false
82-
83-
auth:
84-
tokens:
85-
- id: backoffice
86-
# use one of token or token_env
87-
token_env: WAIT0_INVALIDATION_TOKEN
88-
scopes:
89-
- invalidation:write
9034

9135
rules:
92-
- match: PathPrefix(/api) | PathPrefix(/admin)
93-
priority: 1
94-
bypass: true
9536
- match: PathPrefix(/)
96-
priority: 2
97-
bypassWhenCookies:
98-
- sessionid
99-
# serves instantly, but if cache is stale,
100-
# it sends request to origin and updates cache
37+
priority: 1
10138
expiration: '1m'
102-
103-
# automatic scheduller which checks all known URLs in origin
104-
warmUp:
105-
runEvery: '10m'
106-
maxRequestsAtATime: 5
107-
108-
urlsDiscover:
109-
# optional: pre-discover URLs from sitemap(s) and seed them as inactive cache keys
110-
# so warmUp can fetch them without any user visiting first.
111-
# (historical typo "initalDelay" is supported)
112-
initalDelay: '20s'
113-
rediscoverEvery: '10m'
114-
sitemaps:
115-
- https://example.com/sitemap.xml
116-
117-
logging:
118-
# use this to analyze cache and RAM stats, e.g:
119-
# 2026-02-08 13:56:15 2026/02/08 11:56:15.116381 Cached: Paths: 7010, RAM usage: 6.4mb, Disk usage: 6.4mb, RSS: 136.7mb, RSSRollup: 138.1mb, RSSSplit: anon=132.1mb file=n/a shmem=n/a, GoAlloc: 73.1mb, Resp Min/avg/max 0b/0b/0b
120-
log_stats_every: '1m'
121-
# log warmup stats for each rule after a warmup batch drains:
122-
# 2026-02-08 13:56:09 2026/02/08 11:56:09.053192 Revalidated for match "PathPrefix(/)": 7010 URLs (unchanged=0 updated=2000 deleted=0 ignoredStatus=0 ignoredCC=0 errors=5010 updated+errors=7010), Took: 2.081s, RPS: 3367.34, resp time min/avg/max - 27ms/248ms/1.898s
123-
log_warmup: true
124-
# log url autodiscovery stats per-sitemap when urlsDiscover is enabled
125-
# urlsDiscover sitemap="https://.../sitemap.xml" urls=123 fit=120 ignored=3
126-
log_url_autodiscover: true
12739
```
12840
129-
## Redeployment pitfall
41+
2. Run `wait0` via Docker:
13042

131-
In Nuxt/Next and similar SSR setups, HTML pages often reference versioned static assets (usually hashed filenames). After a redeploy those filenames can change, and you typically **should not** keep old static files around.
132-
133-
If old HTML is still cached in wait0, it may reference static files that are no longer available (or not yet present in a given CDN/geo). This can cause broken pages after redeploy.
134-
135-
To avoid this, invalidate all wait0 caches, by enforcing a docker service restart:
136-
137-
e.g. in compose:
138-
139-
```yaml
140-
docker compose restart wait0
43+
```bash
44+
docker run --rm -p 8082:8082 \
45+
-v "$(pwd)/wait0.yaml:/wait0.yaml:ro" \
46+
devforth/wait0:latest
14147
```
14248

143-
Both RAM and disk caches are cleared on restart, so all stale HTML is removed and new HTML with correct static asset references is cached.
49+
3. Alternative: build a tiny wrapper image with your config baked in.
14450

145-
If you need to pre-warm cache after redeploy, it is recommended to use a sitemap.
51+
`Dockerfile`:
14652

147-
## Invalidation API
53+
```dockerfile
54+
FROM devforth/wait0:latest
55+
COPY wait0.yaml /wait0.yaml
56+
EXPOSE 8082
57+
```
14858

149-
`wait0` exposes an authenticated async invalidation endpoint:
59+
Build and run:
15060

151-
- `POST /wait0/invalidate`
152-
- `Authorization: Bearer <token>`
153-
- `Content-Type: application/json`
61+
```bash
62+
docker build -t my-wait0:latest .
63+
docker run --rm -p 8082:8082 my-wait0:latest
64+
```
15465

155-
Request body:
66+
4. Send requests through wait0:
15667

157-
```json
158-
{
159-
"paths": ["/products/123", "/"],
160-
"tags": ["product:123", "homepage"]
161-
}
68+
```bash
69+
curl -i http://localhost:8082/
16270
```
16371

164-
Notes:
165-
- `paths`, `tags`, or both can be provided.
166-
- Path inputs are normalized to wait0 cache keys (path-only; query/fragment are ignored).
167-
- Tag invalidation matches cached entries where origin response headers contain `X-Wait0-Tag` (supports repeated and comma-separated values).
168-
- Invalidation runs in the background and returns `202 Accepted` immediately.
169-
- After invalidation, wait0 automatically re-crawls affected paths to refill cache with fresh origin responses.
72+
First request is usually `X-Wait0: miss`, subsequent requests are usually `X-Wait0: hit`.
17073

171-
Example:
74+
## Invalidation Example
17275

17376
```bash
17477
curl -i \
@@ -178,62 +81,34 @@ curl -i \
17881
-d '{"paths":["/products/123"],"tags":["product:123"]}'
17982
```
18083

84+
Returns `202 Accepted` and processes invalidation asynchronously.
85+
Authorization is scope-based (`invalidation:write`) to support least-privilege tokens and future API growth without changing token model.
18186

182-
## Under the hood
87+
## Documentation
18388

184-
- First it checks RAM-cache for the URL, if exists, serves it instantly and revalidates in background if revalidate condition is met.
185-
- Revalidated content calculates quick hash and check hash in storage, if hash is different, updates cache with new content and hash (read-safe, write-atomic)
186-
- If RAM storage is full, it moves 10% of least recently used items to disk storage (based on leveldb) and removes them from RAM, if disk storage is full, it evicts 10% of least recently used items - deletes them from disk storage. Then it puts new item to RAM storage (by prechecking if it can fit in RAM, if not, it directly puts to disk storage if it can fit)
187-
- if some storage is overflowin it drops log warning, not ofter then once per minute.
89+
| Guide | Description |
90+
|-------|-------------|
91+
| [For Developers](docs/for-developers.md) | Build/test commands, config reference, runtime options |
92+
| [API Endpoints](docs/api-endpoints.md) | Proxy behavior, invalidation API, schemas, status codes |
18893

189-
# For developers
94+
## Redeploy Note
19095

191-
How to run:
96+
SSR frameworks like Next.js/Nuxt usually output versioned static asset names (for example, `app.abc123.js`).
97+
After a redeploy, HTML references switch to new filenames (for example, `app.def456.js`), and old files are commonly removed.
19298

193-
```bash
194-
make test
195-
go run ./cmd/wait0 -config ./debug/wait0.yaml
196-
```
197-
198-
Testing and coverage gates:
99+
If stale HTML is still cached, clients can receive pages that point to missing assets. Typical symptoms are broken UI, hydration failures, and partial renders.
199100

200-
```bash
201-
# show all available targets
202-
make help
203-
204-
# full local quality gate (lint + tests + race + coverage threshold + build)
205-
make ci-check
206-
207-
# common commands
208-
make build
209-
make test
210-
make test-race
211-
make lint
212-
make fmt
213-
make coverage
214-
make dev
215-
```
101+
To reduce this risk, `wait0` clears disk cache on startup by default (`WAIT0_INVALIDATE_DISK_CACHE_ON_START=true`).
102+
That behavior makes rollout safer because old HTML is not reused across deploy generations.
216103

217-
Coverage policy:
218-
- `internal/wait0` minimum coverage threshold: `80%`
219-
- Explicit exclusions from threshold calculation:
220-
- `internal/wait0/stats/proc_linux.go`
221-
- `internal/wait0/stats/proc_other.go`
104+
If you do not restart between deploys, proactively refresh cache using invalidation and warmup for critical routes.
222105

223-
Docker quick commands:
106+
## Under the Hood
224107

225-
```bash
226-
make docker-build
227-
make docker-run
228-
make docker-logs
229-
make docker-stop
230-
```
231-
232-
Debug stack (origin + wait0):
233-
234-
```bash
235-
bash debug/debug.sh
236-
# get origin and wait0 logs:
237-
curl -i http://localhost:8080/xx
238-
curl -i http://localhost:8082/xx
239-
```
108+
- Request pipeline: RAM cache -> disk cache -> origin.
109+
- Cache key is path-only (query and fragment are ignored for cache identity).
110+
- Only `GET` requests are cache candidates.
111+
- Only origin `2xx` responses are stored.
112+
- For stale entries (rule `expiration`), wait0 serves cached response immediately and revalidates asynchronously.
113+
- For origin non-`2xx`, wait0 skips caching and evicts any existing key for that path.
114+
- Invalidation is asynchronous: accept request -> resolve keys by `paths` and `tags` -> delete keys -> recrawl in background.

0 commit comments

Comments
 (0)