The 2dc9ad2 commit ("refactor: distribute via Codeberg release assets,
drop the upstream image") rewrote AGENTS.md and CLAUDE.md but left
several pre-existing references to the old write-to-website/releases
flow and the now-removed Containerfile / podman-compose / release-image.sh.
This sweeps the rest:
- CLAUDE.md
- drop "podman/podman-compose" from the zddc/ blurb (no Containerfile)
- drop the broken `podman build -t zddc-server zddc/` command
- rewrite the "Most-used commands" table so --release semantics match
actual behavior (tag + Codeberg upload, not file write)
- rewrite "Things that bite": replace "never write to website/releases/"
and the obsolete "alpha exception" bullet with the new rules
($CODEBERG_TOKEN required, dist files no longer force-tracked, etc.)
- rewrite the website/ description in "Repo shape" to reflect that
only index.html + manifest.json live there now
- ARCHITECTURE.md
- rewrite the website/ directory tree (no more <tool>_v*.html, _stable
symlinks, or _alpha/_beta files)
- rewrite "Channels" section: every cut now tags + uploads to Codeberg,
alpha/beta have .N counters and matching tags, no more in-place
overwrites
- rewrite the build-label table: dev builds carry the next-stable
target as a -alpha pre-release suffix with full timestamp + dirty
marker (was: "Built: <ts> BETA")
- update level-2 bootstrap description: resolves channel via
manifest.json, fetches /releases/<tag>/<asset>, not a flat URL
- update landing-tool description: ships only as Codeberg release
asset, not a committed website/releases/landing_v<X>.html
- AGENTS.md
- update website/ tree to the post-refactor layout
- replace the two-step podman build / podman-compose run blocks under
zddc-server with a Go build + go run quickstart (no container in
this repo)
- drop the "Containerfile uses a multi-stage build" note from the
"Notes" list (Containerfile is gone)
- drop the stale "landing/build.sh writes website/index.html" note —
website/index.html is now hand-edited, not produced by landing's
build
- README.md (top-level)
- tools table no longer links to /releases/<tool>_stable.html
(those URLs return 404 post-refactor); link to the releases page
once instead
- bootstrap/README.md
- update the "permanent pin" URL examples and CORS verification
snippet to use /releases/<tag>/<asset> URLs (Caddy → Codeberg)
instead of the old flat /releases/<tool>_<channel>.html pattern
- explain that channel resolution is via manifest.json now
- zddc/README.md
- rewrite Quick Start: download a release binary or build from source,
no `podman build`
- rewrite TLS examples to invoke ./zddc-server directly instead of
`podman run ... zddc-server` (image name no longer exists)
- mention ZDDC_INSECURE_DIRECT in the env-var table and the plain-HTTP
example — startup is refused without it on non-loopback binds
- replace the "Container image" section with "Distribution" (binaries
on Codeberg, no image) and the "Building" section with go build
instructions
- replace "Release Tagging" with documentation of zddc/release.sh
(the canonical replacement for release-image.sh, which is gone)
- shared/build-lib.sh
- fix the comment claiming "plain builds mirror to website/releases/"
— they don't anymore
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
477 lines
21 KiB
Markdown
477 lines
21 KiB
Markdown
# zddc-server
|
|
|
|
A purpose-built HTTPS file server for ZDDC document archives. Designed to replace
|
|
`caddy file-server --browse` with features specific to ZDDC workflows.
|
|
|
|
## Features
|
|
|
|
- **High-performance static file serving** — ETag, conditional GET, Cache-Control
|
|
- **Cascading `.zddc` ACL** — email-based allow/deny lists evaluated bottom-up from requested directory to root
|
|
- **Caddy-compatible JSON listings** — the Archive Browser works without modification
|
|
- **Virtual `.archive` index** — resolve the earliest revision of any tracked document by URL
|
|
- **Filesystem watcher** — archive index updates automatically when files change
|
|
- **Flexible TLS modes** — self-signed, real certificates, or plain HTTP
|
|
- **Single static binary** — CGO-free, no runtime dependencies; cross-compiled to Linux/macOS/Windows
|
|
|
|
## 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.
|
|
|
|
```sh
|
|
# Download the latest stable binary for your platform from
|
|
# https://zddc.varasys.io/releases/ (page lists all platforms + channels)
|
|
curl -L -o zddc-server \
|
|
https://zddc.varasys.io/releases/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)
|
|
ZDDC_ROOT=/srv/archive ./zddc-server
|
|
```
|
|
|
|
Or build from source (requires Go 1.24+):
|
|
|
|
```sh
|
|
git clone https://codeberg.org/VARASYS/ZDDC.git
|
|
cd ZDDC/zddc
|
|
go build -o zddc-server ./cmd/zddc-server
|
|
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).
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Default | Description |
|
|
|---|---|---|
|
|
| `ZDDC_ROOT` | *(required)* | Absolute path to the served file tree |
|
|
| `ZDDC_ADDR` | `:8443` | Bind address (host:port) |
|
|
| `ZDDC_TLS_CERT` | *(empty)* | Path to PEM certificate file. `none` = plain HTTP (no TLS); empty = generate self-signed |
|
|
| `ZDDC_TLS_KEY` | *(empty)* | Path to PEM private key file. Required when `ZDDC_TLS_CERT` is a file path; ignored otherwise |
|
|
| `ZDDC_INSECURE_DIRECT` | *(empty)* | Must be `1` when `ZDDC_TLS_CERT=none` and the bind address is non-loopback. Acknowledges that an authenticating reverse proxy is in front of zddc-server; without it, plain-HTTP non-loopback startup is refused |
|
|
| `ZDDC_LOG_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` |
|
|
| `ZDDC_INDEX_PATH` | `.archive` | URL path segment name for the virtual archive index |
|
|
| `ZDDC_EMAIL_HEADER` | `X-Auth-Request-Email` | HTTP request header containing the authenticated user's email (the oauth2-proxy / nginx auth-request convention) |
|
|
| `ZDDC_CORS_ORIGIN` | `https://zddc.varasys.io` | Comma-separated allowlist of origins permitted to make cross-origin requests. Empty value disables CORS entirely. Default lets ZDDC tools served from `zddc.varasys.io` (e.g. via the bootstrap pattern) call back into your deployed server. |
|
|
|
|
`ZDDC_TLS_CERT=none` disables TLS entirely (plain HTTP). Both cert and key must be set together when using real certificates.
|
|
|
|
### CORS
|
|
|
|
The default `ZDDC_CORS_ORIGIN=https://zddc.varasys.io` exists so the canonical
|
|
ZDDC tool builds (hosted at `zddc.varasys.io`) can call back into your
|
|
deployed `zddc-server` without extra configuration. If you self-host the
|
|
tools on your own domain (e.g. `tools.acme.com`), set:
|
|
|
|
```sh
|
|
ZDDC_CORS_ORIGIN=https://tools.acme.com
|
|
```
|
|
|
|
Multiple origins are comma-separated. To disable CORS entirely (e.g. when
|
|
all clients are same-origin), set `ZDDC_CORS_ORIGIN=` (empty value). The
|
|
middleware echoes the matched origin back per-request and sets
|
|
`Access-Control-Allow-Credentials: true` so the upstream-set
|
|
`X-Auth-Request-Email` header crosses the boundary.
|
|
|
|
## TLS
|
|
|
|
### Plain HTTP (no TLS)
|
|
|
|
Set `ZDDC_TLS_CERT=none` to run without TLS. Recommended when an upstream reverse proxy
|
|
(nginx, Caddy, Traefik) terminates external TLS and talks to zddc-server over plain HTTP
|
|
on a private network. zddc-server requires `ZDDC_INSECURE_DIRECT=1` for any non-loopback
|
|
bind in this mode — an explicit acknowledgement that an authenticating proxy sits in front:
|
|
|
|
```sh
|
|
ZDDC_ROOT=/srv/archive \
|
|
ZDDC_TLS_CERT=none \
|
|
ZDDC_ADDR=:8080 \
|
|
ZDDC_INSECURE_DIRECT=1 \
|
|
./zddc-server
|
|
```
|
|
|
|
When `ZDDC_TLS_CERT` / `ZDDC_TLS_KEY` are empty (or when using real certificates), zddc-server generates an ECDSA P-256
|
|
self-signed certificate in memory at startup. The certificate changes on every restart —
|
|
this is intentional and acceptable when an upstream reverse proxy terminates external TLS
|
|
and uses this server only for encrypted in-datacenter transport.
|
|
|
|
To use a real certificate (e.g. from Let's Encrypt or an internal CA):
|
|
|
|
```sh
|
|
ZDDC_ROOT=/srv/archive \
|
|
ZDDC_TLS_CERT=/etc/ssl/zddc/server.crt \
|
|
ZDDC_TLS_KEY=/etc/ssl/zddc/server.key \
|
|
./zddc-server
|
|
```
|
|
|
|
## Authentication
|
|
|
|
zddc-server does **not** perform authentication itself. It reads the user's email address
|
|
from a request header (default: `X-Auth-Request-Email`) that must be set by an upstream reverse proxy
|
|
(nginx, Caddy, Traefik, Azure Application Gateway, etc.) after authenticating the user.
|
|
|
|
If the header is absent, the user is treated as anonymous (empty email). A directory with
|
|
no `.zddc` rules is publicly accessible; a directory with an allowlist requires a matching
|
|
email.
|
|
|
|
## `.zddc` Access Control Files
|
|
|
|
Place a `.zddc` YAML file in any directory to control access. Rules cascade from parent
|
|
directories — child rules are appended to (not replaced by) parent rules.
|
|
|
|
```yaml
|
|
# Example .zddc file
|
|
acl:
|
|
allow:
|
|
- "*@mycompany.com" # all users at mycompany.com
|
|
- "contractor@partner.com" # specific external user
|
|
deny:
|
|
- "intern@mycompany.com" # override: block this specific user
|
|
```
|
|
|
|
### ACL evaluation order
|
|
|
|
Rules are evaluated **bottom-up**: starting at the requested directory and walking
|
|
toward the root. The first explicit match (allow or deny) at any level wins.
|
|
|
|
1. Check deny patterns at the current level — if email matches → **403 Forbidden**
|
|
2. Check allow patterns at the current level — if email matches → **allow**
|
|
3. No match at this level → walk up to parent directory and repeat
|
|
4. If no `.zddc` files were found anywhere in the chain → **allow** (public, no rules)
|
|
5. If `.zddc` files exist but email matched nothing → **403 Forbidden** (not on any list)
|
|
|
|
This model supports three user tiers in a single tree:
|
|
|
|
| Level | Rule | Effect |
|
|
|---|---|---|
|
|
| Root | `allow: ["*@company.com"]` | All company users see everything |
|
|
| Project dir | `allow: ["team@company.com"]` | Restricts to the project team |
|
|
| Vendor subdir | `allow: ["vendor@ext.com"]` | Grants a third-party access to their folder only |
|
|
|
|
A vendor navigating to their subdirectory is allowed by the deepest matching rule,
|
|
even if a higher-level rule would deny them.
|
|
|
|
### Glob patterns
|
|
|
|
`*` matches any sequence of characters within one side of the `@` boundary:
|
|
|
|
| Pattern | Matches |
|
|
|---|---|
|
|
| `*@mycompany.com` | Any user at mycompany.com |
|
|
| `alice@*` | alice at any domain |
|
|
| `*` | Any non-empty email |
|
|
| `alice@example.com` | Exact match only |
|
|
|
|
### Directory visibility
|
|
|
|
Directories for which the user lacks access are **omitted** from JSON listings entirely —
|
|
they are neither listed nor queryable. A direct request to a denied path returns `403`.
|
|
|
|
### Reserved hidden segments
|
|
|
|
Two prefixes are filtered from listings under `ZDDC_ROOT`:
|
|
|
|
- **`.`-prefixed** (e.g. `/.devshell/`, `/Project-A/.internal/notes.md`) — excluded
|
|
from listings **and** 404 on direct HTTP access. The recognized virtual prefixes
|
|
(`.archive`, `.admin`) are explicitly permitted through. This lets operators store
|
|
side-state (caches, dev-shell home dirs, snapshot staging) on the same volume
|
|
that's served, without exposing it.
|
|
- **`_`-prefixed** (e.g. `/_template/`) — excluded from listings only. Direct URL
|
|
access still works, so the `_template/` directory of bootstrap stubs created
|
|
by the install snippet is reachable but doesn't clutter the project picker.
|
|
Use this for operator-managed scaffolding the user shouldn't browse to but
|
|
might link to.
|
|
|
|
## Admin Debug Page
|
|
|
|
`zddc-server` exposes a built-in debug page at `/.admin/` for operators who can
|
|
push code/images but cannot `kubectl exec` into the running container. It surfaces:
|
|
|
|
- **`/.admin/whoami`** — every header on the current request, the configured email
|
|
header name, the value observed at that name, and the resolved email. This is the
|
|
first thing to look at when access logs show `email=anonymous` — it tells you
|
|
exactly which (if any) header the upstream proxy is sending.
|
|
- **`/.admin/config`** — the resolved `Config` (env vars). Equivalent to
|
|
`kubectl exec -- env | grep ^ZDDC_` for diagnosing chart / deployment overrides.
|
|
- **`/.admin/logs`** — recent log entries (last 500) from an in-memory ring buffer.
|
|
Optional `?level=info|warn|error|debug` and `?since=<RFC3339>` query params.
|
|
At `ZDDC_LOG_LEVEL=debug` every request also logs its full header map under
|
|
`msg=request headers` — useful for diagnosing proxy / SSO header passthrough
|
|
(e.g. confirming which header carries the email). Note: that dump includes
|
|
auth tokens and cookies; only enable debug in trusted environments.
|
|
- **`/.admin/`** — HTML dashboard that fetches the three JSON endpoints client-side.
|
|
|
|
### Authorization
|
|
|
|
Authorization is via an `admins:` list in the **root** `.zddc` file (`<ZDDC_ROOT>/.zddc`).
|
|
Patterns use the same glob syntax as `acl.allow` / `acl.deny`:
|
|
|
|
```yaml
|
|
admins:
|
|
- alice@mycompany.com
|
|
- "*@admin.mycompany.com"
|
|
acl:
|
|
allow:
|
|
- "*@mycompany.com"
|
|
```
|
|
|
|
Only the root-level `admins` entry is honored — subdirectory `.zddc` files'
|
|
`admins` keys are ignored. Otherwise anyone with subtree write access could
|
|
elevate themselves.
|
|
|
|
If the root `.zddc` has no `admins` list (or no `.zddc` exists), every admin
|
|
endpoint returns **404** to every caller. Non-admin requests also receive 404
|
|
(not 403) so the existence of the admin page is invisible to unauthorized
|
|
callers.
|
|
|
|
### Caveats
|
|
|
|
- Logs are in-memory and lost on restart. The buffer holds the most recent 500
|
|
records; for long-term audit, parse the stderr stream the way you already do.
|
|
- The page reads only configuration and request state — it does not modify anything.
|
|
- An interactive terminal is not yet available; that's planned as a follow-up
|
|
behind a separate `ZDDC_ADMIN_TERM=1` env-var gate so it stays opt-in.
|
|
|
|
## Landing Page and Tool Install
|
|
|
|
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/`:
|
|
|
|
- **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.
|
|
|
|
After running one of the snippets, the deployment looks like:
|
|
|
|
```
|
|
ZDDC_ROOT/
|
|
index.html ← landing page (current stable, or level-2 stub)
|
|
archive.html ← archive browser (likewise)
|
|
transmittal.html
|
|
classifier.html
|
|
mdedit.html
|
|
_template/ ← level-1 bootstrap stubs (self-contained snippet only);
|
|
rename a copy to <project-name>/ for each project
|
|
Project-001/
|
|
archive.html ← level-1 stub: fetches ../archive.html
|
|
transmittal.html
|
|
classifier.html
|
|
mdedit.html
|
|
Project-002/
|
|
…
|
|
```
|
|
|
|
The level-2 stubs require `zddc-server` to accept cross-origin requests
|
|
from `zddc.varasys.io`, controlled via `ZDDC_CORS_ORIGIN`. See
|
|
[`bootstrap/README.md`](../bootstrap/README.md) for the full install
|
|
guide and the `?v=…` URL parameter for per-request version selection.
|
|
|
|
The landing page fetches `GET /` (with `Accept: application/json`) to retrieve the list
|
|
of top-level project directories the requesting user has access to. It renders checkboxes
|
|
for each project and opens `archive.html?projects=Proj-A,Proj-B` when the user clicks
|
|
"Open Archive".
|
|
|
|
**Presets** (named project selections) are stored in the browser's `localStorage` — no
|
|
server-side state required.
|
|
|
|
**Shared URLs**: the `?projects=` parameter is preserved in the archive browser URL so
|
|
users can email direct links to a pre-filtered view. If the recipient does not have
|
|
access to a project listed in the URL, a warning banner is shown.
|
|
|
|
## Access Logging
|
|
|
|
Every HTTP request is logged as a structured `slog` entry at `INFO` level:
|
|
|
|
| Field | Description |
|
|
|---|---|
|
|
| `ts` | Request arrival timestamp (RFC3339) |
|
|
| `email` | User email from the configured header, or `anonymous` |
|
|
| `method` | HTTP method |
|
|
| `path` | URL path |
|
|
| `status` | HTTP response status code |
|
|
| `bytes` | Response body bytes written |
|
|
| `duration_ms` | Request duration in milliseconds |
|
|
|
|
Log output goes to `stderr`. Use `ZDDC_LOG_LEVEL=warn` to suppress access logs if needed,
|
|
or pipe `stderr` to a log aggregator.
|
|
|
|
## Virtual Archive Index (`.archive`)
|
|
|
|
Any URL path segment named `.archive` (configurable via `ZDDC_INDEX_PATH`) is intercepted
|
|
by the server and treated as a virtual document index.
|
|
|
|
The index is built at startup by scanning all transmittal folders under `ZDDC_ROOT`. It
|
|
maps each `(trackingNumber, revision, modifier)` tuple to the file from the
|
|
**chronologically earliest** transmittal folder that contains it.
|
|
|
|
### URL patterns
|
|
|
|
| URL | Resolves to |
|
|
|---|---|
|
|
| `GET /Project/.archive/TRK-001.html` | Latest base revision of TRK-001 |
|
|
| `GET /Project/.archive/TRK-001_A.html` | Base revision A of TRK-001 |
|
|
| `GET /Project/.archive/TRK-001_A+C1.html` | Modifier C1 of revision A of TRK-001 |
|
|
| `GET /Project/.archive/` | JSON listing of all resolvable trackingNumber.html entries |
|
|
|
|
All responses are `302 Found` redirects to the actual file URL. ACL is enforced on both
|
|
the `.archive` context directory and the resolved target file.
|
|
|
|
### Why "earliest" transmittal?
|
|
|
|
Any file claiming to be `TRK-001_A (IFC)` should be identical across transmittals
|
|
(same content, same SHA-256). If the same tracking number and revision appears in multiple
|
|
transmittals, the first one received chronologically is treated as the authoritative copy.
|
|
A later arrival with a different hash is an error condition (to be detected separately).
|
|
|
|
### Index refresh
|
|
|
|
The index refreshes automatically via an `fsnotify` filesystem watcher. Changes are
|
|
debounced by 2 seconds before the relevant transmittal folder is re-indexed.
|
|
|
|
> **Note for Azure Files**: Azure SMB mounts do not support `inotify`/`fsnotify` reliably.
|
|
> The watcher will log a warning and the index will only be updated by restarting the server.
|
|
|
|
## ZDDC Filename Convention
|
|
|
|
The server parses filenames following the ZDDC convention:
|
|
|
|
```
|
|
trackingNumber_revision (status) - title.extension
|
|
```
|
|
|
|
| Part | Format | Example |
|
|
|---|---|---|
|
|
| `trackingNumber` | No spaces or underscores | `123456-EL-SPC-2623` |
|
|
| `revision` | `~?[A-Z0-9]+(\+[CBNQ][0-9]+)?` | `A`, `~B`, `C+C1` |
|
|
| `status` | One of the valid status codes | `IFC`, `REC`, `---` |
|
|
| `title` | Free text | `Electrical Specification` |
|
|
|
|
Valid status codes: `IFA IFB IFC IFD IFI IFP IFR IFU REC RSA RSB RSC RSD RSI ---`
|
|
|
|
Transmittal folder format: `YYYY-MM-DD_trackingNumber (STATUS) - title`
|
|
|
|
## Integration with Archive Browser
|
|
|
|
The Archive Browser (`archive.html`) can connect to zddc-server in HTTP mode. The server
|
|
returns JSON directory listings in exactly the same format as Caddy's `file-server --browse`
|
|
— no changes to `archive/js/source.js` are needed.
|
|
|
|
To use: install `archive.html` at `ZDDC_ROOT/archive.html` (or any subdirectory) — either
|
|
the actual built tool fetched by the self-contained install snippet, or a
|
|
level-1/level-2 bootstrap stub that fetches it. Then open it via the zddc-server URL;
|
|
the app will auto-connect and scan the directory tree.
|
|
|
|
## 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:
|
|
|
|
| File | Platform |
|
|
|---|---|
|
|
| `zddc-server-linux-amd64` | Linux (x86-64) |
|
|
| `zddc-server-darwin-amd64` | macOS (Intel) |
|
|
| `zddc-server-darwin-arm64` | macOS (Apple Silicon) |
|
|
| `zddc-server-windows-amd64.exe` | Windows (x86-64) |
|
|
|
|
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:
|
|
|
|
```
|
|
https://zddc.varasys.io/releases/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/>.
|
|
|
|
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.
|
|
|
|
### Env-var contract (for chart consumers)
|
|
|
|
Downstream Helm charts and Compose files should set these explicitly:
|
|
|
|
| Variable | Typical value (behind ingress + SSO) | Purpose |
|
|
|---|---|---|
|
|
| `ZDDC_ROOT` | `/srv` | Path of the served archive (volume mount) |
|
|
| `ZDDC_TLS_CERT` | `none` | TLS terminated upstream |
|
|
| `ZDDC_INSECURE_DIRECT` | `1` | Acknowledge plain HTTP behind a trusted proxy |
|
|
| `ZDDC_ADDR` | `:8080` | Match service / probe port |
|
|
| `ZDDC_EMAIL_HEADER` | `X-Auth-Request-Email` | Header your auth proxy sets |
|
|
| `ZDDC_CORS_ORIGIN` | `https://your-host` | Origins permitted to call back into the server |
|
|
|
|
See "Environment Variables" above for the full list.
|
|
|
|
## Building from source
|
|
|
|
Requires Go 1.24+.
|
|
|
|
```sh
|
|
# Single binary for the host platform
|
|
(cd zddc && go build -o zddc-server ./cmd/zddc-server)
|
|
|
|
# All four release platforms (cross-compiled, statically linked)
|
|
sh build.sh # at the repo root — silently skips if Go isn't on PATH
|
|
# → outputs to zddc/dist/zddc-server-{linux,darwin,windows}-*
|
|
```
|
|
|
|
To run unit tests:
|
|
|
|
```sh
|
|
(cd zddc && go test ./...)
|
|
```
|
|
|
|
## 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
|
|
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
|
|
```
|
|
|
|
**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>`.
|
|
|
|
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:
|
|
|
|
```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
|
|
```
|
|
|
|
Single-developer / solo-release flow by design — no CI babysitting, no separate dashboard to debug. The script fails loudly and visibly on the developer's terminal if anything goes wrong.
|
|
|
|
### 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.
|
|
|
|
```
|
|
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.
|
|
|
|
---
|
|
|
|
**Notes:**
|
|
|
|
- The `.archive` virtual path resolves ZDDC tracking numbers to their earliest-received revision
|
|
- ACL is enforced via bottom-up `.zddc` file evaluation
|