# ZDDC Architecture This document is the single authoritative reference for how ZDDC tools are designed and built. It covers the shared single-file HTML application pattern, the build system, tool-specific architectural decisions, and contribution guidelines. --- ## Why Single-File HTML Applications Every ZDDC tool compiles to a single self-contained `.html` file — no servers, no installers, no subscriptions. | Principle | Rationale | |-----------|-----------| | **Reliability** | Opens in any modern Chromium-based browser without network access or external services | | **Portability** | Can be emailed, archived, or deployed to air-gapped environments with no tooling | | **Auditability** | Source, embedded data, and output travel together, satisfying ZDDC traceability requirements | | **Longevity** | Static assets remain functional long after build environments have changed | | **Simplicity** | A single `.html` file eliminates deployment steps and brittle dependency chains | --- ## Repository Structure Every HTML tool follows the same directory layout: ``` tool/ README.md # Feature scope, UI design, domain rules, help content css/ # Logically separated stylesheets (one responsibility per file) js/ # Vanilla ES modules (one responsibility per file) template.html # Shell markup with {{PLACEHOLDER}} markers for development build.sh # Inlines css/ and js/ into dist/tool.html dist/ tool.html # Generated output — never edit this manually ``` Website files (what `zddc.varasys.io` serves) live on a **separate Codeberg repo** (`codeberg.org/VARASYS/ZDDC-website`, typically cloned at `~/src/zddc-website/`) for hand-edited content, plus the **deploy host's `/srv/zddc/`** for the assembled live site. The system Caddy bind-mounts `/srv/zddc/`. `./deploy` rsyncs both into it. ``` ~/src/zddc-website/ (clone of codeberg.org/VARASYS/ZDDC-website) index.html # hand-edited intro page + install snippets (root URL) reference.html # hand-edited file-naming convention spec css/, js/, img/ # hand-edited static assets README.md, LICENSE # repo housekeeping # NO releases/ — release artifacts are NOT in any git history. ~/src/zddc/dist/release-output/ (gitignored, produced by ./build alpha|beta|release) index.html # download page, regenerated by build _v.html # real per-version HTML (immutable) _v.html → ... # symlink: latest patch within X.Y.* _v.html → ... # symlink: latest within X.*.* _stable.html → ... # symlink: current stable HTML _beta.html → ... # symlink to stable (or real bytes when active beta dev) _alpha.html → ... # symlink to beta/stable (or real bytes when active alpha dev) zddc-server_v_ # real per-version cross-compiled binary (raw bytes, no LFS) zddc-server_v_ → ... # symlink chain (mirrors the HTML cascade per platform) zddc-server_v_ → ... zddc-server__ → ... # channel mirror per platform zddc-server_.html # generated stub: cell link → fans out 4 platform downloads /srv/zddc/ (deploy host; Caddy bind-mount) index.html, reference.html, css/, js/, img/ ← rsync'd from ~/src/zddc-website/ releases/ ← rsync'd from ~/src/zddc/dist/release-output/ ``` `` ∈ {archive, transmittal, classifier, mdedit, landing, form, tables, browse}. `` ∈ {linux-amd64, darwin-amd64, darwin-arm64, windows-amd64.exe}. Every URL under `/releases/` resolves directly via the symlink chain — no `manifest.json`, no Caddy regex-rewrite, no JavaScript indirection, no third-party mirror. 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 reproducible from a tag, not in git** — `./build alpha|beta|release` cross-compiles them into `dist/release-output/`, `./deploy` rsyncs them to `/srv/zddc/releases/`, Caddy serves from there. Older versions: `git checkout zddc-server-v0.0.8 && ./build release 0.0.8`. The `helm/zddc-server-{prod,dev,cache}/` charts build from source via init container, but operators who want a prebuilt binary just `curl -O https://zddc.varasys.io/releases/zddc-server_stable_linux-amd64`. The single cell link per release points at `zddc-server_.html`, a small generated stub that surfaces all four platform downloads. To preview a build locally, open `dist/tool.html` directly via the dev server. To publish on `zddc.varasys.io`, cut a release with `./build alpha|beta|release` and then `./deploy`. Vendor dependencies (bundled third-party libraries) live in `tool/vendor/` if present. The build script is responsible for inlining them into the output. --- ## Documentation ownership Each topic has exactly one authoritative home; everything else links to it. | Topic | Single home | Linked from | |---|---|---| | What ZDDC is + tool channel links + dual-mode (local/server) overview + install snippets | `~/src/zddc-website/index.html` (hand-edited intro for `zddc.varasys.io/`, in the `ZDDC-website` repo) | repo `README.md`, `zddc/README.md` | | File-naming convention spec (status codes, modifiers, folder format) | `~/src/zddc-website/reference.html` | repo `README.md`, in-tool help text | | Versions + channel builds index of every tool | `dist/release-output/index.html` (regenerated by `./build`; deployed to `/srv/zddc/releases/index.html`) | website intro nav, "Browse all versions" link | | Customer-deployment install (`zddc-server` binary embeds current-stable tools; `.zddc apps:` cascade overrides; cache at `/_app/`) | `zddc/README.md` "Apps: virtual tool HTMLs" section | website intro, `AGENTS.md` | | zddc-server operations: env vars, ACL syntax, `.archive` URLs, container vs binary | `zddc/README.md` | `AGENTS.md`, website intro | | Build / release / channel commands | `AGENTS.md` | repo `README.md` ("see AGENTS.md") | | Architecture & internal patterns | `ARCHITECTURE.md` (this file) | `AGENTS.md` | | Per-tool internal design quirks | `/README.md` | (linked from website intro tool cards) | `index.html` in the `ZDDC-website` repo (working dir `~/src/zddc-website/index.html`) is **hand-edited static content** (analogous to `reference.html`), not the landing-tool output. The install section points operators at two paths: **local** (download a `.html` file from `/releases/`) and **server** (run `zddc-server`; current-stable builds of all eight HTML tools are baked into the binary at compile time via `//go:embed`). The landing tool's released bytes live at `/srv/zddc/releases/landing_v.html` (rsync'd from `dist/release-output/`); the embedded copy serves at the deployment root by default. The public website at `zddc.varasys.io/` is the same hand-edited `index.html` — its root URL is the introduction page, not the project picker (because there are no projects to pick from a static site). When updating documentation, prefer linking over duplicating. If you find yourself rewriting the file-naming convention in a tool's README, link to `reference.html` instead. --- ## Build System ### How It Works Each HTML tool's `build.sh`: 1. Reads CSS 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 `` to terminate the block — backslash escaping (`<\/script>`) does **not** prevent termination. Any JS source file or vendor library that contains `` sequences inside string literals or template literals will break the inline `` (not `<\/script>`) to close the `` or any `` in JS string literals | Breaks inline HTML embedding — escape with `'<' + '/tag>'` or use `<\/` in `sed` at build time | | No external dependencies at runtime | Self-contained output requirement | | No TypeScript, no bundlers | Keeps the build system auditable and simple | | Only `window.app` and `window.zddc` are global | Keeps the global namespace clean; expose only what's needed for debugging | | Defensive input validation | File System API handles and user-pasted data are untrusted | | Update README.md when features ship | Documentation parity is a delivery requirement, not optional | --- ## Git Workflow **Branching:** short-lived feature branches (`feature/`, `bugfix/`, `hotfix/`), squash-merged to `main` and immediately deleted. Quick fixes (typos, one-liners) go direct to `main`. **Commit messages:** Conventional Commits — `(): `. Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`. See `AGENTS.md` for the full table and examples. **Releases:** Tag the commit after confirming `dist/` is current. Format: `{project}-v{version}` (e.g. `archive-v1.0.0`). Semantic versioning applies. There is no CI/CD — the built `.html` file is already committed to the repo. ```bash bash tool/build.sh # rebuild dist/ git add -f tool/dist/tool.html # stage if needed git commit -m "chore(tool): rebuild for vX.Y.Z" git tag tool-vX.Y.Z git push origin main --tags git tag -l "archive-v*" # list releases git push origin :refs/tags/tag-name # delete a remote tag ``` --- ## Adding a New Tool 1. Create `tool/` with the standard directory layout 2. Write `template.html` with `{{CSS_PLACEHOLDER}}` and `{{JS_PLACEHOLDER}}` markers 3. Write `tool/build.sh` following the pattern of an existing tool 4. Add `bash "$SCRIPT_DIR/tool/build.sh"` to the root `build.sh` 5. Add a test project entry to `playwright.config.js` 6. Create a stub `tests/tool.spec.js` 7. Force-add the dist output: `git add -f tool/dist/tool.html` If the tool requires vendor dependencies, download them to `tool/vendor/`, add them to `.gitignore` exclusions if appropriate, and update `build.sh` to inline them (with the `