This guide exposes Trit through a stable HTTPS hostname while keeping its origin on 127.0.0.1. Cloudflare Access authenticates the browser, and Trit verifies the signed Access JWT instead of trusting an unsigned proxy email header.
Prerequisites
- Trit installed and healthy in local mode
- A domain active in the intended Cloudflare account
- A Cloudflare Zero Trust organization with an identity provider configured
cloudflaredinstalled on the Trit host from Cloudflare’s official package or repository- A dedicated hostname such as
trit.example.com - A working systemd user manager if the tunnel will run as a user service
Do not open port 9420 in a host firewall, router, cloud security group, or public reverse proxy.
1. Record the current state
Run read-only checks and keep their output for rollback:
trit version
trit paths
trit doctor
systemctl --user is-active trit.service
curl -fsS http://127.0.0.1:9420/health
command -v cloudflared
cloudflared version
Back up Trit’s environment before editing it:
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
backup_dir="${XDG_STATE_HOME:-$HOME/.local/state}/trit/backups/cloudflare-$timestamp"
install -d -m 700 "$backup_dir"
install -m 600 "$HOME/.config/trit/env" "$backup_dir/env"
Use the custom environment path if the installation sets TRIT_ENV_FILE.
2. Create the named tunnel
Authenticate cloudflared and create a tunnel, but do not start it as a persistent service yet:
cloudflared tunnel login
cloudflared tunnel create trit
cloudflared tunnel list
Record the tunnel UUID and the generated credentials-file path. Keep the credentials file private.
Create ~/.cloudflared/trit.yml, replacing the placeholders with the real UUID, home directory, and hostname:
tunnel: 00000000-0000-0000-0000-000000000000
credentials-file: /home/user/.cloudflared/00000000-0000-0000-0000-000000000000.json
ingress:
- hostname: trit.example.com
service: http://127.0.0.1:9420
- service: http_status:404
Protect the files and validate the ingress rules:
chmod 600 "$HOME/.cloudflared/trit.yml" "$HOME/.cloudflared/00000000-0000-0000-0000-000000000000.json"
cloudflared tunnel --config "$HOME/.cloudflared/trit.yml" ingress validate
3. Create the Access application
In Cloudflare Zero Trust:
- Open Access > Applications and add a Self-hosted application.
- Set the application domain to the dedicated Trit hostname, with no path restriction.
- Add an Allow policy containing only the intended emails or identity-provider groups.
- Keep the default deny behavior for every identity that does not match an Allow policy.
- Choose an appropriate session duration for a development shell with repository access.
- Save the application and record its Application Audience (AUD) Tag.
- Record the Zero Trust team domain, such as
your-team.cloudflareaccess.com.
Use an explicit email allowlist in Trit even when the Access policy already restricts users. The two checks provide defense in depth.
4. Configure Trit’s remote mode
Edit ~/.config/trit/env and set:
NODE_ENV=production
TRIT_AUTH_MODE=cloudflare-access
TRIT_PUBLIC_ORIGIN=https://trit.example.com
TRIT_CF_ACCESS_TEAM_DOMAIN=your-team.cloudflareaccess.com
TRIT_CF_ACCESS_AUD=your-access-application-audience
TRIT_ALLOWED_EMAILS=owner@example.com,friend@example.com
HOST=127.0.0.1
PORT=9420
TRIT_PUBLIC_ORIGIN must be the exact canonical HTTPS origin, without a trailing slash or path. The audience value is the Access application’s AUD tag. Keep the environment file mode 0600:
chmod 600 "$HOME/.config/trit/env"
trit restart
trit doctor
curl -fsS http://127.0.0.1:9420/health
Trit should remain healthy locally before the public hostname is connected.
5. Connect DNS and test the tunnel
Create the Cloudflare DNS route for the named tunnel:
cloudflared tunnel route dns trit trit.example.com
Run the tunnel in the foreground for the first test:
cloudflared tunnel --config "$HOME/.cloudflared/trit.yml" run trit
From a private browser window, open the HTTPS hostname. Cloudflare should require Access authentication before Trit loads. After authentication:
- Open
/api/whoamiand confirm the expected email andcloudflare-accessmode. - Create or open an agent thread and confirm streaming output.
- Open a terminal, disconnect it, and reconnect.
- Try an identity outside the policy and confirm it is denied.
Stop the foreground tunnel after this test.
6. Run the tunnel as a user service
Create ~/.config/systemd/user/cloudflared-trit.service. Replace /usr/bin/cloudflared with the absolute path returned by command -v cloudflared:
[Unit]
Description=Cloudflare Tunnel for Trit
After=network-online.target trit.service
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/cloudflared --config=%h/.cloudflared/trit.yml tunnel run trit
Restart=on-failure
RestartSec=5
UMask=0077
NoNewPrivileges=true
[Install]
WantedBy=default.target
Enable and inspect it:
systemctl --user daemon-reload
systemctl --user enable --now cloudflared-trit.service
systemctl --user status cloudflared-trit.service --no-pager
journalctl --user-unit cloudflared-trit.service --since '-10 minutes' --no-pager
If the service must survive logout, inspect loginctl show-user "$USER" -p Linger. Enabling lingering may require an administrator and should be an explicit host-level decision.
7. Verify the boundary
On the Trit host:
ss -ltn | grep ':9420'
curl -fsS http://127.0.0.1:9420/health
systemctl --user is-active trit.service cloudflared-trit.service
The only Trit listener must be 127.0.0.1:9420 or another loopback address. There must be no 0.0.0.0:9420, public firewall rule, or router forwarding rule.
From a client without an Access session, requesting the public hostname should redirect to or be denied by Access rather than return Trit directly. From an approved browser session, /api/whoami, HTTP API requests, and WebSockets must all succeed with the same origin and identity.
Troubleshooting
| Symptom | Check |
|---|---|
| Trit fails to start | Validate the exact HTTPS TRIT_PUBLIC_ORIGIN, team domain, and non-empty AUD value |
| Access login loops | Confirm the Access application domain exactly matches the hostname |
| Trit returns unauthorized after Access login | Confirm the AUD tag, team domain, JWT issuer, and Trit email allowlist |
| Browser API requests return forbidden origin | Remove trailing slashes or paths from TRIT_PUBLIC_ORIGIN and use only that hostname in the browser |
| Page loads but streams or terminals fail | Check WebSocket requests, tunnel logs, and browser origin; do not add a second proxy hostname |
| Tunnel reports ingress errors | Run cloudflared tunnel --config "$HOME/.cloudflared/trit.yml" ingress validate |
| Service stops after logout | Check the systemd user manager and lingering policy |
Use journalctl --user-unit trit.service for Trit and journalctl --user-unit cloudflared-trit.service for the tunnel. Do not paste environment files, tunnel credentials, cookies, or JWTs into issue reports.
Rollback
- Disable the tunnel service:
systemctl --user disable --now cloudflared-trit.service. - Remove or disable the tunnel’s public hostname and DNS route in Cloudflare.
- Restore the previous Trit environment backup or set
TRIT_AUTH_MODE=local,HOST=127.0.0.1, andPORT=9420. - Run
trit restart,trit doctor, and the loopback health check. - Remove the Access application only after the hostname no longer routes to the tunnel.
Retain the backup and tunnel credentials until local mode is verified. Deleting a tunnel is optional and should be a separate, deliberate cleanup step.