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
```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
# Build single tool
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 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 beta
@ -55,25 +55,37 @@ shared/
sourced by every tool's build.sh via: . "$root_dir/../shared/build-lib.sh"
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/
index.html versions index, regenerated by build.sh from the Codeberg release list
manifest.json <tool>-<channel> → tag map (regenerated by build.sh; consumed by the level-2 stub at runtime)
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>.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/
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
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/
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`)
@ -162,37 +174,47 @@ Format: `trackingNumber_revision (status) - title.extension`
### 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).
- **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`.
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.
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)
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.
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.
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.
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.
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`.
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.
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.
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).
5. **Cascade is automatic.** A stable cut resets beta and alpha symlinks → stable. A beta cut resets alpha → beta. So "no active beta" silently shows current stable and "no active alpha" silently shows current beta. Operators don't need to run a freshen step after a stable release; the cascade handles it.
6. **Hotfix path.** For critical bugs: fix on `main`, cut a new stable. Tag the commit message `fix:` or include "hotfix" so intent is visible in `git log`.
7. **Beta soak before promoting (recommended).** Give a beta a few days of exposure before cutting the same code as stable. Not enforced; use judgment for trivial changes.
### 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
./freshen-channel archive alpha
@ -203,21 +225,19 @@ What it does:
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.
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.
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`.
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.
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
`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/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.
@ -290,59 +310,18 @@ ZDDC_ROOT=/path/to/your/archive ZDDC_TLS_CERT=none ZDDC_ADDR=:8080 \
### Release tagging
`zddc/release.sh` is the canonical path. It tags the commit, compiles
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).
`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.
```sh
sh zddc/release.sh # alpha cut, version auto-derived
sh zddc/release.sh alpha # same
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
sh zddc/release.sh # patch-bump from latest clean stable tag
sh zddc/release.sh 0.1.0 # 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>`.
**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. Example
sequence (current stable v0.0.7):
**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.
```
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.
**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.
Prerequisites:
- 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
```
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/
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/
index.html # versions index, regenerated by build.sh from the Codeberg API
manifest.json # <tool>-<channel> → tag map, regenerated by build.sh; the level-2 stub fetches this
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>.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/
level1/<tool>.html # same-origin stubs for project subdirectories
track-{alpha,beta,stable}/ # per-channel level-2 stubs (5 tools each)
level1/<tool>.html # same-origin stubs for project subdirectories
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.
@ -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` |
| 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.
@ -85,21 +93,21 @@ Each tool's `build.sh`:
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
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
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.
- **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>`.
- **Alpha**`--release alpha` tags `<tool>-v<next-patch>-alpha.N` and uploads, analogous to beta.
- **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` 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` 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:
@ -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:
- **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.

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:
- `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).
- `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`).
- `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/`.
- `zddc/` — Go HTTP server (separate sub-project; Go 1.24+). Serves `ZDDC_ROOT/index.html` at `GET /` as the landing page; `Accept: application/json` on `/` returns the ACL-filtered project list. Stable releases ship as cross-compiled binaries on Codeberg release assets; the `helm/` charts in this repo build from source at deploy time.
- `shared/``base.css` plus shared JS modules (`zddc.js`, `hash.js`, `zddc-filter.js`, `theme.js`, `help.js`) included by every tool's build, `build-lib.sh` (POSIX sh helpers sourced by every tool's `build.sh`), and `publish-codeberg-release.sh` (used by `zddc/release.sh` to upload zddc-server binaries — HTML tools no longer use it).
- `website/` — committed static site: `index.html` (root URL, hand-edited intro), `releases/<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)
## Most-used commands
```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 --release [version] # cut stable; tag <tool>-vX.Y.Z and upload <tool>_vX.Y.Z.html to Codeberg
sh tool/build.sh --release alpha|beta # cut channel build; tag <tool>-vX.Y.Z-{alpha,beta}.N and upload to Codeberg
./freshen-channel <tool> <channel> # rebuild alpha/beta from current stable tag (run after every stable release)
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)
./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!)
npx playwright test <tool> # one spec
./dev-server start # ./dev-server stop # cache-busting HTTP on :8000
# zddc/ Go server (separate sub-project, not part of sh build.sh)
(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.
## 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.
- **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.
- **`$CODEBERG_TOKEN` must be exported** before any `--release` invocation. Without it, `publish_codeberg_release` aborts with a clear error.
- **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`.
- **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`.
- **`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.
- **`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.
- **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 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.** 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://`.
- **`</` 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.

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. |
| **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
@ -42,7 +42,7 @@ npm install && npx playwright install chromium && npm test # tests
./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

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
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
"Install on your server" section. The snippet uses `curl` to fetch either
the current stable HTMLs (self-contained) or tiny level-2 stubs (channel
trackers) into the deployment directory. The published stubs live under
`https://zddc.varasys.io/bootstrap/`:
End users install via the unified [`install.sh`](install.sh) script, served at
`https://zddc.varasys.io/install.sh`. The script handles all three deployment
patterns (self-contained / channel-tracking / pin-to-version) plus both target
shapes (deployment root or project subdirectory) via a single command:
```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
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).
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
@ -46,18 +52,18 @@ A typical `zddc-server` deployment looks like this:
- **At deployment root** (`<ZDDC_ROOT>/<tool>.html`), put either:
- the actual built tool HTML — fully self-contained install, no external
dependencies; or
- a level-2 bootstrap — resolves the desired channel against
`https://zddc.varasys.io/releases/manifest.json` (or skips that step for an
explicit `?v=X.Y.Z` pin) and fetches the asset from
`https://zddc.varasys.io/releases/<tag>/<tool>_v<X.Y.Z>.html`. Caddy at
`zddc.varasys.io` reverse-proxies that to the corresponding Codeberg
release-asset URL.
- a level-2 bootstrap — fetches `<source>/releases/<tool>_<channel>.html`
directly (that path is a checked-in symlink on the upstream that resolves
to the current channel mirror). No manifest lookup, no version arithmetic,
no Codeberg proxy magic — every URL is a real static file or a static
symlink chain.
The site administrator switches the whole site to a channel by re-running
the `track-<channel>` install snippet from the home page — that overwrites
the root `<tool>.html` files with the matching level-2 stubs. A single
project can override one tool by editing just `<project-X>/<tool>.html`
(replace the relative `upstream` URL with an absolute zddc.varasys.io URL).
`install.sh --mode track --channel <name>` — that overwrites the root
`<tool>.html` files with the matching level-2 stubs. A single project can
override one tool by editing just `<project-X>/<tool>.html` (replace the
relative `upstream` URL with an absolute zddc.varasys.io URL pointing at
the desired version, channel mirror, or partial-version pin).
## 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
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
"pin to current stable" is the default — no editing required. To pin
this single tool to a specific version permanently, replace the level-2
stub with one that bypasses the manifest:
The default level-2 stub fetches `<source>/releases/<tool>_<channel>.html`
which is itself a symlink on the upstream that resolves to the current
channel mirror. To pin permanently, change the URL inside the stub:
| To pin | Replace stub body with a fetch of |
|-----------------------------------------|-------------------------------------------------------------------------|
| Exact stable version `vX.Y.Z` | `https://zddc.varasys.io/releases/<tool>-vX.Y.Z/<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` |
| Channel default (current implementation)| Resolves `<tool>-<channel>` against `releases/manifest.json` at runtime |
| Target stability | URL the stub should fetch |
|----------------------------------|------------------------------------------------------------------------|
| Exact stable version | `https://zddc.varasys.io/releases/<tool>_vX.Y.Z.html` |
| Latest patch within `<X.Y>.*` | `https://zddc.varasys.io/releases/<tool>_v<X.Y>.html` (symlink) |
| 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
@ -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 →
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=alpha` | switches to alpha channel |
| `?v=beta` | switches to beta channel |
| `?v=latest` | latest stable |
| (omitted) | the default baked into the stub |
| `?v=0.0.4` (or `?v=v0.0.4`) | `<tool>_v0.0.4.html` (exact) |
| `?v=0.0` (or `?v=v0.0`) | `<tool>_v0.0.html` (latest 0.0.x patch — symlink) |
| `?v=0` (or `?v=v0`) | `<tool>_v0.html` (latest 0.x — symlink) |
| `?v=stable` | `<tool>_stable.html` |
| `?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 the admin has staged specific versions locally) and falls back to
`../<tool>.html` if 404 — which then forwards the parameter via level-2
if one is installed. So the same URL works whether the version is
staged locally, served by a level-2 stub, or both.
when the admin has staged specific versions locally — the upstream's
symlink layout works the same locally) and falls back to `../<tool>.html`
if 404 — which then forwards the parameter via level-2 if one is installed.
Stable releases are immutable. Alpha and beta channel files are
overwritten in place each time their channel is rebuilt; expect them to
change without notice. The build label rendered on the tool page tells
you what you are running (date + commit SHA for alpha/beta, version
number for stable).
Stable per-version files are immutable. The `<tool>_stable.html`,
`<tool>_beta.html`, and `<tool>_alpha.html` symlinks (or real bytes when a
channel has active dev) get updated whenever the relevant channel
advances upstream — expect them to change. The build label rendered on
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
@ -128,13 +138,11 @@ grep -rn "fallback\|upstream" <ZDDC_ROOT>
A level-2 fetch is cross-origin (deployment → `zddc.varasys.io`). The
upstream must serve `Access-Control-Allow-Origin: *` (or a list including
your deployment origin) on `manifest.json` and on each released asset.
Verify with:
your deployment origin) on each released asset. Verify with:
```sh
curl -I https://zddc.varasys.io/releases/manifest.json | grep -i access-control
curl -I https://zddc.varasys.io/releases/archive-v0.0.2/archive_v0.0.2.html \
| 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.html | grep -i access-control
```
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
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
# Download the latest stable binary for your platform from
# https://zddc.varasys.io/releases/ (page lists all platforms + channels)
# Pick a tag from https://codeberg.org/VARASYS/ZDDC/releases (filter by zddc-server-v*)
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
# 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.
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
@ -235,16 +237,21 @@ callers.
## Landing Page and Tool Install
The recommended install is a short shell snippet copy-pasted from the
"Install on your server" section of `https://zddc.varasys.io/`. There
are four snippets, all of which `cd`-and-curl into `ZDDC_ROOT/`:
The recommended install is the unified [`install.sh`](https://zddc.varasys.io/install.sh) script — `cd` into `ZDDC_ROOT/` and run:
- **Self-contained** — fetches the five current-stable tool HTMLs and
populates a `_template/` directory of level-1 bootstrap stubs. No
runtime dependency on `zddc.varasys.io`. Re-run to update.
- **Track stable / beta / alpha** — fetches five tiny level-2 stubs
(~10 KB total) that fetch the named channel from `zddc.varasys.io`
on every page load.
```sh
sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" # self-contained, current stable (default)
sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" -- --mode track --channel beta # track beta
sh -c "$(curl -fsSL https://zddc.varasys.io/install.sh)" -- --channel 0.0.2 # pin to a specific version
```
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:
@ -368,7 +375,7 @@ the app will auto-connect and scan the directory tree.
## 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 |
|---|---|
@ -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.
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)
@ -425,29 +432,23 @@ To run unit tests:
## 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 zddc/release.sh # alpha cut, version auto-derived (default)
sh zddc/release.sh alpha # same
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
sh zddc/release.sh # patch-bump from latest clean stable tag
sh zddc/release.sh 0.1.0 # 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:
- Go 1.24+ on PATH.
- `$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 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 zddc-server-vX.Y.Z
```
@ -456,18 +457,9 @@ Single-developer / solo-release flow by design — no CI babysitting, no separat
### 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.
```
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.
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.
---