Installing Ash Nazg
Status: DRAFT. This guide will be finalised in the batch covering the
wire-dosbox-engineend-to-end demo. Steps below are written from the design docs, not from a verified install. Expect minor inaccuracies until that change lands; treat this as a sketch, not a procedure.What is verified (wire-dosbox-engine, on NC 32.0.14 + AppAPI 5.x + HaRP v0.4.0): the deploy path — image pull, HaRP-spawned container, port allocation, the heartbeat/init/enabled handshake, and reaching the app through the AppAPI proxy. Running a binary is not: see the engine-spawn gap in that change's tasks.
Requirements
| Component | Minimum | Notes |
|---|---|---|
| Nextcloud | 32 | Verified against 32.0.14 with AppAPI 5.x. NC 30 was the scaffold's first target and is no longer supported. |
| AppAPI | 5.x | Install from the App Store before installing Ash Nazg. |
| HaRP FRP tunnel | on | The ExApp opens an FRP tunnel back to HaRP; the image ships frpc for it. |
| Sessions | in-app | A session runs inside the Ash Nazg container, not in a container of its own — see Isolation below. |
| Deploy daemon | HaRP | DSP is not supported. Streaming uses websockets, which DSP does not proxy reliably. |
| Container runtime | Docker (or compatible) | Required by HaRP. |
| Architecture | linux/amd64 or linux/arm64 | Both host and engine images are multi-arch. |
Steps (sketch — to verify)
- Install AppAPI from the Nextcloud App Store and enable it.
- Configure HaRP as the active deploy daemon. Confirm with:
bash occ app_api:daemon:list - Install Ash Nazg from the Nextcloud App Store. The first
install pulls the host container image
(
ghcr.io/mwest2020/ash-nazg-host:<tag>). Allow a few minutes for the pull on a slow link. - Verify the install via the admin settings page:
Administration → Ash Nazg → Test installation. The button
runs the host shim's
/selftestendpoint and reports four checks. All four green = ready. - Enable an engine. The dosbox-x engine ships disabled by default (per the engines spec — newly discovered engines are admin-opt-in). Toggle it on in the same settings panel.
The self-check returns four named checks in this fixed order:
| Check id | Verifies |
|---|---|
host-health |
The host shim's /health endpoint returns 200. |
engines-registered |
At least one engine is registered and enabled. |
deploy-daemon-spawn |
HaRP can spawn and tear down a transient sidecar in 30 s. |
audit-log-write |
Writing an ash_nazg.selftest event to the audit log succeeds. |
In this scaffold release every check returns
status: "skipped". The wire-dosbox-engine change replaces the
per-check logic; the JSON shape stays identical so the frontend
binding is stable.
Verifying it works
Until the wiring change lands, verification is limited. What you can do today:
- Confirm
appinfo/info.xmlvalidates locally:bash ./scripts/verify-info-xml.sh - Confirm the host container starts and
/healthreturns 200:bash docker run --rm -p 8080:8080 \ ghcr.io/mwest2020/ash-nazg-host:<tag> curl -s http://127.0.0.1:8080/health - Confirm the engine container builds and the binaries are
present:
bash docker buildx build --platform linux/amd64 \ -t ash-nazg-dosbox-x:test \ engines/dosbox-x/ docker run --rm -it ash-nazg-dosbox-x:test \ /usr/bin/dosbox-x --version
The end-to-end "click Run, see Commander Keen" flow does NOT yet
work. That's wire-dosbox-engine.
What ships in this scaffold
A scaffold install — the manifest registers, the host
container responds to /health and /heartbeat, the admin
settings page renders, and the Files action is registered but
toasts "not yet wired" when clicked. No actual sandbox spawns.
That is intentional; the scaffolding change deliberately stops
short of dispatch logic so the dispatcher gets its own change with
its own review.
Troubleshooting (placeholder)
To be filled in when the demo flow exists. Expected sections:
- "Install hangs at container pull"
- "AppAPI daemon registration failed"
- "Self-test reports DAEMON-UNREACHABLE"
- "Self-test reports IMAGE-NOT-FOUND"
- "Files action does not appear on
.exefiles"
Uninstalling
occ app_api:app:unregister ash_nazg
occ app:remove ash_nazg
Removes the manifest registration and the host container. User Files are untouched (Ash Nazg stores no persistent state of its own outside the AppAPI volume; sessions are ephemeral).
Isolation — what a session is, and is not
A Run does not start a container. The emulator ships inside the Ash Nazg app container, and a session is a process tree there. An ExApp cannot start a sibling container: it has no Docker CLI, no socket, and the deploy daemon offers ExApps no spawn API.
What that means for you as an administrator:
- The binary your user runs is a DOS program inside DOSBox-X. The emulator is the boundary — the program never executes as native code on your host.
- A session has no cgroup CPU or memory limit. It runs at a lower scheduling priority than the app itself, the number of concurrent sessions is capped, and every session has a maximum duration, but a busy session can use the CPU the container is allowed.
- A session runs under the same user as the app, so a compromise of DOSBox-X itself — not of the DOS program it runs — reaches the app's environment, including its Nextcloud credentials. The app keeps its own variables out of the session's environment, but that is hygiene, not a boundary.
- A session gets a private directory containing only the binary, downloaded from Files. There is no WebDAV mount, so a session cannot reach the rest of the user's Files, and the directory is removed when the session ends.
If you need container-level isolation per session, this release is not the one for you: that arrives when each engine becomes its own ExApp, deployed by AppAPI.