From adb690439711f8fc967575d1f551afe64372f2ec Mon Sep 17 00:00:00 2001 From: ZDDC Date: Fri, 1 May 2026 15:25:57 -0500 Subject: [PATCH] docs: rewrite for embedded + cascade install model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates every repo doc to reflect the simplified install model: - Local install is just a download from /releases/. - Server install is just running zddc-server (current-stable HTMLs embedded at compile time). - Customize via .zddc apps: cascade entries (channel/version/URL/path, with default + per-app composition); editor at /.profile/zddc/. Removes references to the old install scripts, level-1/level-2 stubs, admin UI at /.profile/apps, SHA-256 verification, TOFU writes, refresh worker, and ZDDC_APPS_* env vars. zddc/README.md: replaces "Landing Page and Tool Install" section with "Apps: virtual tool HTMLs" — covers the folder-name availability rules, the resolution chain (real-file override / cascade / embedded), spec syntax cheat sheet, cache layout under /_app/, the ?v= cache-only override, and the X-ZDDC-Source response header. ARCHITECTURE.md: install-distribution-model section rewritten to describe the embed-first / cascade-override model with one canonical example. AGENTS.md, CLAUDE.md: short-form summaries pointing at the same model. README.md: install bullet rewritten. Co-Authored-By: Claude Opus 4.7 (1M context) --- AGENTS.md | 45 +++++++++++---------- ARCHITECTURE.md | 47 ++++++++++++++-------- CLAUDE.md | 4 +- README.md | 2 +- zddc/README.md | 105 ++++++++++++++++++++++++++++-------------------- 5 files changed, 118 insertions(+), 85 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7e74f91..3a4eb22 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,7 +3,7 @@ ## Commands ```bash -# Build all tools (writes to dist/ only; also regenerates website/releases/index.html + bootstrap stubs) +# Build all tools (writes to dist/ only; also regenerates website/releases/index.html) sh build.sh # Build single tool @@ -55,8 +55,7 @@ shared/ sourced by every tool's build.sh via: . "$root_dir/../shared/build-lib.sh" website/ - index.html hand-edited intro page (root URL) - install.sh → ../bootstrap/install.sh symlink so the upstream serves /install.sh + index.html hand-edited intro page + install snippets (root URL) releases/ index.html versions index, regenerated by build.sh from filesystem scan _v.html real per-version files (committed, immutable) @@ -65,17 +64,6 @@ website/ _stable.html -> ... symlink: current stable _beta.html -> ... symlink to stable (or real bytes if active beta) _alpha.html -> ... symlink to beta/stable (or real bytes if active alpha) - bootstrap/ - level1/.html same-origin level-1 stubs (4 tools, no landing) - track-stable/.html level-2 stubs that track the current-stable channel - track-alpha/.html level-2 stubs that track the alpha channel - track-beta/.html level-2 stubs that track the beta channel - -bootstrap/ - level1.html.tmpl per-project bootstrap template (relative ../.html) - level2.html.tmpl level-2 channel-tracking bootstrap template - install.sh unified bootstrap script (modes: copy / track; channels: stable/beta/alpha/) - README.md install / channel / pin docs helm/ zddc-server-prod/ production-shaped Helm chart (compiles from source via init container) @@ -198,7 +186,7 @@ After cutting a stable release, `git push origin ` to publish the tag. `git No `$CODEBERG_TOKEN` is needed for HTML-tool releases — they never touch Codeberg. (`zddc/release.sh` for zddc-server stable cuts still requires it for binary uploads.) -`website/index.html` (the root URL of zddc.varasys.io) is **hand-edited static content**, not built by `landing/build.sh`. The landing tool's release file is `website/releases/landing_v.html`; the unified install script (`bootstrap/install.sh`) curls the chosen channel's `landing_.html` to `/index.html` at customer-deployment time. +`website/index.html` (the root URL of zddc.varasys.io) is **hand-edited static content**, not built by `landing/build.sh`. Its "Install on your server" section points operators at two paths: local download (per-tool links to `/releases/`) and `zddc-server` install (the binary has the current-stable build of every tool baked in via `//go:embed`). The landing tool's release file is `website/releases/landing_v.html`; on a `zddc-server` deployment, the embedded copy is served at the root URL by default, with operators able to override via `.zddc apps:` entries or by dropping a real `index.html`. ### Channel discipline (MUST rules) @@ -207,7 +195,7 @@ The build system does not enforce these. Treating channels carelessly defeats th 1. **Stable doesn't regress.** No known-broken features that worked in the previous stable. If you ship `v0.0.5` with a bug, the path forward is `v0.0.6` with a fix — never edit a previously-published `_v0.0.5.html` in place. Stable per-version files are immutable. 2. **Coordinated minor/major bumps.** When any tool needs a minor or major bump, all five tools cut at the same time even if patches differ. Per-tool patches stay independent. This is a release-time process rule, not enforced by tooling. 3. **No backports.** Don't try to patch an older stable version. Always cut a new stable at a higher version. Users pinned to the old version stay pinned by choice; they can move forward when they want. -4. **Alpha and beta are mutable.** Document this anywhere you invite users to test them. Pinning a deployment to a channel mirror means it gets rebuilt without notice. For something stable, pin to a per-version URL (`?v=0.0.5` or the `_v0.0.5.html` mirror). +4. **Alpha and beta are mutable.** Document this anywhere you invite users to test them. Pinning a deployment to a channel mirror means it gets rebuilt without notice. For something stable, pin to a per-version URL (the `_v0.0.5.html` file). 5. **Cascade is automatic.** A stable cut resets beta and alpha symlinks → stable. A beta cut resets alpha → beta. So "no active beta" silently shows current stable and "no active alpha" silently shows current beta. Operators don't need to run a freshen step after a stable release; the cascade handles it. 6. **Hotfix path.** For critical bugs: fix on `main`, cut a new stable. Tag the commit message `fix:` or include "hotfix" so intent is visible in `git log`. 7. **Beta soak before promoting (recommended).** Give a beta a few days of exposure before cutting the same code as stable. Not enforced; use judgment for trivial changes. @@ -230,16 +218,29 @@ What it does: The build pipeline used is the one **at the tag**, not on `main`. That is intentional (pure reproducibility). If you have made build-system improvements since stable was cut and want the freshen to use them, cut a new stable first. -### Bootstrap stubs +### Install model -`build.sh` regenerates `website/bootstrap/` on every invocation: +No install script. Two paths: -- `bootstrap/level1/.html` — 4 same-origin level-1 stubs (archive, transmittal, classifier, mdedit; landing has no level-1 stub since it only lives at deployment root). -- `bootstrap/track-{alpha,beta,stable}/.html` — 5 level-2 stubs per channel that fetch `/releases/_.html` directly. Static-file resolution end-to-end via the symlink chain — no JS indirection, no manifest lookup. +- **Local** — download a tool `.html` from `https://zddc.varasys.io/releases/` and open it. Done. +- **Server** (`zddc-server`) — every tool is `//go:embed`'d into the binary at compile time (the current-stable build). The server virtually serves them at folder-name-driven paths: + - `archive.html` at every directory (multi-project, project, archive, vendor levels) + - `classifier.html` in any `Incoming`/`Working`/`Staging` directory and its subtree + - `mdedit.html` in any `Working` directory and its subtree + - `transmittal.html` in any `Staging` directory and its subtree + - `index.html` (landing) only at the deployment root -End users install via the unified `bootstrap/install.sh` script (also served at `https://zddc.varasys.io/install.sh`). The home page's "Install on your server" section prints one-liners that invoke the script with the appropriate `--mode` / `--channel` / `--target` flags. + See `internal/apps/availability.go`. Outside these locations, requesting `.html` returns 404 (just like any other missing file). -See `bootstrap/README.md` for the install / pin / audit story. +To override at any level, either: +1. Drop a real `.html` file at the path → static handler serves it (highest priority). +2. Write an `apps:` entry in any `.zddc` along the path. Spec is one of `stable`/`beta`/`alpha`/`v0.0.4`/`v0.0`/`v0`/full URL/local path. Closer-to-leaf entries win. + +URL sources fetch once and cache forever in `/_app//`. To force a re-fetch, delete the cache file. No background refresh, no SHA-256 verification, no admin UI. If a configured URL fetch fails, the server falls back to the embedded copy and emits a one-time WARN log. + +Operators audit by reading the `X-ZDDC-Source` response header: `fetch:URL` / `cache:URL` / `path:/abs` / `embedded:@`. Direct URL access to `/_app/...` is blocked at the dispatch layer. + +**Runtime mode detection** in archive is independent of install: it auto-detects multi-project / project-root / in-archive from `?projects=` plus folder shape. The other tools don't care where they live. ### Worktrees diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b2eb1e7..6901047 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -37,8 +37,7 @@ Website files (what `zddc.varasys.io` serves) — committed in this repo as stat ``` website/ - index.html # hand-edited intro page (root URL) - install.sh → ../bootstrap/install.sh # symlink so the upstream serves /install.sh + index.html # hand-edited intro page + install snippets (root URL) releases/ index.html # versions index, regenerated by build.sh from filesystem scan _v.html # real per-version files (committed, immutable) @@ -47,9 +46,6 @@ website/ _stable.html → ... # symlink: current stable _beta.html → ... # symlink to stable (or real bytes when active beta dev) _alpha.html → ... # symlink to beta/stable (or real bytes when active alpha dev) - bootstrap/ - level1/.html # same-origin stubs for project subdirectories - track-{alpha,beta,stable}/ # per-channel level-2 stubs (5 tools each) ``` Every URL under `/releases/` resolves directly via the symlink chain — no `manifest.json`, no Caddy regex-rewrite, no JavaScript indirection, no Codeberg proxy. Caddy serves these as plain static files. The Docker-tag pattern: `:1.2.3` is pinned, `:1.2` floats, `:1` floats further, `:stable` floats furthest, and `:beta` / `:alpha` are mutable channel mirrors that overwrite in place. @@ -68,16 +64,16 @@ Each topic has exactly one authoritative home; everything else links to it. | Topic | Single home | Linked from | |---|---|---| -| What ZDDC is + tool channel links + dual-mode (local/server) overview + install snippets | `website/index.html` (hand-edited intro for `zddc.varasys.io/`) | repo `README.md`, `bootstrap/README.md` | +| What ZDDC is + tool channel links + dual-mode (local/server) overview + install snippets | `website/index.html` (hand-edited intro for `zddc.varasys.io/`) | repo `README.md`, `zddc/README.md` | | File-naming convention spec (status codes, modifiers, folder format) | `website/reference.html` | repo `README.md`, in-tool help text | | Versions + channel builds index of every tool | `website/releases/index.html` (regenerated by `build.sh`) | website intro nav, "Browse all versions" link | -| Customer-deployment install (copy-paste shell snippets, level-1/2 stubs, `?v=`, audit) | `bootstrap/README.md` | website intro, `zddc/README.md` | -| zddc-server operations: env vars, ACL syntax, `.archive` URLs, container vs binary | `zddc/README.md` | `AGENTS.md`, `bootstrap/README.md`, website intro | +| Customer-deployment install (`zddc-server` binary embeds current-stable tools; `.zddc apps:` cascade overrides; cache at `/_app/`) | `zddc/README.md` "Apps: virtual tool HTMLs" section | website intro, `AGENTS.md` | +| zddc-server operations: env vars, ACL syntax, `.archive` URLs, container vs binary | `zddc/README.md` | `AGENTS.md`, website intro | | Build / release / channel commands | `AGENTS.md` | repo `README.md` ("see AGENTS.md") | | Architecture & internal patterns | `ARCHITECTURE.md` (this file) | `AGENTS.md` | | Per-tool internal design quirks | `/README.md` | (linked from website intro tool cards) | -`website/index.html` is **hand-edited static content** (analogous to `reference.html`), not the landing-tool output. The landing tool's released bytes live at `website/releases/landing_v.html` (with `landing_.html` symlinks for channel mirrors). The unified install script (`bootstrap/install.sh`, served at `/install.sh`) curls the chosen channel's `landing_.html` to `/index.html` for customer sites where the project picker UI is actually useful (it queries `zddc-server` for the project list). The public website at `zddc.varasys.io/` has nothing to pick, so its root URL is the introduction page. +`website/index.html` is **hand-edited static content** (analogous to `reference.html`), not the landing-tool output. The install section points operators at two paths: **local** (download a `.html` file from `/releases/`) and **server** (run `zddc-server`; current-stable builds of all five tools are baked into the binary at compile time via `//go:embed`). The landing tool's released bytes live at `website/releases/landing_v.html`; the embedded copy serves at the deployment root by default. The public website at `zddc.varasys.io/` is the same hand-edited `index.html` — its root URL is the introduction page, not the project picker (because there are no projects to pick from a static site). When updating documentation, prefer linking over duplicating. If you find yourself rewriting the file-naming convention in a tool's README, link to `reference.html` instead. @@ -95,7 +91,7 @@ Each tool's `build.sh`: 4. Writes the result to `dist/tool.html` 5. If `--release ` was passed, calls `promote_release` to write into `website/releases/` (per-version file + symlink updates for stable; channel mirror overwrite for alpha/beta). -The top-level `build.sh` at the repository root calls all five tool build scripts in sequence, regenerates `website/bootstrap/` (level-1 stubs and per-channel level-2 stubs), and writes `website/releases/index.html` from a filesystem scan of `website/releases/` so the versions index always matches the on-disk state. +The top-level `build.sh` at the repository root calls all five tool build scripts in sequence and writes `website/releases/index.html` from a filesystem scan of `website/releases/` so the versions index always matches the on-disk state. ### Channels @@ -120,16 +116,35 @@ The on-page `{{BUILD_LABEL}}` is rendered red+bold for dev/alpha/beta builds (`i `X.Y.Z` for non-stable labels is the **next-stable target** — patch+1 from the latest clean `-vX.Y.Z` tag. Dev builds use the full timestamp + `-dirty` marker so iterative work is distinguishable from a formal `--release alpha` cut (which stamps date-only and is committed-clean by definition). -### Two-level bootstrap +### Install distribution model -Customer deployments under `zddc-server` use a two-level bootstrap pattern that keeps tool installation decoupled from publishing. See `bootstrap/README.md` for the full story; in short: +Two orthogonal axes: how the bytes get there (this section), and what runtime mode the tool ends up in (next section). -- **Level 1**: per-project stub at `/.html` that fetches `../.html` (always same-origin). One file per project per tool, never edited after install. -- **Level 2** (optional): site admin replaces `/.html` with a stub that fetches `/releases/_.html` (e.g. `https://zddc.varasys.io/releases/archive_beta.html`) — a checked-in symlink on the upstream that resolves to the current channel mirror. Switches the whole site to a channel. Without it, `/.html` is just the actual built tool HTML (self-contained install). The `bootstrap/install.sh` script handles both modes plus per-version pinning via a single `--mode` / `--channel` interface. +**Local mode** (no server): download a tool `.html` from and open it. Tools are self-contained — no install, no install script. -`document.write()` chains across both levels; origin stays at the deployment domain throughout. CORS only matters at level 2 (cross-origin to `zddc.varasys.io`); level 1 is same-origin. +**Server mode** (`zddc-server`): the binary `//go:embed`s the current-stable build of every tool at compile time. That's the default — no fetching happens out of the box. The server virtually serves each tool only at directories where the convention says it belongs (`internal/apps/availability.go`): -The stubs are generated from `bootstrap/level{1,2}.html.tmpl` by the root `build.sh` and published as standalone files under `website/bootstrap/level1/` and `website/bootstrap/track-/`. The home page's "Install on your server" section prints copy-paste shell snippets that `curl` these into the operator's deployment directory. +| App | Available at | +|---------------|-------------------------------------------------------------------------| +| `archive` | every directory (multi-project, project, archive, vendor) | +| `classifier` | any `Incoming`, `Working`, or `Staging` directory and its subtree | +| `mdedit` | any `Working` directory and its subtree | +| `transmittal` | any `Staging` directory and its subtree | +| `landing` | only at the deployment root | + +Resolution order at a request to `/.html` where the app is available: + +1. **Override** — real `.html` file at the path → static handler. +2. **`.zddc apps:` cascade** — walk leaf→root for an `apps.` entry. Spec is `stable`/`beta`/`alpha` (canonical channel), `v0.0.4`/`v0.0`/`v0` (canonical version), full URL (custom mirror), or local path. Closer-to-leaf wins. +3. **Embedded** — the build-time HTML compiled into the binary. + +URL sources fetch once on first request and cache forever in `/_app//`. There is no background refresh, no SHA-256 verification, no admin UI. To pull a new build, delete the cache file. Concurrent misses for the same URL share one outbound fetch (hand-rolled singleflight). Failed fetches fall through to embedded with a one-time WARN log per source URL. Direct URL access to `/_app/...` is blocked at dispatch. + +The `X-ZDDC-Source` response header always reports what was served: `fetch:URL`, `cache:URL`, `path:/abs`, or `embedded:@`. + +### Runtime mode detection + +Independent of how the tool got installed. `archive` auto-detects from the URL and folder shape (`?projects=` set → multi-project; scan root has an `archive/` child → project-root; otherwise → in-archive). The other tools don't care — `transmittal`, `classifier`, `mdedit` work the same regardless of where they live. ### Build Script Requirements diff --git a/CLAUDE.md b/CLAUDE.md index 83e4520..053e8b4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,14 +18,14 @@ This is a **monorepo of independent tools**, not one application: - `archive/`, `transmittal/`, `classifier/`, `mdedit/`, `landing/` — five self-contained HTML tools, each compiled to a single inlined HTML file in its own `dist/`. Naming: the first four output `dist/tool.html`; **`landing/` outputs `dist/index.html`** (it's the project picker served at the root of `zddc-server`). - `zddc/` — Go HTTP server (separate sub-project; Go 1.24+). Serves `ZDDC_ROOT/index.html` at `GET /` as the landing page; `Accept: application/json` on `/` returns the ACL-filtered project list. Stable releases ship as cross-compiled binaries on Codeberg release assets; the `helm/` charts in this repo build from source at deploy time. - `shared/` — `base.css` plus shared JS modules (`zddc.js`, `hash.js`, `zddc-filter.js`, `theme.js`, `help.js`) included by every tool's build, `build-lib.sh` (POSIX sh helpers sourced by every tool's `build.sh`), and `publish-codeberg-release.sh` (used by `zddc/release.sh` to upload zddc-server binaries — HTML tools no longer use it). -- `website/` — committed static site: `index.html` (root URL, hand-edited intro), `releases/_v.html` (immutable per-version archives), `releases/_v.html` and `_v.html` (symlinks to latest patch within minor / major), `releases/_{stable,beta,alpha}.html` (channel mirrors — symlinks when no active channel dev, real bytes when in flight), `releases/index.html` (regenerated by `build.sh` from a filesystem scan), `bootstrap/{level1,track-stable,track-beta,track-alpha}/.html` (per-channel level-2 stubs + same-origin level-1 stubs), and `install.sh` (symlink to `../bootstrap/install.sh` so the upstream serves it at `/install.sh`). All tool HTML files live in git — no Codeberg indirection, no manifest.json. There is no `website/dev/`. +- `website/` — committed static site: `index.html` (root URL, hand-edited intro), `releases/_v.html` (immutable per-version archives), `releases/_v.html` and `_v.html` (symlinks), `releases/_{stable,beta,alpha}.html` (channel mirrors), `releases/index.html` (regenerated by `build.sh`). **Install model:** local use is a download from `/releases/`. Server use is `zddc-server`, which has the current-stable build of all five tools baked in via `//go:embed` (compile-time default). Tools auto-served at folder-name-driven paths: `archive` everywhere, `classifier` in `Incoming`/`Working`/`Staging` subtrees, `mdedit` in `Working` subtrees, `transmittal` in `Staging` subtrees, `landing` only at root. Override via `.zddc apps:` cascade entry (channel/version/URL/path) — fetched once, cached at `/_app/`. Drop a real `.html` file at any path to override. - `helm/` — example Helm charts for zddc-server (`zddc-server-prod/`, `zddc-server-dev/`). Both compile from source via init container. Operators copy `values.yaml.example` and customize. No secrets in repo. - `tests/` — Playwright specs (Chromium only, requires File System Access API). `tests/schema.spec.js` validates `transmittal.schema.json` against canonical fixtures via `ajv` (only dev dep besides Playwright) ## Most-used commands ```bash -sh build.sh # build all five HTML tools (dist/ only) + regen website/releases/index.html + bootstrap stubs +sh build.sh # build all five HTML tools (dist/ only) + regen website/releases/index.html sh tool/build.sh # build one (archive|transmittal|classifier|mdedit|landing) sh tool/build.sh --release [version] # cut stable; write website/releases/_v.html, refresh 5 symlinks, tag -vX.Y.Z sh tool/build.sh --release alpha|beta # cut channel; overwrite website/releases/_.html in place. No tag (channel URLs are stable URLs by design) diff --git a/README.md b/README.md index 445fbc2..752c630 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The name "Zero Day Document Control" comes from the convention itself — adopt | **Document Classifier** | Spreadsheet-like bulk-renamer that copy/pastes with Excel and writes back to disk. | | **Markdown Editor** | Browser-based markdown editor with YAML front matter, TOC, and direct local file access. | -Each tool is published in three channels (stable, beta, alpha) as static files committed under `website/releases/`, browsable at . Append `?v=alpha`, `?v=0.0.4` (exact), `?v=0.0` (latest patch), or `?v=0` (latest minor) to any deployment URL to switch versions for one request. See [`bootstrap/README.md`](bootstrap/README.md) for the install / pin / audit story. +Each tool is published in three channels (stable, beta, alpha) as static files committed under `website/releases/`, browsable at . **Local use:** download a `.html` file from `releases/` and open it in a browser. **Server use:** run `zddc-server` — the current-stable build of every tool is baked into the binary at compile time, so a fresh deployment Just Works with zero config. Tools auto-appear at folder-name-driven paths (archive everywhere; classifier in `Incoming`/`Working`/`Staging`; mdedit in `Working`; transmittal in `Staging`). Override per-directory by writing an `apps:` entry in any `.zddc` file (channel/version/URL/path). URL overrides are fetched once and cached in `/_app/`; drop a real `.html` file at any path to override entirely. ## File-naming convention diff --git a/zddc/README.md b/zddc/README.md index 47f11d1..5d749d5 100644 --- a/zddc/README.md +++ b/zddc/README.md @@ -180,10 +180,9 @@ Two prefixes are filtered from listings under `ZDDC_ROOT`: side-state (caches, dev-shell home dirs, snapshot staging) on the same volume that's served, without exposing it. - **`_`-prefixed** (e.g. `/_template/`) — excluded from listings only. Direct URL - access still works, so the `_template/` directory of bootstrap stubs created - by the install snippet is reachable but doesn't clutter the project picker. - Use this for operator-managed scaffolding the user shouldn't browse to but - might link to. + access still works. Use this for operator-managed scaffolding the user shouldn't + browse to but might link to (e.g. a `_template/` directory of stub-HTML examples + to copy into project subdirs). ## Admin Debug Page @@ -235,48 +234,66 @@ callers. - An interactive terminal is not yet available; that's planned as a follow-up behind a separate `ZDDC_ADMIN_TERM=1` env-var gate so it stays opt-in. -## Landing Page and Tool Install +## Apps: virtual tool HTMLs -The recommended install is the unified [`install.sh`](https://zddc.varasys.io/install.sh) script — `cd` into `ZDDC_ROOT/` and run: +`zddc-server` virtually serves the five tool HTMLs (archive, transmittal, +classifier, mdedit, landing) at the appropriate paths. The current-stable +build of each tool is **baked into the binary at compile time** via +`//go:embed`; that's the default. No fetch happens out of the box. -```sh -sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" # self-contained, current stable (default) -sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" -- --mode track --channel beta # track beta -sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" -- --channel 0.0.2 # pin to a specific version +### Where each tool is served + +| App | Available at | +|---------------|-------------------------------------------------------------------------| +| `archive` | every directory (multi-project, project, archive, vendor) | +| `classifier` | any `Incoming`, `Working`, or `Staging` directory and its subtree | +| `mdedit` | any `Working` directory and its subtree | +| `transmittal` | any `Staging` directory and its subtree | +| `landing` | only at the deployment root (the project picker) | + +Outside these locations, the corresponding `.html` URL returns 404. + +### Override and version-pin + +For any path, the resolution order is: + +1. **Real file at the path** — operator drops `archive.html` (or any other) + into a directory; the static handler serves it. Beats everything below. +2. **Closer-to-leaf `.zddc apps:` entry** — walks `.zddc` files leaf→root + for an `apps.` entry. The first match wins. Spec is one of: + - `stable` / `beta` / `alpha` (canonical upstream channel) + - `v0.0.4` / `v0.0` / `v0` (canonical upstream version pin) + - `https://...` (full URL to a custom mirror) + - `./local.html` / `/abs/path.html` (local file) +3. **Embedded** — the build-time HTML compiled into the binary. + +URL sources are fetched once on first request and cached forever in +`/_app//`. There is no background refresh and no +hash verification — to pull a new build, delete the cache file. Concurrent +misses for the same URL share one outbound fetch (singleflight). Direct +URL access to `/_app/...` is blocked at dispatch; cached HTMLs are served +only via the apps resolver. + +If a configured URL fetch fails (network down, 5xx), the server falls back +to the embedded copy and emits a one-time WARN log per source. The +`X-ZDDC-Source` response header always reports what was served: +`fetch:URL`, `cache:URL`, `path:/abs`, or `embedded:@`. + +### Example + +```yaml +# /Project-A/.zddc +apps: + classifier: alpha # track alpha for this project + archive: https://my-mirror.internal/zddc/archive_v0.0.4.html # custom mirror, pinned + mdedit: ./our-mdedit.html # local fork ``` -The script handles all three deployment patterns: +### Env vars -- **Self-contained** (default) — fetches the five current-channel tool HTMLs and populates a `_template/` directory of level-1 bootstrap stubs. No runtime dependency on `zddc.varasys.io`. Re-run to update. -- **Channel-tracking** — fetches five tiny level-2 stubs (~10 KB total) that fetch the named channel from `zddc.varasys.io` on every page load. -- **Pin-to-version** — copies a specific version's HTML locally; site does not move. - -For project subdirectories, run the same command from inside `/Project-X/` — it auto-detects the parent and installs four level-1 stubs that fetch `../.html`. - -After running one of the snippets, the deployment looks like: - -``` -ZDDC_ROOT/ - index.html ← landing page (current stable, or level-2 stub) - archive.html ← archive browser (likewise) - transmittal.html - classifier.html - mdedit.html - _template/ ← level-1 bootstrap stubs (self-contained snippet only); - rename a copy to / for each project - Project-001/ - archive.html ← level-1 stub: fetches ../archive.html - transmittal.html - classifier.html - mdedit.html - Project-002/ - … -``` - -The level-2 stubs require `zddc-server` to accept cross-origin requests -from `zddc.varasys.io`, controlled via `ZDDC_CORS_ORIGIN`. See -[`bootstrap/README.md`](../bootstrap/README.md) for the full install -guide and the `?v=…` URL parameter for per-request version selection. +| Variable | Default | Purpose | +|----------------------|---------|----------------------------------------------------------| +| `ZDDC_BUILD_VERSION` | `dev` | String stamped into `X-ZDDC-Source: embedded:@` | The landing page fetches `GET /` (with `Accept: application/json`) to retrieve the list of top-level project directories the requesting user has access to. It renders checkboxes @@ -369,9 +386,9 @@ returns JSON directory listings in exactly the same format as Caddy's `file-serv — no changes to `archive/js/source.js` are needed. To use: install `archive.html` at `ZDDC_ROOT/archive.html` (or any subdirectory) — either -the actual built tool fetched by the self-contained install snippet, or a -level-1/level-2 bootstrap stub that fetches it. Then open it via the zddc-server URL; -the app will auto-connect and scan the directory tree. +the actual built tool downloaded by the self-contained install snippet, or one of the +six-line stubs from the project-subdir / track-upstream snippets that fetches it. Then +open it via the zddc-server URL; the app will auto-connect and scan the directory tree. ## Distribution