From ae758550a855f6a9507df08075475cb87cb67086 Mon Sep 17 00:00:00 2001 From: ZDDC Date: Mon, 4 May 2026 07:48:53 -0500 Subject: [PATCH] docs: surface recent server features in README + AGENTS zddc/README.md and AGENTS.md hadn't caught up with the loading- efficiency + ops-hygiene work. Add coverage for: - ETag + max-age=0 on embedded tool HTMLs (304 on revalidation) - gzip compression middleware (75% size reduction on bodies > 1 KB) - public landing page semantics (root bypasses dir-level ACL; per-project filtering still hides hidden projects) - file-based audit log (default-on, auto-mkdir, hostname-tagged filename + record field, lumberjack-rotated) - HTTP timeouts (slowloris-resistant) Adds ZDDC_ACCESS_LOG row to both env-var tables. --- AGENTS.md | 6 ++++++ zddc/README.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index db86021..05fe7f0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -392,6 +392,7 @@ ZDDC_ROOT=/path/to/your/archive ZDDC_TLS_CERT=none ZDDC_ADDR=:8080 \ | `ZDDC_INDEX_PATH` | `.archive` | Virtual archive index URL segment | | `ZDDC_LOG_LEVEL` | `info` | Logging verbosity | | `ZDDC_CORS_ORIGIN` | `https://zddc.varasys.io` | Comma-separated CORS allowlist; empty value disables CORS. Default lets tools served from zddc.varasys.io call back into a customer-deployed server. | +| `ZDDC_ACCESS_LOG` | `/.zddc.d/logs/access-.log` | JSON-line audit log (lumberjack-rotated, 100 MB / 10 backups / 90 days, gzipped). Server auto-mkdirs the parent. Set explicitly to empty (`--access-log=`) to disable. Per-host filename + `host` field in every record so multi-replica deployments writing to the same `.zddc.d/` dir disambiguate cleanly. | ### Release tagging @@ -423,3 +424,8 @@ local path that fails loudly and visibly on the developer's terminal. - `.zddc` schema also supports a top-level `admins:` glob list, peer to `acl.allow`/`acl.deny`. Honored **only** at the root `.zddc` (subdir `admins` entries are ignored to prevent privilege escalation via subtree write access). Drives the built-in debug dashboard at `/.admin/` (sub-routes: `/whoami`, `/config`, `/logs`); non-admin requests get 404 so the page is invisible. See `zddc/README.md` § "Admin Debug Page". - `GET /.auth/admin` is a **forward_auth target** for upstream proxies — returns 200 if the request's `X-Auth-Request-Email` is in the root `.zddc` `admins:` list, 403 otherwise. No body, no UI. Used by the dev-shell pod's Caddy to gate `/devshell/*` (code-server) on root-admin status without code-server learning about auth. zddc-server's own routes use the regular `.zddc` cascade ACL — they do NOT go through this endpoint. - **Reserved entry prefixes** under `ZDDC_ROOT`: `.`-prefixed entries are excluded from listings AND 404 on direct fetch (only `.archive` and `.admin` are exempt) — for invisible side-state like dev-shell home dirs. `_`-prefixed entries are excluded from listings only — for operator scaffolding like the `_template/` directory created by the self-contained install snippet, still reachable by direct URL. Drop side-state under `_` if it should be linkable; under `.` if it should be unreachable. +- **Caching on embedded tool HTMLs** (landing, browse served at `/`, plus the five canonical app HTMLs at `/.html`): `Cache-Control: public, max-age=0, must-revalidate` + content-addressed `ETag` (sha256 hex prefix). Browser revalidates on every load; matching ETag returns `304 Not Modified` with empty body. ETag changes only when the binary is redeployed (computed once at startup from `EmbeddedBytes` + `BuildVer`, memoized). +- **Compression**: gzip middleware (`github.com/klauspost/compress/gzhttp`) wraps the entire mux. Skipped for bodies under 1 KB and for 304 responses. Roughly 75% size reduction on tool HTMLs and JSON listings. +- **Public landing page**: `GET /` (HTML or JSON) bypasses the directory-level ACL gate so anonymous callers see the project picker. Per-project filtering inside `fs.ListDirectory` still hides projects the caller can't reach. Subdirectory ACL gates remain in force. +- **Audit log**: every request is mirrored to a JSON-line file under `/.zddc.d/logs/access-.log` (configurable via `--access-log` / `ZDDC_ACCESS_LOG`, opt out with empty). Lumberjack rotation (100 MB / 10 backups / 90 days, gzip). Hostname is in both the filename and every record's `host` field — multi-replica deployments sharing one `.zddc.d/` dir disambiguate cleanly. +- **HTTP timeouts**: `ReadHeaderTimeout: 10s, ReadTimeout: 60s, WriteTimeout: 60s, IdleTimeout: 120s`. Slowloris-resistant; legit traffic completes in milliseconds even with gzip. diff --git a/zddc/README.md b/zddc/README.md index f2ce9cb..afafc48 100644 --- a/zddc/README.md +++ b/zddc/README.md @@ -6,10 +6,15 @@ A purpose-built HTTPS file server for ZDDC document archives. Designed to replac ## Features - **High-performance static file serving** — ETag, conditional GET, Cache-Control +- **ETag on embedded tool HTMLs** — sha256 of the embedded bytes; repeat loads return 304 Not Modified instead of re-shipping 50–920 KB +- **gzip compression middleware** — wraps the entire mux; ~75% size reduction on tool HTMLs and JSON listings (skips bodies under 1 KB) +- **Public landing page** — root `/` is reachable by anyone, including anonymous; per-project ACL filtering still hides projects the caller can't reach - **Cascading `.zddc` ACL** — email-based allow/deny lists evaluated bottom-up from requested directory to root - **Caddy-compatible JSON listings** — the Archive Browser works without modification - **Virtual `.archive` index** — resolve the earliest revision of any tracked document by URL - **Filesystem watcher** — archive index updates automatically when files change +- **File-based audit log** — JSON-line access log tee'd to `/.zddc.d/logs/access-.log` by default, rotated by lumberjack (100 MB / 10 backups / 90 days, gzipped) +- **Conservative HTTP timeouts** — slowloris-resistant; 10 s read-header, 60 s read+write, 120 s idle - **Flexible TLS modes** — self-signed, real certificates, or plain HTTP - **Single static binary** — CGO-free, no runtime dependencies; cross-compiled to Linux/macOS/Windows @@ -56,6 +61,7 @@ There is no Containerfile / Dockerfile / compose file in this repo. Two ways to | `ZDDC_INDEX_PATH` | `.archive` | URL path segment name for the virtual archive index | | `ZDDC_EMAIL_HEADER` | `X-Auth-Request-Email` | HTTP request header containing the authenticated user's email (the oauth2-proxy / nginx auth-request convention) | | `ZDDC_CORS_ORIGIN` | `https://zddc.varasys.io` | Comma-separated allowlist of origins permitted to make cross-origin requests. Empty value disables CORS entirely. Default lets ZDDC tools served from `zddc.varasys.io` (e.g. via the bootstrap pattern) call back into your deployed server. | +| `ZDDC_ACCESS_LOG` | `/.zddc.d/logs/access-.log` | Tee'd structured access log. Auto-mkdir on first run. Empty value (set explicitly with `--access-log=`) disables file logging; stderr stream stays. Per-host filenames let multiple replicas write to the same `.zddc.d/` directory without collision; every record carries a `host` field for downstream aggregation. | `ZDDC_TLS_CERT=none` disables TLS entirely (plain HTTP). Both cert and key must be set together when using real certificates.