152 lines
8.6 KiB
Markdown
152 lines
8.6 KiB
Markdown
# Private TrueNAS container
|
|
|
|
The requested instance starts with **new games**, serving HTTP at
|
|
`http://192.168.2.251:33046` behind the existing NGINX HTTPS endpoint
|
|
`https://dune.procrastination.games`. Development games remain in their original
|
|
`.wrangler/state`; they are never copied into an image. This private local
|
|
runtime prototype does not change public release or rules-mode gates.
|
|
|
|
## Checkpoint images
|
|
|
|
The source repository is moving to [Gitea](https://git.juiced.computer/admin/Dune).
|
|
Its `.gitea/workflows/container.yml` is intended to build and verify checkpoint
|
|
images on pushes to `main`. Configure the Gitea Actions variables
|
|
`DUNE_CONTAINER_IMAGE` for the full registry image name and
|
|
`DUNE_REGISTRY_USERNAME` for the registry account; supply the registry token as
|
|
the `CONTAINER_REGISTRY_TOKEN` Actions secret. The default image name is
|
|
`git.juiced.computer/admin/dune`. Registry access and image visibility depend
|
|
on the actual Gitea package settings; do not assume anonymous pulls work.
|
|
|
|
No Gitea runner has been registered and no Gitea image build has succeeded yet.
|
|
A source push is therefore not a verified container image or a deployment.
|
|
Register and configure an `ubuntu-latest` runner whose job environment has a
|
|
working Docker CLI, daemon and Buildx, plus network access to actions, npm and
|
|
container images. Configure registry access, then verify the
|
|
workflow and its published image before considering a manual NAS update. The
|
|
existing Compose configuration continues to reference the GHCR image until a
|
|
separately verified migration is applied at a safe point.
|
|
|
|
The previous GitHub workflow, `.github/workflows/container.yml`, builds a
|
|
production image using the lockfile on GitHub pushes. Types, lint and offline
|
|
tests remain part of source checkpoint verification rather than being repeated
|
|
in the container build.
|
|
Before publishing, CI creates an isolated container and volume, checks accepted
|
|
and rejected HTTP origins, verifies saved-seat restoration through restart and
|
|
container replacement, then runs the HTTP integration suite.
|
|
|
|
Only a passing GitHub image is pushed to `ghcr.io/quintine/dune:checkpoint`,
|
|
alongside `ghcr.io/quintine/dune:sha-<full-commit>`. TrueNAS currently pulls
|
|
that image. Failed checks leave the previous update tag untouched. This
|
|
workflow targets Linux AMD64, the architecture of ordinary x86 TrueNAS systems.
|
|
|
|
The user selected the existing GHCR image as **public**, matching the public
|
|
source repository, so TrueNAS needs no registry login for that image. GitHub initially creates packages
|
|
privately; after the first successful publication, set this package's visibility
|
|
to public in GitHub package settings. Images contain application code and assets,
|
|
never saved games or credentials. Publishing uses the workflow's built-in token.
|
|
A source push alone does not prove image publication; repository permissions or
|
|
package policy may prevent the workflow from publishing.
|
|
|
|
## Install
|
|
|
|
On a Docker-based TrueNAS release, use **Apps → Discover Apps → Install via YAML**
|
|
and paste [the Compose configuration](../deploy/truenas.compose.yaml), naming the
|
|
app `dune`. It maps host port **33046** to container port 3000. A persistent named
|
|
volume mounts at `/data`, starts empty and inherits writable ownership from the
|
|
image. The container runs as UID/GID 1000. If using a dedicated host dataset
|
|
instead, mount it at `/data` and give UID/GID 1000 write access before starting.
|
|
|
|
Startup applies pending additive migrations before serving. Both commands use
|
|
the same database ID and storage root. A failed migration stops startup without
|
|
deleting games. The inspector remains on container loopback and is not published.
|
|
|
|
On a database without administrator accounts, startup creates one owner and
|
|
prints its personal access key once in the container log. Save it privately
|
|
before replacing the container; its original log may be lost. Anyone with log
|
|
access can use that key, so restrict log access and do not publish diagnostics
|
|
containing it. Existing accounts never trigger a new key on restart or update.
|
|
See [admin access recovery](ADMIN_PANEL.md#initial-owner-and-access-recovery).
|
|
|
|
The Docker build context explicitly allows `deploy/bootstrap-admin.mjs`; keep
|
|
that allowlist in `.dockerignore` when changing the startup entrypoint. A CI
|
|
image-build failure does not replace the running NAS image or provision a key.
|
|
|
|
The container serves the built Worker directly through pinned Miniflare/workerd,
|
|
with persisted D1-compatible SQLite. Wrangler only applies startup migrations.
|
|
The development proxy produced intermittent POST failures after rejected request
|
|
bodies; the direct listener avoids that proxy. This preserves the application without a database
|
|
rewrite. It does not supply managed D1 backups, replication, high availability
|
|
or durable unattended bot scheduling. Keep this prototype on a trusted LAN;
|
|
public hosting remains separately gated. HTTP does not encrypt cookies or seat
|
|
recovery kits. Browser operation IDs now use cryptographic `getRandomValues`,
|
|
which also works at LAN HTTP origins where `randomUUID` is unavailable.
|
|
|
|
## Production navigation
|
|
|
|
Use native anchors for app page links. The pinned Vinext/Vite production build
|
|
renames navigation exports while its Link runtime dynamically looks up their
|
|
original names, breaking clicks and prefetch despite successful HTTP responses.
|
|
Native navigation keeps query strings, rule anchors, browser history and saved
|
|
seat cookies. The Next.js lint preference for client-side Link is disabled for
|
|
this intentional choice. Verify page links in the production browser when
|
|
changing framework versions; development navigation alone does not cover it.
|
|
|
|
## Update and preserve games
|
|
|
|
### Existing NGINX HTTPS termination
|
|
|
|
Keep the NGINX upstream **HTTP**, `192.168.2.251:33046`. Set the app environment
|
|
variable `DUNE_PUBLIC_ORIGIN` to the exact external HTTPS origin (scheme and
|
|
hostname, optional port, no path). This keeps strict origin validation and marks
|
|
seat cookies Secure, even though the proxy-to-app connection is HTTP. Dune does
|
|
not blindly trust client-supplied forwarding headers. When configured, use the
|
|
HTTPS address to play; direct HTTP browser POSTs are intentionally rejected.
|
|
The supplied Compose file already sets `https://dune.procrastination.games`.
|
|
|
|
Keep NGINX's usual original Host and forwarded-protocol headers. Enable
|
|
WebSocket support in NGINX Proxy Manager, or forward Upgrade/Connection headers
|
|
with HTTP/1.1 in an existing NGINX location. Dune currently uses HTTP polling,
|
|
not game WebSockets; enabling proxy support does not change its transport.
|
|
See [NGINX WebSocket forwarding](https://nginx.org/en/docs/http/websocket.html).
|
|
|
|
### Applying an update
|
|
|
|
1. Wait for the checkpoint workflow for the configured image registry to
|
|
succeed and confirm the intended revision was published. A Gitea source
|
|
push alone is insufficient, and the GHCR Compose image does not automatically
|
|
switch registries. Check for image updates in TrueNAS and apply the update
|
|
manually at a safe point between decisions.
|
|
2. Before updating, stop the app and back up or snapshot its persistent storage.
|
|
Include the named volume in the Apps backup procedure; a dedicated host
|
|
dataset provides a more explicit ZFS snapshot boundary.
|
|
3. Update while retaining the same app identity and `/data` volume. Never delete
|
|
the volume, reinstall with empty storage or reset a database to resolve an
|
|
error. Restart and confirm the saved seat restores.
|
|
4. Retain the prior immutable image tag. Rollback may require its matching storage
|
|
snapshot if a subsequent migration is incompatible; switching the image alone
|
|
is not a guaranteed database rollback.
|
|
|
|
No unattended updater is installed. The health probe checks HTTP availability;
|
|
the CI integration and saved-seat checks separately exercise persistence.
|
|
|
|
## Verification and installation boundary
|
|
|
|
On a machine with Docker:
|
|
|
|
```sh
|
|
docker build -t dune:verify .
|
|
node tools/verify-container.mjs dune:verify
|
|
```
|
|
|
|
The verifier only removes containers and volumes with names it randomly creates.
|
|
It never mounts development games or an existing TrueNAS volume.
|
|
|
|
Container execution and NAS installation require a successful CI run and an
|
|
authenticated TrueNAS session. Final acceptance includes create/join at the NAS
|
|
URL, saved-seat restoration, restart and update retaining storage. Source checks
|
|
alone do not certify these steps.
|
|
|
|
Sources: [TrueNAS custom apps](https://apps.truenas.com/managing-apps/installing-custom-apps/),
|
|
[GitHub container registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry),
|
|
[Cloudflare local runtime](https://developers.cloudflare.com/workers/local-development/).
|