docs: align with simplified release model

Updates to all six top-level docs to describe the new flow:

- Storage: HTML tools live in website/releases/ as committed static
  files. Per-version files are real bytes; partial-version pins and
  channel mirrors are checked-in symlinks. No manifest.json, no Codeberg
  indirection, no Caddy regex-rewrite.
- URL scheme: <tool>_v<X.Y.Z>.html (exact), <tool>_v<X.Y>.html (latest
  patch), <tool>_v<X>.html (latest minor), <tool>_<channel>.html
  (channel mirror). All resolve via the symlink chain.
- Cascade rule: stable cut → beta + alpha symlinks reset to stable;
  beta cut → alpha resets to beta. Channels are never stale.
- No -alpha.N / -beta.N counter tags. Channel URLs are stable URLs by
  design; counters defeat that. The on-page <date> · <sha> label is
  enough for traceability.
- bootstrap/install.sh is the canonical install path. The four hand-
  rolled snippets are gone; one script handles all three deployment
  patterns + both target shapes.
- Helm charts under helm/ (zddc-server-{prod,dev}/) build from source
  via init container; documented as the recommended k8s deployment
  path.
- zddc-server now publishes binaries on stable cuts only — no alpha/
  beta channel for binaries. Active dev runs through the dev helm chart
  which builds from source on each rollout.

Files updated:

- CLAUDE.md — Repo shape, Most-used commands, Things that bite if you
  forget. Drops mentions of manifest.json, the Codeberg-as-canonical
  model, and -alpha.N/-beta.N tags.
- AGENTS.md — website/ tree, Releasing — channels and layout, Channel
  discipline rules (renumbered to add coordinated minor/major bump
  rule), Freshen helper, Bootstrap stubs, zddc-server Release tagging.
- ARCHITECTURE.md — website/ tree, build.sh step 5, Channels section,
  level-2 bootstrap description.
- README.md — tool publishing description, link to helm/.
- bootstrap/README.md — install path is install.sh now; pin URL table
  uses static symlinks; CORS check uses release-asset URLs (not
  manifest.json).
- zddc/README.md — Quick Start uses Codeberg URLs directly (no proxy);
  Release tagging is stable-only; Distribution / Versioning sections
  rewritten.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-04-30 09:56:34 -05:00
parent 607121a9ea
commit 7365e94cac
6 changed files with 189 additions and 201 deletions

143
AGENTS.md
View file

@ -3,17 +3,17 @@
## Commands ## Commands
```bash ```bash
# Build all tools (writes to dist/ only; also regenerates website/releases/{index.html,manifest.json}) # Build all tools (writes to dist/ only; also regenerates website/releases/index.html + bootstrap stubs)
sh build.sh sh build.sh
# Build single tool # Build single tool
sh tool/build.sh # archive | transmittal | classifier | mdedit | landing sh tool/build.sh # archive | transmittal | classifier | mdedit | landing
# Cut a stable release (auto-increments patch version, tags <tool>-vX.Y.Z, uploads <tool>_vX.Y.Z.html to Codeberg) # Cut a stable release (auto-increments patch version, writes website/releases/<tool>_v<X.Y.Z>.html, refreshes 5 symlinks, tags <tool>-v<X.Y.Z>)
sh tool/build.sh --release sh tool/build.sh --release
sh tool/build.sh --release 1.2.0 # explicit version sh tool/build.sh --release 1.2.0 # explicit version
# Cut an alpha/beta channel build (tags <tool>-vX.Y.Z-{alpha,beta}.N, uploads to Codeberg as a prerelease) # Cut an alpha/beta channel build (overwrites website/releases/<tool>_<channel>.html in place; on a beta cut, alpha cascades to a symlink → beta. No git tag.)
sh tool/build.sh --release alpha sh tool/build.sh --release alpha
sh tool/build.sh --release beta sh tool/build.sh --release beta
@ -55,25 +55,37 @@ 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 (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 the Codeberg release list index.html versions index, regenerated by build.sh from filesystem scan
manifest.json <tool>-<channel> → tag map (regenerated by build.sh; consumed by the level-2 stub at runtime) <tool>_v<X.Y.Z>.html real per-version files (committed, immutable)
<tool>_v<X.Y>.html -> ... symlink: latest patch within X.Y.*
<tool>_v<X>.html -> ... symlink: latest within X.*.*
<tool>_stable.html -> ... symlink: current stable
<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)
bootstrap/ bootstrap/
level1/<tool>.html same-origin level-1 stubs (4 tools, no landing) 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-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-alpha/<tool>.html level-2 stubs that track the alpha channel
track-beta/<tool>.html level-2 stubs that track the beta channel track-beta/<tool>.html level-2 stubs that track the beta channel
bootstrap/ bootstrap/
level1.html.tmpl per-project bootstrap template (relative ../<tool>.html) level1.html.tmpl per-project bootstrap template (relative ../<tool>.html)
level2.html.tmpl level-2 channel-tracking bootstrap template 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 README.md install / channel / pin docs
helm/
zddc-server-prod/ production-shaped Helm chart (compiles from source via init container)
zddc-server-dev/ dev-shaped variant (tracks main HEAD; debug-level logging; faster probes)
README.md chart design rationale + quick-start
``` ```
**Critical:** `dist/` files are gitignored. They're the canonical built artifact for testing and the source for `--release` uploads to Codeberg, but they aren't checked in. Never edit them directly. **Critical:** `dist/` files are gitignored. They're the canonical built artifact for testing and the source for `--release` writes into `website/releases/`, but they aren't checked in. Never edit them directly.
The per-version `<tool>_v<X.Y.Z>.html` artifacts and zddc-server binaries also aren't checked in — they live on Codeberg as release assets attached to git tags. `website/releases/` only contains `index.html` (versions index) and `manifest.json` (channel → tag map), both regenerated by `build.sh` from a Codeberg API call. `website/releases/` IS committed — per-version files as real bytes, partial-version pins (`_v<X.Y>`, `_v<X>`) and channel mirrors (`_stable`, `_beta`, `_alpha`) as symlinks. The build script (`shared/build-lib.sh promote_release`) maintains the symlink chain on each release. zddc-server binaries are NOT in this repo — they're attached as Codeberg release assets to clean `zddc-server-vX.Y.Z` tags by `zddc/release.sh`.
## Shared CSS (`shared/base.css`) ## Shared CSS (`shared/base.css`)
@ -162,37 +174,47 @@ Format: `trackingNumber_revision (status) - title.extension`
### Releasing — channels and layout ### Releasing — channels and layout
Three channels. Versioning is **pre-release semver**: stable owns clean `vX.Y.Z`; alpha and beta carry `vX.Y.Z-alpha.N` / `vX.Y.Z-beta.N`. The next-stable target X.Y.Z is patch-bumped from the latest clean `<tool>-vX.Y.Z` tag. Three channels. Versioning is **per-tool semver**: stable owns clean `vX.Y.Z`; alpha and beta are mutable channel mirrors that get overwritten in place (no counter tags — channel URLs are stable URLs by design). The next-stable target X.Y.Z used in alpha/beta on-page labels is patch-bumped from the latest clean `<tool>-vX.Y.Z` tag.
**Storage model.** Built artifacts live on Codeberg as release assets attached to git tags — *not* committed to this repo. The website at zddc.varasys.io serves them by reverse-proxying `/releases/<tag>/<asset>` to the corresponding Codeberg URL, so consumers (operators' bootstrap stubs, `zddc-use`) only ever talk to zddc.varasys.io. Channel resolution is via `website/releases/manifest.json` — a small file `build.sh` regenerates from the Codeberg API and commits. **Storage model.** All HTML tool artifacts live in this repo under `website/releases/`. Per-version files (`<tool>_v<X.Y.Z>.html`) are real, immutable, committed bytes; partial-version pins (`<tool>_v<X.Y>.html`, `<tool>_v<X>.html`) and channel mirrors (`<tool>_<channel>.html`) are checked-in symlinks pointing at the appropriate concrete file. No `manifest.json`, no Codeberg indirection, no Caddy proxy magic. Caddy serves these as plain static files.
- **Stable**: `sh tool/build.sh --release [version]` (or just `--release` to auto-bump patch from the latest stable tag). Tags `<tool>-v<version>`, uploads `<tool>_v<version>.html` as a release asset on Codeberg. Label: `vX.Y.Z` (black). Skips silently if source has not changed since the latest stable tag (HEAD-vs-tag diff). zddc-server binaries are a separate concern — they ship as Codeberg release assets attached to clean `zddc-server-vX.Y.Z` tags by `zddc/release.sh`. zddc-server has no alpha/beta channel for binaries; the helm charts under `helm/` build from source.
- **Beta**: `sh tool/build.sh --release beta`. Tags `<tool>-v<next-patch>-beta.N`, uploads `<tool>_v<next-patch>-beta.N.html`. Label: `vX.Y.Z-beta · <date> · <sha>` (red).
- **Alpha**: `sh tool/build.sh --release alpha`. Tags `<tool>-v<next-patch>-alpha.N`, uploads. Label: `vX.Y.Z-alpha · <date> · <sha>` (red).
- **Plain dev builds** (no `--release`): produce `tool/dist/<tool>.html` only. No website/releases side-effect, no Codeberg upload. To publish, re-run with `--release alpha`.
After any release run, `sh build.sh` queries the Codeberg API once and rewrites `website/releases/index.html` and `manifest.json`. Commit those alongside the release. `shared/build-lib.sh promote_release` is the single point of truth for HTML-tool releases:
After cutting a release, run `git push --tags` to publish the tag. - **Stable** (`sh tool/build.sh --release [version]`, or just `--release` to auto-bump patch): Writes `website/releases/<tool>_v<X.Y.Z>.html` (immutable real bytes), then refreshes 5 symlinks — `<tool>_v<X.Y>.html`, `<tool>_v<X>.html`, `<tool>_stable.html`, `<tool>_beta.html`, `<tool>_alpha.html` — all → the new versioned file. Tags `<tool>-v<X.Y.Z>`. Cascade rule: stable cut means beta and alpha both reset to stable (no active dev on either downstream channel). Skips silently if source has not changed since the latest stable tag.
- **Beta** (`sh tool/build.sh --release beta`): Overwrites `<tool>_beta.html` with the dist HTML bytes (replacing the symlink with a real file if one was there). Cascade: `<tool>_alpha.html``<tool>_beta.html` (symlink). No tag.
- **Alpha** (`sh tool/build.sh --release alpha`): Overwrites `<tool>_alpha.html` with the dist HTML bytes. No tag, no other side-effects.
- **Plain dev builds** (no `--release`): produce `tool/dist/<tool>.html` only. No `website/releases/` side-effect, no commit. To publish, re-run with `--release alpha`.
`$CODEBERG_TOKEN` must be exported before any `--release` invocation. The `promote_release` helper calls `publish_codeberg_release` which uses the token to create the release and upload the asset. On-page `{{BUILD_LABEL}}` format:
`website/index.html` (the root URL of zddc.varasys.io) is **hand-edited static content**, not built by `landing/build.sh`. The landing tool ships only as a Codeberg release asset; the self-contained install snippet curls `landing_v<ver>.html` to `<deployment-root>/index.html` at customer-deployment time. - Plain dev: `vX.Y.Z-alpha · <full-ts> · <sha>[-dirty]` (red), where X.Y.Z is the next-stable target.
- `--release alpha`: `vX.Y.Z-alpha · <date> · <sha>` (red).
- `--release beta`: `vX.Y.Z-beta · <date> · <sha>` (red).
- `--release [version]`: `v<X.Y.Z>` (black).
After cutting a stable release, `git push origin <tag>` to publish the tag. `git push origin main` publishes the new versioned file + updated symlinks.
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.
### Channel discipline (MUST rules) ### Channel discipline (MUST rules)
The build system does not enforce these. Treating channels carelessly defeats the point of having three. Be disciplined. The build system does not enforce these. Treating channels carelessly defeats the point of having three. Be disciplined.
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 `v0.0.5` in place. Stable 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. **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 their own choice; they can move forward when they want. 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. **Alpha and beta are mutable.** Document this anywhere you invite users to test them. Pinning `?v=alpha` (or `_alpha.html`) in a production deployment is a mistake; it gets rebuilt without notice. 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. **Stale-channel rule.** Users tracking alpha (or beta) MUST never see a build older than current stable. After every stable release, run `./freshen-channel <tool> alpha` and `./freshen-channel <tool> beta` so each channel is at-least-current. This is not optional. 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).
5. **Hotfix path.** For critical bugs: fix on `main`, cut a new stable (no beta soak required), then freshen alpha + beta. Tag the commit message `fix:` or include "hotfix" so the intent is visible in `git log`. 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. **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. 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.
### Freshen helper ### Freshen helper
`./freshen-channel <tool> <channel>` rebuilds the alpha or beta channel of a tool from its current stable tag, cutting a new pre-release tag (e.g., `<tool>-v<next-patch>-alpha.N`) and uploading the asset to Codeberg. Use it after every stable release (rule 4 above) and any other time alpha/beta has fallen behind stable. `./freshen-channel <tool> <channel>` rebuilds the alpha or beta channel of a tool from its current stable tag — useful when you want a channel to advance to current stable code without doing active dev on it (e.g. after upstream dependency changes). Most of the time you don't need it: the cascade rule (rule 5 above) means a stable cut already resets the downstream channel symlinks. Use this when you specifically want a fresh build with a new on-page label timestamp instead of a symlink.
```sh ```sh
./freshen-channel archive alpha ./freshen-channel archive alpha
@ -203,21 +225,19 @@ What it does:
1. Finds the latest `<tool>-v*` clean stable tag. 1. Finds the latest `<tool>-v*` clean stable tag.
2. Creates a temporary git worktree at that tag — does **not** touch the main worktree's HEAD or working tree. 2. Creates a temporary git worktree at that tag — does **not** touch the main worktree's HEAD or working tree.
3. Runs `<tool>/build.sh --release <channel>` inside the worktree, which tags `<tool>-v<next-patch>-<channel>.N` and uploads to Codeberg. 3. Runs `<tool>/build.sh --release <channel>` inside the worktree, which overwrites `<tool>_<channel>.html` with the freshly-built bytes. (Note: this is in the worktree, not on main — you'll need to commit the resulting changes back to main afterward.)
4. Removes the worktree. 4. Removes the worktree.
The on-page label of the freshened build is `v<next-stable>-<channel> · <today> · <stable-tag-sha>` — the SHA pins which stable was used as the source, recoverable via `git checkout`. 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.
Note: 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 ### Bootstrap stubs
`build.sh` regenerates `website/bootstrap/` on every invocation: `build.sh` regenerates `website/bootstrap/` on every invocation:
- `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). - `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).
- `bootstrap/track-{alpha,beta,stable}/<tool>.html` — 5 level-2 stubs per channel that resolve the channel via `zddc.varasys.io/releases/manifest.json` and fetch the asset via `zddc.varasys.io/releases/<tag>/<tool>_v<version>.html` (Caddy proxies to Codeberg). - `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.
End users install via copy-paste shell snippets on the home page's "Install on your server" section — each snippet `curl`s the relevant stubs (or a one-shot version-pinned HTML, for the self-contained option) into the operator's deployment directory. 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 `bootstrap/README.md` for the install / pin / audit story. See `bootstrap/README.md` for the install / pin / audit story.
@ -290,59 +310,18 @@ ZDDC_ROOT=/path/to/your/archive ZDDC_TLS_CERT=none ZDDC_ADDR=:8080 \
### Release tagging ### Release tagging
`zddc/release.sh` is the canonical path. It tags the commit, compiles `zddc/release.sh` is the canonical path. It tags the commit, cross-compiles binaries (native Go), and uploads them as Codeberg release assets. **Stable cuts only** — zddc-server has no alpha/beta channel for binary distribution. Active dev/soak happens via the `helm/zddc-server-dev/` chart, which builds from source on every pod restart against any commit you point it at.
the binaries (native Go), and uploads them as Codeberg release assets.
There's no container image build / push anymore — the chart's
`Dockerfile.prod` and `Dockerfile` (dev) compile zddc-server from
source at build time, fetching the right tag from Codeberg directly.
The upstream `codeberg.org/varasys/zddc-server` registry is frozen
(historical tags only).
```sh ```sh
sh zddc/release.sh # alpha cut, version auto-derived sh zddc/release.sh # patch-bump from latest clean stable tag
sh zddc/release.sh alpha # same sh zddc/release.sh 0.1.0 # explicit version
sh zddc/release.sh beta # beta cut
sh zddc/release.sh stable # stable cut, patch++ from latest stable
sh zddc/release.sh stable 0.1.0 # stable cut, explicit version
``` ```
**Default channel is `alpha`** so a stable-equivalent tag never The script tags the commit but does NOT push — finish with `git push origin main` and `git push origin <tag>`.
appears by accident during active development. Pass `beta` to soak;
pass `stable` only when deliberately promoting. The script tags the
commit but does NOT push — finish with `git push origin <branch>` and
`git push origin <tag>`.
**Versioning** — pre-release semver. Stable cuts get clean `vX.Y.Z` **Versioning** — clean semver. Stable cuts get `<tool>-vX.Y.Z` tags; no `-alpha.N` / `-beta.N` counters. The historical `zddc-server-v0.0.8-alpha.1` and `-alpha.2` tags from the previous scheme stay as artifacts but no new alpha/beta tags get added.
tags. Alpha and beta cuts get `vX.Y.Z-alpha.N` / `vX.Y.Z-beta.N`
where `X.Y.Z` is the next patch of the latest clean stable and `N`
is a per-channel counter that resets when stable advances. Example
sequence (current stable v0.0.7):
``` **Binary publishing** — release.sh uploads the four cross-compiled binaries (`zddc-server-{linux,darwin,windows}-{amd64,arm64}`) as release assets attached to the new git tag on Codeberg. Operators download from `https://codeberg.org/VARASYS/ZDDC/releases/download/zddc-server-vX.Y.Z/zddc-server-<platform>` directly.
alpha → v0.0.8-alpha.1
alpha → v0.0.8-alpha.2
beta → v0.0.8-beta.1
alpha → v0.0.8-alpha.3 (alpha and beta count separately)
stable → v0.0.8 (counter resets at next-patch advance)
alpha → v0.0.9-alpha.1
```
Pre-release semver ordering (`0.0.8-alpha.1 < 0.0.8-alpha.2 <
0.0.8-beta.1 < 0.0.8`) is honored by all standard tooling Codeberg
release sorting, `git tag --sort=-v:refname`, `sort -V`, npm, cargo —
so consumers can pin or compare versions without surprises.
**Binary publishing** — release.sh uploads the four cross-compiled
binaries (`zddc-server-{linux,darwin,windows}-{amd64,arm64}`) as
release assets attached to the new git tag on Codeberg. The website
at zddc.varasys.io reverse-proxies `/releases/<tag>/<asset>` URLs to
the corresponding Codeberg release-asset URL, so consumers
(`zddc-use`, the level-2 bootstrap stubs, the dynamic chart
Dockerfiles) only ever talk to zddc.varasys.io.
After publishing: run `sh build.sh` to refresh
`website/releases/index.html` and `manifest.json` against the new
release list, and commit those.
Prerequisites: Prerequisites:
- Go 1.24+ on PATH (or run from a Go container). - Go 1.24+ on PATH (or run from a Go container).

View file

@ -33,20 +33,28 @@ tool/
tool.html # Generated output — never edit this manually tool.html # Generated output — never edit this manually
``` ```
Website files (what `zddc.varasys.io` serves) — committed in this repo as static assets, but the actual built tool HTML and zddc-server binaries live on Codeberg as release assets: Website files (what `zddc.varasys.io` serves) — committed in this repo as static assets, including the per-version HTML tool files:
``` ```
website/ website/
index.html # hand-edited intro page (root URL) index.html # hand-edited intro page (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 the Codeberg API index.html # versions index, regenerated by build.sh from filesystem scan
manifest.json # <tool>-<channel> → tag map, regenerated by build.sh; the level-2 stub fetches this <tool>_v<X.Y.Z>.html # real per-version files (committed, immutable)
<tool>_v<X.Y>.html → ... # symlink: latest patch within X.Y.*
<tool>_v<X>.html → ... # symlink: latest within X.*.*
<tool>_stable.html → ... # symlink: current stable
<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)
bootstrap/ bootstrap/
level1/<tool>.html # same-origin stubs for project subdirectories level1/<tool>.html # same-origin stubs for project subdirectories
track-{alpha,beta,stable}/ # per-channel level-2 stubs (5 tools each) track-{alpha,beta,stable}/ # per-channel level-2 stubs (5 tools each)
``` ```
The per-version `<tool>_v<X.Y.Z>.html` files and zddc-server binaries are **not** committed — they live on Codeberg as release assets attached to git tags. Caddy at `zddc.varasys.io` reverse-proxies `/releases/<tag>/<asset>` to the corresponding Codeberg URL, so consumers (operators' bootstrap stubs, `zddc-use`, the chart Dockerfiles) only ever talk to `zddc.varasys.io`. 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.
zddc-server binaries are a separate concern — they ship as Codeberg release assets attached to clean `zddc-server-vX.Y.Z` tags by `zddc/release.sh`. The `helm/zddc-server-{prod,dev}/` charts build from source via init container instead of fetching binaries.
There is no `website/dev/`. To preview a build locally, open `dist/tool.html` directly via the dev server. To publish on `zddc.varasys.io`, cut a release. There is no `website/dev/`. To preview a build locally, open `dist/tool.html` directly via the dev server. To publish on `zddc.varasys.io`, cut a release.
@ -69,7 +77,7 @@ Each topic has exactly one authoritative home; everything else links to it.
| 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 ships only as a Codeberg release asset (`landing-v<X.Y.Z>` tag → `landing_v<X.Y.Z>.html` asset) — the self-contained install snippet curls the current-stable asset through Caddy at `zddc.varasys.io/releases/<tag>/landing_v<ver>.html` and saves it as `<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 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.
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.
@ -85,21 +93,21 @@ Each tool's `build.sh`:
2. Reads JS files in declaration order, concatenates them 2. Reads JS files in declaration order, concatenates them
3. Processes `template.html` with `awk`, replacing `{{PLACEHOLDER}}` markers with the concatenated content and stripping CDN `<script>`/`<link>` tags 3. Processes `template.html` with `awk`, replacing `{{PLACEHOLDER}}` markers with the concatenated content and stripping CDN `<script>`/`<link>` tags
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 tag the commit and upload the dist HTML as a Codeberg release asset (via `shared/publish-codeberg-release.sh`). 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 then queries the Codeberg API once to rewrite `website/releases/index.html` and `manifest.json` so the website's versions index reflects current Codeberg state. 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.
### Channels ### Channels
Three release channels. Each `--release` invocation tags the commit and uploads the resulting HTML to Codeberg as a release asset; nothing is written under `website/releases/` other than the regenerated `index.html` / `manifest.json`. Three release channels. `promote_release` in `shared/build-lib.sh` is the single point of truth for what each cut produces; the cascade rule keeps downstream channel symlinks current automatically.
- **Stable** — versioned, immutable. `--release [version]` tags `<tool>-v<version>` in git and uploads `<tool>_v<version>.html` to the new Codeberg release. Skips automatically when there is no source change since the last stable tag. - **Stable** — versioned, immutable. `--release [version]` writes `website/releases/<tool>_v<X.Y.Z>.html` (real bytes), refreshes 5 symlinks (`<tool>_v<X.Y>.html`, `<tool>_v<X>.html`, `<tool>_stable.html`, `<tool>_beta.html`, `<tool>_alpha.html`) all → the new versioned file, and tags `<tool>-v<X.Y.Z>` in git. Skips automatically when there is no source change since the last stable tag.
- **Beta**`--release beta` tags `<tool>-v<next-patch>-beta.N` (auto-incrementing counter) and uploads `<tool>_v<next-patch>-beta.N.html`. The Codeberg release is marked `prerelease: true`. On-page label: `vX.Y.Z-beta · <date> · <sha>`. - **Beta**`--release beta` overwrites `<tool>_beta.html` with the dist HTML bytes (replacing the symlink with a real file if one was there). Cascades `<tool>_alpha.html``<tool>_beta.html` (symlink). No tag — channel URLs are stable URLs by design; counter tags would defeat that. On-page label: `vX.Y.Z-beta · <date> · <sha>` where X.Y.Z is the next-stable target.
- **Alpha**`--release alpha` tags `<tool>-v<next-patch>-alpha.N` and uploads, analogous to beta. - **Alpha**`--release alpha` overwrites `<tool>_alpha.html` with the dist HTML bytes. No tag, no other side-effects. On-page label: `vX.Y.Z-alpha · <date> · <sha>`.
A plain `sh tool/build.sh` (no `--release`) is a dev build: it produces `dist/<tool>.html` only, with the on-page label `vX.Y.Z-alpha · <full-ts> · <sha>[-dirty]`. No tag, no Codeberg upload. A plain `sh tool/build.sh` (no `--release`) is a dev build: it produces `dist/<tool>.html` only, with the on-page label `vX.Y.Z-alpha · <full-ts> · <sha>[-dirty]`. No write to `website/releases/`, no tag, no commit.
Stable releases do not automatically advance the alpha/beta channels. Use `./freshen-channel <tool> <channel>` (worktree-based, no manual `git checkout`) to cut a fresh `-alpha.N` / `-beta.N` from the current stable tag — channel discipline rule 4 says do this after every stable release. The cascade rule (stable cut → beta + alpha both reset to stable; beta cut → alpha resets to beta) means downstream channels are never stale. "No active beta" silently shows current stable; "no active alpha" silently shows current beta or stable. Operators don't need to run a freshen step after each stable release.
The on-page `{{BUILD_LABEL}}` is rendered red+bold for dev/alpha/beta builds (`is_red=1`) and black for stable releases. The label format is: The on-page `{{BUILD_LABEL}}` is rendered red+bold for dev/alpha/beta builds (`is_red=1`) and black for stable releases. The label format is:
@ -117,7 +125,7 @@ The on-page `{{BUILD_LABEL}}` is rendered red+bold for dev/alpha/beta builds (`i
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: 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:
- **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. - **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.
- **Level 2** (optional): site admin replaces `<deployment-root>/<tool>.html` with a stub that fetches `https://zddc.varasys.io/releases/manifest.json` to resolve `<tool>-<channel>` → tag, then fetches `https://zddc.varasys.io/releases/<tag>/<tool>_v<version>.html` (Caddy reverse-proxies to the Codeberg release-asset URL). Switches the whole site to a channel. Without it, `<deployment-root>/<tool>.html` is just the actual built tool HTML (self-contained install). - **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. `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.

View file

@ -16,37 +16,38 @@ If something in this CLAUDE.md conflicts with those, those win — and please up
This is a **monorepo of independent tools**, not one application: 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. Released as cross-compiled binaries on Codeberg (no container image — the chart Dockerfiles compile 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` (the create-release-and-upload-asset helper used by both `build-lib.sh` and `zddc/release.sh`). - `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/` — published artifacts: `index.html` (root URL), `releases/index.html` + `releases/manifest.json` (regenerated by `build.sh` from the Codeberg API; the manifest maps `<tool>-<channel>` → tag for runtime channel resolution), and `bootstrap/{level1,track-stable,track-beta,track-alpha}/<tool>.html` (per-channel level-2 stubs + same-origin level-1 stubs that the home-page install snippets curl into the operator's deployment dir). The actual built tool HTML and zddc-server binaries live on Codeberg as release assets — Caddy at `zddc.varasys.io` reverse-proxies `/releases/<tag>/<asset>` to the corresponding Codeberg URL. `bootstrap/` is regenerated by every plain `sh build.sh`. 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 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/`.
- `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 releases index/manifest sh build.sh # build all five HTML tools (dist/ only) + regen website/releases/index.html + bootstrap stubs
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; tag <tool>-vX.Y.Z and upload <tool>_vX.Y.Z.html to Codeberg 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 build; tag <tool>-vX.Y.Z-{alpha,beta}.N and upload to Codeberg 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)
./freshen-channel <tool> <channel> # rebuild alpha/beta from current stable tag (run after every stable release) ./freshen-channel <tool> <channel> # rebuild alpha/beta from current stable tag (run after every stable release if you want to advance the channel mirror)
npm test # all Playwright specs (build first!) npm test # all Playwright specs (build first!)
npx playwright test <tool> # one spec npx playwright test <tool> # one spec
./dev-server start # ./dev-server stop # cache-busting HTTP on :8000 ./dev-server start # ./dev-server stop # cache-busting HTTP on :8000
# zddc/ Go server (separate sub-project, not part of sh build.sh) # zddc/ Go server (separate sub-project, not part of sh build.sh)
(cd zddc && go test ./...) # unit tests (Go 1.24+) (cd zddc && go test ./...) # unit tests (Go 1.24+)
sh zddc/release.sh [alpha|beta|stable] [<version>] # cut + publish zddc-server binaries to Codeberg release assets (default: alpha; auto-derives version) sh zddc/release.sh [<version>] # cut stable zddc-server release; tags + cross-compiles binaries + uploads to Codeberg. Stable-only (no alpha/beta channel for binaries).
``` ```
No lint/typecheck/format commands exist for the HTML tools — vanilla JS + POSIX sh by design. No lint/typecheck/format commands exist for the HTML tools — vanilla JS + POSIX sh by design.
## Things that bite if you forget ## Things that bite if you forget
- **`dist/` is gitignored.** `tool/dist/<tool>.html` is the canonical built artifact for testing and as the source for `--release` uploads. Never hand-edit a `dist/` file. - **`dist/` is gitignored.** `tool/dist/<tool>.html` is the canonical built artifact for testing and as the source for `--release` writes. Never hand-edit a `dist/` file.
- **Codeberg is the canonical store for release assets.** `--release` is the only path to publishing — it tags the commit and uploads the dist HTML (or zddc-server binaries) to Codeberg via `shared/publish-codeberg-release.sh`. Nothing under `website/releases/` other than `index.html` and `manifest.json` is checked in; the `*.html` per-version files and zddc-server binaries are gitignored. Don't reintroduce them. - **`website/releases/` is committed.** Per-version files (`<tool>_v<X.Y.Z>.html`) are real immutable files; partial-version pins (`<tool>_v<X.Y>.html`, `<tool>_v<X>.html`) and channels (`<tool>_<channel>.html`) are checked-in symlinks. Stable cuts write the new versioned file + refresh the 5 symlinks (cascade rule: stable cut → beta + alpha both reset to the new stable). Beta/alpha cuts overwrite their channel mirror in place; on a beta cut, alpha cascades to point at beta.
- **`$CODEBERG_TOKEN` must be exported** before any `--release` invocation. Without it, `publish_codeberg_release` aborts with a clear error. - **No tags for alpha/beta.** Channel URLs are stable URLs by design — appending counter tags would defeat the purpose. The on-page label encodes `<date> · <sha>` for traceability. Stable cuts still get clean `<tool>-vX.Y.Z` tags.
- **Pre-release semver for alpha/beta.** On-page label embeds `vX.Y.Z-{alpha,beta}` where X.Y.Z is the next-stable target (patch+1 from latest clean `<tool>-vX.Y.Z` tag). Every alpha/beta cut is tagged with a `.N` counter (`<tool>-v0.0.3-alpha.1`, `.2`, …) that resets when the next stable is cut. Stable tags are clean `<tool>-vX.Y.Z`. - **Pre-release semver in the on-page label.** Plain dev builds and `--release alpha|beta` cuts embed `vX.Y.Z-{alpha,beta}` in `{{BUILD_LABEL}}` where X.Y.Z is the next-stable target (patch+1 from latest clean `<tool>-vX.Y.Z` tag). Plain dev adds a full timestamp + `-dirty` marker; `--release alpha|beta` is date-only.
- **Plain `sh tool/build.sh` is a dev build.** The on-page label says `vX.Y.Z-alpha · <full-ts> · <sha>[-dirty]` (full timestamp + dirty marker distinguish iterative dev work from a formal `--release alpha` cut), but no Codeberg upload happens. To publish, re-run with `--release alpha`. - **Plain `sh tool/build.sh` is a dev build.** Writes `dist/<tool>.html` only; no `website/releases/` side-effect. To publish, re-run with `--release alpha`.
- **Always build before running tests** — Playwright opens `dist/tool.html` via `file://`. - **Always build before running tests** — Playwright opens `dist/tool.html` via `file://`.
- **`</` in JS string/template literals breaks inline `<script>`** embedding. `shared/build-lib.sh` provides `escape_js_close_tags`; every tool's `build.sh` runs JS through it before inlining. - **`</` in JS string/template literals breaks inline `<script>`** embedding. `shared/build-lib.sh` provides `escape_js_close_tags`; every tool's `build.sh` runs JS through it before inlining.
- **All ZDDC parsing/formatting/hashing goes through `window.zddc`** (from `shared/zddc.js` + `shared/hash.js` + `shared/zddc-filter.js`). API: `parseFilename`, `parseFolder`, `parseRevision`, `formatFilename`, `formatFolder`, `compareRevisions`, `isValidStatus`, `splitExtension`, `joinExtension`, `crypto.{sha256Hex, sha256String, sha256File, bytesToHex}`, `filter.{parse, matches}`. File objects across tools use `trackingNumber` (string) and `extension` (string, **no leading dot** — use `zddc.joinExtension(name, ext)` to build a filename). Add edge cases to `tests/zddc.spec.js`, not per-tool tests. - **All ZDDC parsing/formatting/hashing goes through `window.zddc`** (from `shared/zddc.js` + `shared/hash.js` + `shared/zddc-filter.js`). API: `parseFilename`, `parseFolder`, `parseRevision`, `formatFilename`, `formatFolder`, `compareRevisions`, `isValidStatus`, `splitExtension`, `joinExtension`, `crypto.{sha256Hex, sha256String, sha256File, bytesToHex}`, `filter.{parse, matches}`. File objects across tools use `trackingNumber` (string) and `extension` (string, **no leading dot** — use `zddc.joinExtension(name, ext)` to build a filename). Add edge cases to `tests/zddc.spec.js`, not per-tool tests.

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 Codeberg release assets, browsable at <https://zddc.varasys.io/releases/>. Append `?v=alpha` (or `?v=0.0.4`, etc.) 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/>. 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.
## File-naming convention ## File-naming convention
@ -42,7 +42,7 @@ npm install && npx playwright install chromium && npm test # tests
./dev-server start # cache-busting HTTP on :8000 ./dev-server start # cache-busting HTTP on :8000
``` ```
Authoritative build/release docs are in [`AGENTS.md`](AGENTS.md). Architecture notes (single-file rationale, JS module pattern, security model) are in [`ARCHITECTURE.md`](ARCHITECTURE.md). zddc-server (optional Go HTTP server with ACL and a virtual archive index) is in [`zddc/README.md`](zddc/README.md). Authoritative build/release docs are in [`AGENTS.md`](AGENTS.md). Architecture notes (single-file rationale, JS module pattern, security model) are in [`ARCHITECTURE.md`](ARCHITECTURE.md). zddc-server (optional Go HTTP server with ACL and a virtual archive index) is in [`zddc/README.md`](zddc/README.md). Example Helm charts for deploying zddc-server (production + dev) are under [`helm/`](helm/).
## Contributing ## Contributing

View file

@ -4,11 +4,16 @@ ZDDC tools (archive, transmittal, classifier, mdedit, landing) are single-file
HTML bundles. The bootstrap pattern lets you install once on a deployment and HTML bundles. The bootstrap pattern lets you install once on a deployment and
update by editing a few lines, without re-uploading multi-megabyte HTML files. update by editing a few lines, without re-uploading multi-megabyte HTML files.
End users install via a short copy-paste shell snippet from the home page's End users install via the unified [`install.sh`](install.sh) script, served at
"Install on your server" section. The snippet uses `curl` to fetch either `https://zddc.varasys.io/install.sh`. The script handles all three deployment
the current stable HTMLs (self-contained) or tiny level-2 stubs (channel patterns (self-contained / channel-tracking / pin-to-version) plus both target
trackers) into the deployment directory. The published stubs live under shapes (deployment root or project subdirectory) via a single command:
`https://zddc.varasys.io/bootstrap/`:
```sh
sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" -- [options]
```
The published stubs live under `https://zddc.varasys.io/bootstrap/`:
- `bootstrap/level1/<tool>.html` — same-origin level-1 stubs (4 tools, no - `bootstrap/level1/<tool>.html` — same-origin level-1 stubs (4 tools, no
landing — landing only lives at deployment root). landing — landing only lives at deployment root).
@ -16,7 +21,8 @@ trackers) into the deployment directory. The published stubs live under
stubs (5 tools each). stubs (5 tools each).
Both directories are produced by the project's top-level `build.sh` from Both directories are produced by the project's top-level `build.sh` from
`bootstrap/level{1,2}.html.tmpl`. `bootstrap/level{1,2}.html.tmpl`. `install.sh` orchestrates which stubs to
fetch and where to put them based on `--mode` / `--target` / `--channel`.
## The two-level model ## The two-level model
@ -46,18 +52,18 @@ A typical `zddc-server` deployment looks like this:
- **At deployment root** (`<ZDDC_ROOT>/<tool>.html`), put either: - **At deployment root** (`<ZDDC_ROOT>/<tool>.html`), put either:
- the actual built tool HTML — fully self-contained install, no external - the actual built tool HTML — fully self-contained install, no external
dependencies; or dependencies; or
- a level-2 bootstrap — resolves the desired channel against - a level-2 bootstrap — fetches `<source>/releases/<tool>_<channel>.html`
`https://zddc.varasys.io/releases/manifest.json` (or skips that step for an directly (that path is a checked-in symlink on the upstream that resolves
explicit `?v=X.Y.Z` pin) and fetches the asset from to the current channel mirror). No manifest lookup, no version arithmetic,
`https://zddc.varasys.io/releases/<tag>/<tool>_v<X.Y.Z>.html`. Caddy at no Codeberg proxy magic — every URL is a real static file or a static
`zddc.varasys.io` reverse-proxies that to the corresponding Codeberg symlink chain.
release-asset URL.
The site administrator switches the whole site to a channel by re-running The site administrator switches the whole site to a channel by re-running
the `track-<channel>` install snippet from the home page — that overwrites `install.sh --mode track --channel <name>` — that overwrites the root
the root `<tool>.html` files with the matching level-2 stubs. A single `<tool>.html` files with the matching level-2 stubs. A single project can
project can override one tool by editing just `<project-X>/<tool>.html` override one tool by editing just `<project-X>/<tool>.html` (replace the
(replace the relative `upstream` URL with an absolute zddc.varasys.io URL). relative `upstream` URL with an absolute zddc.varasys.io URL pointing at
the desired version, channel mirror, or partial-version pin).
## Why two levels ## Why two levels
@ -76,18 +82,19 @@ preferences stay scoped to the deployment.
There are two ways to choose a version: edit the stub for a permanent There are two ways to choose a version: edit the stub for a permanent
pin, or pass a `?v=` URL parameter for a per-request override. pin, or pass a `?v=` URL parameter for a per-request override.
### 1. Permanent pin (edit the stub) ### 1. Permanent pin (point the stub at a fixed URL)
The level-2 stub resolves channels via `manifest.json` at runtime, so a The default level-2 stub fetches `<source>/releases/<tool>_<channel>.html`
"pin to current stable" is the default — no editing required. To pin which is itself a symlink on the upstream that resolves to the current
this single tool to a specific version permanently, replace the level-2 channel mirror. To pin permanently, change the URL inside the stub:
stub with one that bypasses the manifest:
| To pin | Replace stub body with a fetch of | | Target stability | URL the stub should fetch |
|-----------------------------------------|-------------------------------------------------------------------------| |----------------------------------|------------------------------------------------------------------------|
| Exact stable version `vX.Y.Z` | `https://zddc.varasys.io/releases/<tool>-vX.Y.Z/<tool>_vX.Y.Z.html` | | Exact stable version | `https://zddc.varasys.io/releases/<tool>_vX.Y.Z.html` |
| Specific alpha/beta build | `https://zddc.varasys.io/releases/<tool>-vX.Y.Z-alpha.N/<tool>_vX.Y.Z-alpha.N.html` | | Latest patch within `<X.Y>.*` | `https://zddc.varasys.io/releases/<tool>_v<X.Y>.html` (symlink) |
| Channel default (current implementation)| Resolves `<tool>-<channel>` against `releases/manifest.json` at runtime | | Latest within `<X>.*.*` | `https://zddc.varasys.io/releases/<tool>_v<X>.html` (symlink) |
| Track stable channel (default) | `https://zddc.varasys.io/releases/<tool>_stable.html` (symlink) |
| Track beta / alpha channel | `https://zddc.varasys.io/releases/<tool>_<channel>.html` |
### 2. Per-request `?v=` parameter ### 2. Per-request `?v=` parameter
@ -95,25 +102,28 @@ Both stub levels honor a `?v=` URL parameter. The parameter survives the
`document.write()` chain, so it flows through level-1 → level-2 → `document.write()` chain, so it flows through level-1 → level-2 →
upstream automatically. upstream automatically.
| URL parameter | Behavior | | URL parameter | Resolves to |
|--------------------------------|-------------------------------------------------------| |--------------------------------|-------------------------------------------------------|
| `?v=0.0.4` (or `?v=v0.0.4`) | tries `<tool>_v0.0.4.html` locally, then upstream | | `?v=0.0.4` (or `?v=v0.0.4`) | `<tool>_v0.0.4.html` (exact) |
| `?v=alpha` | switches to alpha channel | | `?v=0.0` (or `?v=v0.0`) | `<tool>_v0.0.html` (latest 0.0.x patch — symlink) |
| `?v=beta` | switches to beta channel | | `?v=0` (or `?v=v0`) | `<tool>_v0.html` (latest 0.x — symlink) |
| `?v=latest` | latest stable | | `?v=stable` | `<tool>_stable.html` |
| (omitted) | the default baked into the stub | | `?v=beta` | `<tool>_beta.html` |
| `?v=alpha` | `<tool>_alpha.html` |
| (omitted) | the default channel baked into the stub at install time |
When level-1 has `?v=…`, it tries `../<tool>_<suffix>.html` first (useful When level-1 has `?v=…`, it tries `../<tool>_<suffix>.html` first (useful
when the admin has staged specific versions locally) and falls back to when the admin has staged specific versions locally — the upstream's
`../<tool>.html` if 404 — which then forwards the parameter via level-2 symlink layout works the same locally) and falls back to `../<tool>.html`
if one is installed. So the same URL works whether the version is if 404 — which then forwards the parameter via level-2 if one is installed.
staged locally, served by a level-2 stub, or both.
Stable releases are immutable. Alpha and beta channel files are Stable per-version files are immutable. The `<tool>_stable.html`,
overwritten in place each time their channel is rebuilt; expect them to `<tool>_beta.html`, and `<tool>_alpha.html` symlinks (or real bytes when a
change without notice. The build label rendered on the tool page tells channel has active dev) get updated whenever the relevant channel
you what you are running (date + commit SHA for alpha/beta, version advances upstream — expect them to change. The build label rendered on
number for stable). the tool page tells you exactly which build you're seeing
(`v<next-stable>-{alpha,beta} · <date> · <sha>` for channel mirrors,
`v<X.Y.Z>` for pinned stables).
## Auditing what's installed ## Auditing what's installed
@ -128,13 +138,11 @@ grep -rn "fallback\|upstream" <ZDDC_ROOT>
A level-2 fetch is cross-origin (deployment → `zddc.varasys.io`). The A level-2 fetch is cross-origin (deployment → `zddc.varasys.io`). The
upstream must serve `Access-Control-Allow-Origin: *` (or a list including upstream must serve `Access-Control-Allow-Origin: *` (or a list including
your deployment origin) on `manifest.json` and on each released asset. your deployment origin) on each released asset. Verify with:
Verify with:
```sh ```sh
curl -I https://zddc.varasys.io/releases/manifest.json | grep -i access-control curl -I https://zddc.varasys.io/releases/archive_stable.html | grep -i access-control
curl -I https://zddc.varasys.io/releases/archive-v0.0.2/archive_v0.0.2.html \ curl -I https://zddc.varasys.io/releases/archive_v0.0.2.html | grep -i access-control
| grep -i access-control
``` ```
Level-1 fetches are same-origin so no CORS is involved. Level-1 fetches are same-origin so no CORS is involved.

View file

@ -15,13 +15,12 @@ A purpose-built HTTPS file server for ZDDC document archives. Designed to replac
## Quick Start ## Quick Start
zddc-server ships as a cross-compiled binary distributed via Codeberg release assets. The website at `zddc.varasys.io` reverse-proxies download URLs to Codeberg. zddc-server ships as a cross-compiled binary distributed via Codeberg release assets.
```sh ```sh
# Download the latest stable binary for your platform from # Pick a tag from https://codeberg.org/VARASYS/ZDDC/releases (filter by zddc-server-v*)
# https://zddc.varasys.io/releases/ (page lists all platforms + channels)
curl -L -o zddc-server \ curl -L -o zddc-server \
https://zddc.varasys.io/releases/zddc-server-vX.Y.Z/zddc-server-linux-amd64 https://codeberg.org/VARASYS/ZDDC/releases/download/zddc-server-vX.Y.Z/zddc-server-linux-amd64
chmod +x zddc-server chmod +x zddc-server
# Run against your archive root (HTTPS on :8443 with an in-memory self-signed cert) # Run against your archive root (HTTPS on :8443 with an in-memory self-signed cert)
@ -39,7 +38,10 @@ ZDDC_ROOT=/srv/archive ./zddc-server
For plain HTTP behind a reverse proxy, set `ZDDC_TLS_CERT=none` and `ZDDC_INSECURE_DIRECT=1` — see "TLS" below. For plain HTTP behind a reverse proxy, set `ZDDC_TLS_CERT=none` and `ZDDC_INSECURE_DIRECT=1` — see "TLS" below.
There is no Containerfile / Dockerfile / compose file in this repo. Operators who want to run zddc-server inside a container can write a minimal Dockerfile that copies the static binary into a `scratch` or `alpine` base, or use the chart Dockerfiles in `tnd-zddc-chart` (which compile from source at build time). There is no Containerfile / Dockerfile / compose file in this repo. Two ways to run zddc-server in Kubernetes / containers:
- The example Helm charts under [`helm/`](../helm/) (`zddc-server-prod/` for stable / `zddc-server-dev/` for tracking main HEAD) compile zddc-server from source via init container — no image registry needed.
- Roll your own image: copy the static binary into a `FROM scratch` or `FROM alpine` base in a few lines.
## Environment Variables ## Environment Variables
@ -235,16 +237,21 @@ callers.
## Landing Page and Tool Install ## Landing Page and Tool Install
The recommended install is a short shell snippet copy-pasted from the The recommended install is the unified [`install.sh`](https://zddc.varasys.io/install.sh) script — `cd` into `ZDDC_ROOT/` and run:
"Install on your server" section of `https://zddc.varasys.io/`. There
are four snippets, all of which `cd`-and-curl into `ZDDC_ROOT/`:
- **Self-contained** — fetches the five current-stable tool HTMLs and ```sh
populates a `_template/` directory of level-1 bootstrap stubs. No sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" # self-contained, current stable (default)
runtime dependency on `zddc.varasys.io`. Re-run to update. sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" -- --mode track --channel beta # track beta
- **Track stable / beta / alpha** — fetches five tiny level-2 stubs sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" -- --channel 0.0.2 # pin to a specific version
(~10 KB total) that fetch the named channel from `zddc.varasys.io` ```
on every page load.
The script handles all three deployment patterns:
- **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 `<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: After running one of the snippets, the deployment looks like:
@ -368,7 +375,7 @@ the app will auto-connect and scan the directory tree.
## Distribution ## Distribution
Each release is a Codeberg git tag (`zddc-server-vX.Y.Z` for stable, `zddc-server-vX.Y.Z-{alpha,beta}.N` for pre-releases) with four pre-built binaries attached as release assets: Each stable release is a Codeberg git tag (`zddc-server-vX.Y.Z`) with four pre-built binaries attached as release assets:
| File | Platform | | File | Platform |
|---|---| |---|---|
@ -379,15 +386,15 @@ Each release is a Codeberg git tag (`zddc-server-vX.Y.Z` for stable, `zddc-serve
All binaries are statically linked (CGO disabled), built with `-trimpath -ldflags="-s -w -X main.version=<ver>"`. No runtime dependencies. All binaries are statically linked (CGO disabled), built with `-trimpath -ldflags="-s -w -X main.version=<ver>"`. No runtime dependencies.
Download URLs go through the website's Caddy proxy: Download URLs from Codeberg directly:
``` ```
https://zddc.varasys.io/releases/zddc-server-vX.Y.Z/zddc-server-linux-amd64 https://codeberg.org/VARASYS/ZDDC/releases/download/zddc-server-vX.Y.Z/zddc-server-linux-amd64
``` ```
(Caddy reverse-proxies that to the Codeberg release-asset URL — operators only ever talk to `zddc.varasys.io`.) Browse all versions at <https://zddc.varasys.io/releases/>. Browse all releases at <https://codeberg.org/VARASYS/ZDDC/releases>.
There is no container image. The chart Dockerfiles in `tnd-zddc-chart` compile zddc-server from source at build time, fetching the right tag from Codeberg directly. If you want your own image, copy the static binary into a `FROM scratch` or `FROM alpine` base in a few lines. There is no alpha/beta channel for binary distribution. Active dev/soak happens via the [`helm/zddc-server-dev/`](../helm/zddc-server-dev/) chart, which builds zddc-server from source on every pod restart against any commit you point it at. There is no container image; if you want your own, copy the static binary into a `FROM scratch` or `FROM alpine` base in a few lines, or use one of the helm charts which compile from source via init container.
### Env-var contract (for chart consumers) ### Env-var contract (for chart consumers)
@ -425,29 +432,23 @@ To run unit tests:
## Release tagging ## Release tagging
`sh zddc/release.sh` is the canonical path. It tags the commit, cross-compiles the four binaries (native Go), and uploads them as Codeberg release assets via the shared `publish-codeberg-release.sh` helper. `sh zddc/release.sh` is the canonical path. **Stable cuts only.** The script tags the commit, cross-compiles the four binaries (native Go), and uploads them as Codeberg release assets via the shared `publish-codeberg-release.sh` helper.
```sh ```sh
sh zddc/release.sh # alpha cut, version auto-derived (default) sh zddc/release.sh # patch-bump from latest clean stable tag
sh zddc/release.sh alpha # same sh zddc/release.sh 0.1.0 # explicit version
sh zddc/release.sh beta # beta cut
sh zddc/release.sh stable # stable cut, patch++ from latest stable
sh zddc/release.sh stable 0.1.0 # stable cut, explicit version
``` ```
**Default channel is `alpha`** so a stable-equivalent tag never appears by accident during active development. Pass `beta` to soak; pass `stable` only when deliberately promoting. The script tags the commit but does NOT push — finish with `git push origin <branch>` and `git push origin <tag>`. The script tags the commit but does NOT push — finish with `git push origin main` and `git push origin <tag>`.
Prerequisites: Prerequisites:
- Go 1.24+ on PATH. - Go 1.24+ on PATH.
- `$CODEBERG_TOKEN` exported, scoped to write the VARASYS/ZDDC repo. Generate one at <https://codeberg.org/user/settings/applications>. - `$CODEBERG_TOKEN` exported, scoped to write the VARASYS/ZDDC repo. Generate one at <https://codeberg.org/user/settings/applications>.
After the script returns successfully, regenerate the website's versions index from the new release list: After the script returns successfully, the website's versions index doesn't need updating for zddc-server (it links out to the Codeberg release page directly). Just push:
```sh ```sh
sh build.sh
git add website/releases/index.html website/releases/manifest.json
git commit -m "release: zddc-server vX.Y.Z"
git push origin main git push origin main
git push origin zddc-server-vX.Y.Z git push origin zddc-server-vX.Y.Z
``` ```
@ -456,18 +457,9 @@ Single-developer / solo-release flow by design — no CI babysitting, no separat
### Versioning ### Versioning
Pre-release semver. Stable cuts get clean `vX.Y.Z` tags. Alpha and beta cuts get `vX.Y.Z-alpha.N` / `vX.Y.Z-beta.N` where `X.Y.Z` is the next patch of the latest clean stable and `N` is a per-channel counter that resets when stable advances. Clean semver. Stable cuts get `<tool>-vX.Y.Z` tags. There are no alpha/beta channel tags for zddc-server — channel URLs are stable URLs by design (counters defeat that), and zddc-server has no static-asset distribution layer where channel mirrors would matter. Active dev runs via `helm/zddc-server-dev/`, which builds from source on each rollout.
``` The two existing `zddc-server-v0.0.8-alpha.1` and `zddc-server-v0.0.8-alpha.2` tags from a previous experiment stay as historical artifacts; no new alpha/beta tags are created going forward.
alpha → v0.0.8-alpha.1
alpha → v0.0.8-alpha.2
beta → v0.0.8-beta.1
alpha → v0.0.8-alpha.3 (alpha and beta count separately)
stable → v0.0.8 (counter resets at next-patch advance)
alpha → v0.0.9-alpha.1
```
Pre-release semver ordering (`0.0.8-alpha.1 < 0.0.8-alpha.2 < 0.0.8-beta.1 < 0.0.8`) is honored by all standard tooling Codeberg release sorting, `git tag --sort=-v:refname`, `sort -V`, npm, cargo so consumers can pin or compare versions without surprises.
--- ---