Mac Studio + NetBird + Warpgate
This is the recommended first company rollout shape for an internal-only Oore CI instance:
- Mac Studio runs
ooredand the embedded runner - NetBird provides private network reachability
- Warpgate protects access and forwards user identity
- A same-host reverse proxy serves the web UI and proxies API requests to
oored
Use this when you want the UI visible only while connected to your VPN, without exposing the daemon directly on the LAN or internet.
Target architecture
Browser on VPN
-> Warpgate (auth)
-> Caddy/nginx on Mac Studio (HTTPS UI + API proxy)
-> oored on 127.0.0.1:8787
-> embedded runner on same Mac StudioKeep oored bound to loopback. Expose HTTPS from the reverse proxy, not from the daemon itself.
For the split frontend variant below, bind oored to the Mac Studio's NetBird address instead of loopback, and firewall it so only the frontend host can reach the daemon.
Split frontend architecture
Use this variant when the Mac Studio should stay private on NetBird and a small Ubuntu host should own HTTPS, Warpgate, and the static web UI:
Browser
-> Warpgate on Ubuntu (auth)
-> Caddy/nginx on Ubuntu (HTTPS)
-> oore-web on 127.0.0.1:4173
-> NetBird
-> oored on Mac Studio NetBird address:8787
-> embedded runner on Mac StudioIn this shape:
- Mac Studio runs
ooredand the embedded runner. - Ubuntu runs only
oore-webplus the static frontend assets. - Warpgate forwards the authenticated identity header to the Ubuntu reverse proxy.
- The Ubuntu reverse proxy forwards that header to
oore-web. oore-webforwards/v1/*requests, including Warpgate identity headers and cookies, to the Mac daemon over NetBird.- Users add the instance with an empty Backend URL so browser requests stay on the HTTPS frontend origin.
On the Mac Studio, start the daemon on the NetBird address:
export OORED_LISTEN_ADDR=100.64.10.20:8787
export OORE_CORS_ORIGINS=https://ci.builds.example.corp
export RUST_LOG=info
oored run --listen 100.64.10.20:8787Install the frontend-only bundle on Ubuntu:
curl -fsSL https://oore.build/install | \
OORE_INSTALL_MODE=frontend \
OORE_WEB_BACKEND_URL=http://100.64.10.20:8787 \
OORE_LOCAL_WEB_LISTEN=127.0.0.1:4173 \
OORE_LOCAL_WEB_MODE=login \
OORE_NONINTERACTIVE=1 \
bashReplace 100.64.10.20 with the Mac Studio NetBird IP or DNS name. Keep OORE_LOCAL_WEB_LISTEN on loopback unless you have a reason to expose the launcher directly.
For the Linux user service to survive logout and reboot:
sudo loginctl enable-linger "$USER"Example Caddyfile on Ubuntu:
ci.builds.example.corp {
reverse_proxy 127.0.0.1:4173 {
header_up Host {host}
header_up X-Forwarded-Proto https
header_up X-Forwarded-For {remote_host}
header_up X-Warpgate-Username {header.X-Warpgate-Username}
header_up X-Oore-Trusted-Proxy-Secret {env.OORE_TRUSTED_PROXY_SHARED_SECRET}
}
}Configure the Mac daemon's trusted proxy CIDRs to allow the Ubuntu NetBird address or subnet, because the backend sees the request as coming from the frontend host over NetBird rather than loopback.
1. Build the binaries and web UI
bun install
bun run build:web
cargo build --release -p oored
cargo build --release -p ooreArtifacts:
- daemon:
target/release/oored - operator CLI:
target/release/oore - web UI:
apps/web/dist
2. Start the daemon on loopback
Choose the VPN-visible HTTPS origin you want users to open, for example:
https://ci.macstudio.internalhttps://ci.builds.example.corp
Then start oored on loopback:
export OORED_LISTEN_ADDR=127.0.0.1:8787
export OORE_CORS_ORIGINS=https://ci.macstudio.internal
export RUST_LOG=info
./target/release/oored run --listen 127.0.0.1:8787Notes:
- The embedded runner starts automatically in the default daemon mode, so a low-risk first app can build on the same Mac Studio.
- Keep the daemon private. Do not bind it to
0.0.0.0for this topology.
3. Serve the UI and API behind internal HTTPS
Example Caddyfile for the same Mac Studio:
ci.macstudio.internal {
root * /absolute/path/to/oore.build/apps/web/dist
file_server
handle /v1/* {
reverse_proxy 127.0.0.1:8787 {
header_up Host {host}
header_up X-Forwarded-Proto https
header_up X-Forwarded-For {remote_host}
header_up X-Warpgate-Username {header.X-Warpgate-Username}
header_up X-Oore-Trusted-Proxy-Secret {env.OORE_TRUSTED_PROXY_SHARED_SECRET}
}
}
handle /healthz {
reverse_proxy 127.0.0.1:8787
}
handle /metrics {
reverse_proxy 127.0.0.1:8787
}
}Important details:
- Serve
apps/web/distfrom the same HTTPS origin users open in the browser. - Preserve the Warpgate identity header when proxying
/v1/*. - If you configure a trusted-proxy shared secret in Oore, forward the same value as
X-Oore-Trusted-Proxy-Secretfrom the reverse proxy. - If Warpgate and the reverse proxy both run on the Mac Studio,
ooredwill see the proxy peer as loopback, sotrusted_proxy_cidrscan stay empty. - If
ooredsees a non-loopback proxy peer, add that proxy source CIDR during trusted-proxy setup.
4. Put Warpgate in front of the HTTPS origin
Configure Warpgate to:
- require user authentication before access
- forward the authenticated email in
X-Warpgate-Username - proxy traffic to the HTTPS origin served by Caddy/nginx
Oore expects the forwarded identity to be an email address by default.
5. Complete setup from the VPN-visible UI
Generate a bootstrap token on the Mac Studio:
./target/release/oore setup token --ttl 15mThen, from a browser connected through NetBird, open:
https://ci.macstudio.internal/setupIn setup:
- Verify the bootstrap token.
- Choose
Remote (Trusted Proxy / Warpgate). - Use
x-warpgate-usernameas the user email header. - Set a shared secret and configure the reverse proxy to send it as
X-Oore-Trusted-Proxy-Secret. - Leave trusted proxy CIDRs empty if the reverse proxy talks to
ooredover loopback. - Claim the owner account from the Warpgate-authenticated request.
- Finalize setup.
6. Set External Access network settings after setup
After the owner account is created, configure the runtime deployment from the Mac Studio with the operator CLI:
oore external-access enable-trusted-proxy \
--public-url https://ci.macstudio.internal \
--proxy-cidr 100.107.126.166/32 \
--user-email-header x-warpgate-usernameUse the frontend host's NetBird IP or subnet for --proxy-cidr, because oored sees proxied API requests as coming from that host.
This keeps runtime auth, callback validation, and artifact links aligned with the VPN-visible HTTPS origin.
If the Warpgate-authenticated user was first created as an admin, transfer the singleton owner role without editing SQLite:
oore users transfer-owner --email owner@example.com7. Verify from a VPN-connected client
curl -I https://ci.macstudio.internal
curl https://ci.macstudio.internal/healthz
curl https://ci.macstudio.internal/v1/public/setup-statusExpected outcomes:
- the UI HTML loads over HTTPS
/healthzreturns200 OK/v1/public/setup-statusreturns JSON- the login page signs in through Warpgate without loopback-only errors
Common mistakes
- Binding
ooreddirectly to0.0.0.0: this bypasses the intended trust boundary. - Using HTTP for the browser-visible origin: External Access expects a non-loopback HTTPS origin.
- Serving the UI from one origin and API from another without adding the UI origin to
allowed_origins. - Forgetting to forward
X-Warpgate-Usernameto/v1/*. - Passing a username instead of an email in the trusted-proxy header.
When to use OIDC instead
Choose OIDC instead of trusted proxy when:
- Warpgate is not the identity boundary
- users access the instance directly rather than through the proxy
- you want the browser to complete the standard OIDC redirect flow against your IdP