Redline local QA
Run Redline’s QA environment on any computer that is on the tailnet and can
reach the GitLab API, published at https://qa-yqa.fio.sh through a Cloudflare
Tunnel. It runs the verified image: the build CI promotes automatically from
main, before anyone releases it.
Reader → Cloudflare (Access) → qa-yqa.fio.sh → tunnel → cloudflared container
→ service container (dashboard + /v1, one origin)
service container → https://glab.fio.sh over the computer's Tailscale connection
Two containers and one settings file. There is no database: GitLab stores every comment and history record, sessions are encrypted cookies, and caches live in memory.
What you need
- Docker (Docker Desktop on macOS), Node 22 and this repository checked out.
- Tailscale connected, so the computer reaches
https://glab.fio.sh. cloudflaredon the computer (macOS:brew install cloudflared), and access to the Cloudflare account forfio.sh.- In GitLab: the Fridai Redline OAuth application (administrator), a token
with
read_api, and a token withread_registryto pull the image.
1. GitLab values
-
OAuth application. In Admin > Applications > Fridai Redline, add
https://qa-yqa.fio.sh/v1/auth/gitlab/callbackas a line under Redirect URI (keep the existing lines). Confidential, scopeapi. Note the Application ID and the secret. -
Read token. A group access token on
fridai(or a project token onfridai/fio-dep/fio-redline), role Reporter, scoperead_apionly. -
Registry login, once per computer, with a
read_registrytoken:docker login glab.fio.sh:5050 --username <your GitLab username>
2. Tunnel and DNS
cloudflared tunnel login # browser: choose the fio.sh zone
npm run qa:local -- tunnel
The second command creates the tunnel fio-redline-qa, adds the DNS record
qa-yqa.fio.sh pointing at it, writes the ingress to
ops/local-qa/cloudflared/config.yml, and stores the tunnel token in
ops/local-qa/cloudflared.env. Both files stay out of git. To use another
name, set QA_HOSTNAME for this and every later command.
3. Cloudflare Access
A tunnel hostname is public. Put it behind Access before anyone else learns the
address: in Cloudflare Zero Trust, Access > Applications > Add an
application > Self-hosted, domain qa-yqa.fio.sh, with the same allow policy
as yqa.fio.sh. The service does not read Access identities (it runs in
gitlab-oauth mode); Access only gates the edge. Readers then connect GitLab
as usual, which needs Tailscale because glab.fio.sh is on the tailnet.
For scripted checks, create an Access service token and allow it in the application’s policy; see section 5.
4. Start
npm run qa:local -- up
Run it in your own terminal. The first run creates the settings file
(ops/local-qa/qa.env, readable only by you), generates the session,
draft-signing and webhook secrets, and then asks for the three GitLab values
from section 1: the read token (checked against GitLab before it is saved), the
OAuth Application ID and its secret. Secrets are not shown as you type, and
nothing is saved if you cancel. It then pulls the image, starts both
containers, waits for health, runs the smoke check against
http://127.0.0.1:18787, and reports whether the public address is behind
Access.
To change the GitLab values later (a new token, a rotated secret), run
npm run qa:local -- configure.
Other commands: npm run qa:local -- status, -- logs, -- down. To update
to the newest verified build, run up again (it pulls first).
5. Check it
In a browser on the tailnet: open https://qa-yqa.fio.sh/, sign in through
Access, open a page under Knowledge base, and connect GitLab from the
comments panel.
From a terminal, with an Access service token in the environment:
export CF_ACCESS_CLIENT_ID=<service token client id>
export CF_ACCESS_CLIENT_SECRET=<service token secret>
npm run smoke -- https://qa-yqa.fio.sh fio-redline kb/index.md
REDLINE_LIVE_URL=https://qa-yqa.fio.sh npm run test:live
Set those two variables in your shell from your password manager, not as part of a command line that history records.
Differences from the GitLab-host QA
- GitLab cannot send webhooks to this computer, so pages pick up new commits and comments when the service cache expires (within about a minute).
- The address is reachable off the tailnet through Cloudflare, behind Access. The tailnet still gates the GitLab sign-in.
- The environment runs while the computer and its Tailscale connection are up.
Troubleshooting
statusreports a TLS error for the public address. On the free plan, Cloudflare’s Universal SSL certificate coversfio.shand first-level names (*.fio.sh) only, which is why QA isqa-yqa.fio.shand notqa.yqa.fio.sh. If you choose anotherQA_HOSTNAME, keep it first-level (one label beforefio.sh) and add its callback URI to the OAuth application. A new record can take a few minutes to get its certificate.- The service is unhealthy and the logs show
gitlab-unreachable. Tailscale is not connected, or the computer cannot reachhttps://glab.fio.sh. pullfails. Rundocker login glab.fio.sh:5050again; theread_registrytoken may have expired.- GitLab reports a redirect URI mismatch. The callback on the OAuth
application must equal
https://<QA_HOSTNAME>/v1/auth/gitlab/callbackexactly.