docs: rewrite for embedded + cascade install model

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 <ZDDC_ROOT>/_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) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-01 15:25:57 -05:00
parent 7b764956bd
commit adb6904397
5 changed files with 118 additions and 85 deletions

View file

@ -3,7 +3,7 @@
## Commands ## Commands
```bash ```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 sh build.sh
# Build single tool # Build single tool
@ -55,8 +55,7 @@ shared/
sourced by every tool's build.sh via: . "$root_dir/../shared/build-lib.sh" sourced by every tool's build.sh via: . "$root_dir/../shared/build-lib.sh"
website/ website/
index.html hand-edited intro page (root URL) index.html hand-edited intro page + install snippets (root URL)
install.sh → ../bootstrap/install.sh symlink so the upstream serves /install.sh
releases/ releases/
index.html versions index, regenerated by build.sh from filesystem scan index.html versions index, regenerated by build.sh from filesystem scan
<tool>_v<X.Y.Z>.html real per-version files (committed, immutable) <tool>_v<X.Y.Z>.html real per-version files (committed, immutable)
@ -65,17 +64,6 @@ website/
<tool>_stable.html -> ... symlink: current stable <tool>_stable.html -> ... symlink: current stable
<tool>_beta.html -> ... symlink to stable (or real bytes if active beta) <tool>_beta.html -> ... symlink to stable (or real bytes if active beta)
<tool>_alpha.html -> ... symlink to beta/stable (or real bytes if active alpha) <tool>_alpha.html -> ... symlink to beta/stable (or real bytes if active alpha)
bootstrap/
level1/<tool>.html same-origin level-1 stubs (4 tools, no landing)
track-stable/<tool>.html level-2 stubs that track the current-stable channel
track-alpha/<tool>.html level-2 stubs that track the alpha channel
track-beta/<tool>.html level-2 stubs that track the beta channel
bootstrap/
level1.html.tmpl per-project bootstrap template (relative ../<tool>.html)
level2.html.tmpl level-2 channel-tracking bootstrap template
install.sh unified bootstrap script (modes: copy / track; channels: stable/beta/alpha/<version>)
README.md install / channel / pin docs
helm/ helm/
zddc-server-prod/ production-shaped Helm chart (compiles from source via init container) 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 <tag>` 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.) 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<X.Y.Z>.html`; the unified install script (`bootstrap/install.sh`) curls the chosen channel's `landing_<channel>.html` to `<deployment-root>/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<X.Y.Z>.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) ### 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 `<tool>_v0.0.5.html` in place. Stable per-version files are immutable. 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 `<tool>_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. 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. 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 `<tool>_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 `<tool>_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. 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`. 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. 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. 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/<tool>.html` — 4 same-origin level-1 stubs (archive, transmittal, classifier, mdedit; landing has no level-1 stub since it only lives at deployment root). - **Local** — download a tool `.html` from `https://zddc.varasys.io/releases/` and open it. Done.
- `bootstrap/track-{alpha,beta,stable}/<tool>.html` — 5 level-2 stubs per channel that fetch `<source>/releases/<tool>_<channel>.html` directly. Static-file resolution end-to-end via the symlink chain — no JS indirection, no manifest lookup. - **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 `<app>.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 `<app>.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 `<ZDDC_ROOT>/_app/<host>/<path>`. 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:<app>@<build>`. 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 ### Worktrees

View file

@ -37,8 +37,7 @@ Website files (what `zddc.varasys.io` serves) — committed in this repo as stat
``` ```
website/ website/
index.html # hand-edited intro page (root URL) index.html # hand-edited intro page + install snippets (root URL)
install.sh → ../bootstrap/install.sh # symlink so the upstream serves /install.sh
releases/ releases/
index.html # versions index, regenerated by build.sh from filesystem scan index.html # versions index, regenerated by build.sh from filesystem scan
<tool>_v<X.Y.Z>.html # real per-version files (committed, immutable) <tool>_v<X.Y.Z>.html # real per-version files (committed, immutable)
@ -47,9 +46,6 @@ website/
<tool>_stable.html → ... # symlink: current stable <tool>_stable.html → ... # symlink: current stable
<tool>_beta.html → ... # symlink to stable (or real bytes when active beta dev) <tool>_beta.html → ... # symlink to stable (or real bytes when active beta dev)
<tool>_alpha.html → ... # symlink to beta/stable (or real bytes when active alpha dev) <tool>_alpha.html → ... # symlink to beta/stable (or real bytes when active alpha dev)
bootstrap/
level1/<tool>.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. 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 | | 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 | | 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 | | 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` | | Customer-deployment install (`zddc-server` binary embeds current-stable tools; `.zddc apps:` cascade overrides; cache at `<root>/_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`, `bootstrap/README.md`, website intro | | 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") | | Build / release / channel commands | `AGENTS.md` | repo `README.md` ("see AGENTS.md") |
| Architecture & internal patterns | `ARCHITECTURE.md` (this file) | `AGENTS.md` | | Architecture & internal patterns | `ARCHITECTURE.md` (this file) | `AGENTS.md` |
| Per-tool internal design quirks | `<tool>/README.md` | (linked from website intro tool cards) | | Per-tool internal design quirks | `<tool>/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<X.Y.Z>.html` (with `landing_<channel>.html` symlinks for channel mirrors). The unified install script (`bootstrap/install.sh`, served at `/install.sh`) curls the chosen channel's `landing_<channel>.html` to `<deployment-root>/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<X.Y.Z>.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. 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` 4. Writes the result to `dist/tool.html`
5. If `--release <channel-or-version>` was passed, calls `promote_release` to write into `website/releases/` (per-version file + symlink updates for stable; channel mirror overwrite for alpha/beta). 5. If `--release <channel-or-version>` 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 ### 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 `<tool>-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). `X.Y.Z` for non-stable labels is the **next-stable target** — patch+1 from the latest clean `<tool>-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 `<project>/<tool>.html` that fetches `../<tool>.html` (always same-origin). One file per project per tool, never edited after install. **Local mode** (no server): download a tool `.html` from <https://zddc.varasys.io/releases/> and open it. Tools are self-contained — no install, no install script.
- **Level 2** (optional): site admin replaces `<deployment-root>/<tool>.html` with a stub that fetches `<source>/releases/<tool>_<channel>.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, `<deployment-root>/<tool>.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.
`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-<channel>/`. 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 `<dir>/<app>.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.<app>` 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 `<ZDDC_ROOT>/_app/<host>/<path>`. 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:<app>@<build>`.
### 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 ### Build Script Requirements

View file

@ -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`). - `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. - `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). - `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/<tool>_v<X.Y.Z>.html` (immutable per-version archives), `releases/<tool>_v<X.Y>.html` and `_v<X>.html` (symlinks to latest patch within minor / major), `releases/<tool>_{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}/<tool>.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/<tool>_v<X.Y.Z>.html` (immutable per-version archives), `releases/<tool>_v<X.Y>.html` and `_v<X>.html` (symlinks), `releases/<tool>_{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 `<ZDDC_ROOT>/_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. - `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) - `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 ## Most-used commands
```bash ```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 # build one (archive|transmittal|classifier|mdedit|landing)
sh tool/build.sh --release [version] # cut stable; write website/releases/<tool>_v<X.Y.Z>.html, refresh 5 symlinks, tag <tool>-vX.Y.Z sh tool/build.sh --release [version] # cut stable; write website/releases/<tool>_v<X.Y.Z>.html, refresh 5 symlinks, tag <tool>-vX.Y.Z
sh tool/build.sh --release alpha|beta # cut channel; overwrite website/releases/<tool>_<channel>.html in place. No tag (channel URLs are stable URLs by design) sh tool/build.sh --release alpha|beta # cut channel; overwrite website/releases/<tool>_<channel>.html in place. No tag (channel URLs are stable URLs by design)

View file

@ -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. | | **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. | | **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 <https://zddc.varasys.io/releases/>. 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 <https://zddc.varasys.io/releases/>. **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 `<ZDDC_ROOT>/_app/`; drop a real `.html` file at any path to override entirely.
## File-naming convention ## File-naming convention

View file

@ -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 side-state (caches, dev-shell home dirs, snapshot staging) on the same volume
that's served, without exposing it. that's served, without exposing it.
- **`_`-prefixed** (e.g. `/_template/`) — excluded from listings only. Direct URL - **`_`-prefixed** (e.g. `/_template/`) — excluded from listings only. Direct URL
access still works, so the `_template/` directory of bootstrap stubs created access still works. Use this for operator-managed scaffolding the user shouldn't
by the install snippet is reachable but doesn't clutter the project picker. browse to but might link to (e.g. a `_template/` directory of stub-HTML examples
Use this for operator-managed scaffolding the user shouldn't browse to but to copy into project subdirs).
might link to.
## Admin Debug Page ## Admin Debug Page
@ -235,48 +234,66 @@ callers.
- An interactive terminal is not yet available; that's planned as a follow-up - 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. 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 ### Where each tool is served
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 | App | Available at |
sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" -- --channel 0.0.2 # pin to a specific version |---------------|-------------------------------------------------------------------------|
| `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 `<app>.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.<app>` 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
`<ZDDC_ROOT>/_app/<host>/<path>`. 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:<app>@<build>`.
### Example
```yaml
# <ZDDC_ROOT>/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. | Variable | Default | Purpose |
- **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. | `ZDDC_BUILD_VERSION` | `dev` | String stamped into `X-ZDDC-Source: embedded:<app>@<v>` |
For project subdirectories, run the same command from inside `<ZDDC_ROOT>/Project-X/` — it auto-detects the parent and installs four level-1 stubs that fetch `../<tool>.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 <project-name>/ 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.
The landing page fetches `GET /` (with `Accept: application/json`) to retrieve the list 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 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. — no changes to `archive/js/source.js` are needed.
To use: install `archive.html` at `ZDDC_ROOT/archive.html` (or any subdirectory) — either 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 the actual built tool downloaded by the self-contained install snippet, or one of the
level-1/level-2 bootstrap stub that fetches it. Then open it via the zddc-server URL; six-line stubs from the project-subdir / track-upstream snippets that fetches it. Then
the app will auto-connect and scan the directory tree. open it via the zddc-server URL; the app will auto-connect and scan the directory tree.
## Distribution ## Distribution