yk-portal Contents robert-patrick-texas/yk-portal on GitHub

The yk-portal lab

yk-portal installed as the deployment guide installs it, in a Docker container, reachable only through the host's nginx, which publishes it under a path of one of the host's sites (or a host name of its own). Bring it up to try a change in a real browser, behind a real proxy, and take it down afterwards: nothing is kept, and while it is down nginx answers 502 at its address. Not for production.

The lab's address and port are this machine's: they go in lab/lab.env, which git ignores, never in the repository. Once:

cp lab/lab.env.example lab/lab.env
editor lab/lab.env      # YK_LAB_PUBLIC_BASE_URL, YK_LAB_PORT

Then:

lab/lab up                          # build and start, with SQLite
lab/lab up --postgresql             # or with PostgreSQL 18 and psycopg's C implementation
lab/lab yk-portal bootstrap-admin root
lab/lab status
lab/lab logs -f
lab/lab yk-portal database-status   # any yk-portal command, as the yk-portal account
lab/lab shell
lab/lab down
lab/lab systemd-check               # yk-portal.service under systemd, sandboxed

lab/lab up builds the image from the working tree (lab/Dockerfile: Ubuntu 26.04, uv, deploy/install), and runs it on a Docker network of its own, yk-portal-lab, published on the host's 127.0.0.1 only, at YK_LAB_PORT (8100 if not set). At each start, lab/entrypoint writes the configuration (public_base_url from YK_LAB_PUBLIC_BASE_URL; the network's gateway, where Docker's port forwarding connects from, as the trusted proxy), creates the master key the first time, migrates, and runs yk-portal serve as the yk-portal account. The audit log's copy goes to the host's journal (journalctl -t yk-portal-audit).

With --postgresql, a postgres:18 container on the same network holds the database, set up as the guide sets up PostgreSQL: a role that owns the database, its password in a passfile.

lab/lab systemd-check builds lab/Dockerfile.systemd (the lab image with systemd as its init), starts yk-portal.service under systemd with its whole sandbox in a privileged container with no network, signs in, checks the journal and the database file, prints systemd-analyze security's rating, checks that the unit ends failed without its master key, and removes the container.

The host's nginx, once

Two files, as for any site with yk-portal under a path, and one line in the site's server { } block. The files publish the lab at /yk/ with the upstream port 8100: change the path to YK_LAB_PUBLIC_BASE_URL's and the port to YK_LAB_PORT first. Here SITE is the site's file:

sudo install -m 0644 lab/nginx/yk-portal-lab.conf /etc/nginx/conf.d/yk-portal-lab.conf
sudo install -m 0644 lab/nginx/yk-portal-lab-location.conf /etc/nginx/snippets/yk-portal-lab.conf
sudoedit /etc/nginx/sites-available/SITE   # add in server { }: include snippets/yk-portal-lab.conf;
sudo nginx -t && sudo systemctl reload nginx

To remove it, delete the line and the two files, then test and reload again. If the site adds its own security headers to every response, some arrive twice (such as X-Frame-Options as both DENY and SAMEORIGIN, which browsers treat as DENY); a host name of the lab's own wouldn't. Under a path, yk-portal sends no HSTS header: the site's own applies.