# usm-web A Statum + Spry application that browses **and serves** a USM repository: human-facing pages for exploring packages, plus the exact HTTP endpoints a real USM client (`usm repository list` / `verify` / `install`) expects to fetch from a repository host. ## What it serves | Endpoint | Purpose | |---|---| | `GET /` | Repo overview: name/summary, signing-key fingerprint, package count and total size, add-repository + install-USM instructions, package table with server-side search | | `GET /package/{file}` | Package detail: manifest fields read out of the `.usmc` (licences, flags, provides/depends tables), sha512, on-disk size/mtime, download button | | `GET /PACKAGES.usml` | The repository listing streamed **verbatim** (`application/json`) | | `GET /repo.usmr` | A rewritten copy of the `.usmr` with its embedded `url` replaced by the derived base | | `GET /.usmc` | Package archives streamed from `/public/` (`application/octet-stream`, exact content-length) | | `GET /install-usm.sh` | The configured installer script (`installer.path` mode only) | The derived base is `base_url` from the config when set, else `X-Forwarded-Proto`/`X-Forwarded-Host`, else the request's own scheme/host — so clients behind a reverse proxy resolve against the public name. ## Configuration `web-config.json` (in the app's working directory, or `ASTRALIS_CONFIG_PATH`) carries the static Statum keys plus an optional `"usm-web"` section: ```json { "statum": { "…": "static keys from spry keys / statum-genkeys" }, "usm-web": { "installer": { "path": "install-usm.sh" }, "base_url": "https://repo.example.com/", "name": "My repository" } } ``` | Key | Meaning | |---|---| | `installer.url` | External installer location; the homepage links out and shows `curl -fsSL \| sh` | | `installer.path` | Installer script this app serves at `/install-usm.sh`; relative paths resolve against the config file's directory | | `base_url` | Optional override of the request-derived repository base (normalised to a trailing slash) | | `name` | Optional display-name override for the `.usmr` name | `installer` takes **exactly one** of `url`/`path`; providing both (or neither, when the block should appear) is a configuration error. Omit the section entirely to hide the install-USM card. The repository root is selected by (in order) the `--repo ` argument, the `USM_WEB_REPO_DIR` environment variable, or the `/repo` default, and is expected to look like a published repository: ``` /repo ├── web-stack.usmr ├── keys/ (not used by usm-web; signing is upstream) └── public/ ├── PACKAGES.usml └── -.usmc ``` Both the `.usmr` and `PACKAGES.usml` are re-read when their mtimes change, so a rebuilt repository is picked up without a restart. There is **no database and no authentication**; all Statum state is stateless PAGE slots. ## Development ```bash spry dev ``` Builds, runs the app on port 8080 (`--port N` to change) and watches `src/`, `meson.build` and `web-config.json`: saving a file rebuilds incrementally — page HTML and resources recompile via the meson codegen targets — and restarts the app when the build succeeds. The environment below is required for the underlying meson builds (the `spry dev` child processes set it for you; export it in shells where you build or run manually): ```bash export WS_PREFIX="$HOME/.local" export PKG_CONFIG_PATH="$WS_PREFIX/lib64/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" export XDG_DATA_DIRS="$WS_PREFIX/share${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}" export LD_LIBRARY_PATH="$WS_PREFIX/lib64${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" meson setup builddir ninja -C builddir ./builddir/usm-web 8080 --repo ../web-stack-repo ``` Repo parsing uses libusm (`Usm.Repository`, `Usm.RepositoryListing`, `Usm.Manifest.from_package`) — no hand-rolled usml/usmr mappers. ## Layout | Path | Purpose | |---|---| | `src/main.vala` | application wiring: modules, config, pages, action, endpoints | | `src/UsmWebConfig.vala` | the `"usm-web"` config section (installer url\|path, base_url, name) | | `src/RepositoryService.vala` | libusm-backed read side with mtime-based reload | | `src/DerivedBase.vala` | base-URI derivation from config/forwarded headers | | `src/PackagesState.vala` | shared `packages` slot-state builder + size/fingerprint formatting | | `src/entrypoints/` | `HomeEntrypoint` (/) and `PackageDetailEntrypoint` (/package/{file}) | | `src/actions/` | `SearchPackagesAction` — server-side table filtering | | `src/endpoints/` | the USM-facing endpoints listed above | | `src/pages/*.html` | Statum pages, compiled by `statum-mkpstm` at build time | | `src/Static/main.css` | stylesheet embedded by `statum-mkres` | | `MANIFEST.usm` | USM package manifest: `provides bin:usm-web`, build/runtime/manage deps | | `usm-scripts/` | USM build + install scripts (meson staging) | | `web-config.json` | static keys and the `usm-web` section (gitignored, 0600) | Registrations live between `// spry:*-begin/end` markers in `src/main.vala`, `# spry:*-begin/end` markers in `meson.build` and `` markers in `src/pages/main.html`; the `spry add` commands edit only those blocks and are idempotent. ## Deployment (USM) ```bash spry deploy --verbose \ --repository ../web-stack-repo/web-stack.usmr \ --installer-url file:///tmp/kilo/usm-shim/install-usm-full.sh ``` This packages the application and builds a single-stage container image through USM (`usm manifest deploy`): the `MANIFEST.usm` + `usm-scripts/` drive the in-container build and install, the Web-Stack resolves from the given repository (including the `usm` package that provides the libusm `pc:`/`vapi:` refs this app builds against), and the system package manager provides the platform libraries. The result is `usm-web-.image.tar.xz`, loadable with `podman load -i`. `web-config.json` is never packaged (`.usmignore`), so keys are never baked into the image. Bind-mount the repository read-only at `/repo` and, when you want stable keys or an installer section, mount a config: ```bash podman load -i usm-web-0.1.image.tar.xz podman run -d -p 8080:8080 \ -v /path/to/web-stack-repo:/repo:ro,Z \ -v $PWD/web-config.json:/run/web-config.json:ro \ -e ASTRALIS_CONFIG_PATH=/run/web-config.json \ localhost/usm-web:0.1 ```