System Architecture
This page explains how Oore CI's components fit together and why the system is designed this way.
Components
Oore CI consists of three components that communicate over HTTP:
┌─────────────┐ HTTPS/API ┌──────────────┐
│ Web UI │ ◄───────────────► │ oored │
│ (React) │ │ (daemon) │
└─────────────┘ └──────┬───────┘
│
│ SQLite
│
┌──────┴───────┐
│ oore.db │
└──────────────┘
┌─────────────┐ HTTP/CLI ┌──────────────┐
│ oore │ ◄───────────────► │ oored │
│ (CLI) │ │ (daemon) │
└─────────────┘ └──────────────┘oored (daemon)
The daemon is the central process. It:
- Serves the REST API (Axum web framework)
- Manages all persistent state in SQLite
- Handles OIDC authentication flows
- Schedules and tracks builds
- Runs an embedded build runner (default mode)
- Receives webhooks from GitHub and GitLab
- Manages artifact storage (local filesystem, S3, or R2)
The daemon runs on macOS only in V1 (required for iOS/macOS builds via Xcode).
oore (CLI)
The operator CLI communicates with the daemon over HTTP. It handles:
- Instance setup (bootstrap token generation, interactive setup wizard)
- External runner registration and management
- Diagnostic checks (
oore doctor)
The CLI shares the same SQLite database as the daemon for bootstrap token operations.
Web UI
The React-based web UI connects to the daemon's API. It provides:
- Setup wizard (alternative to CLI setup)
- Project and pipeline management
- Build triggering and log streaming
- User invitation and role management
- Instance settings configuration
- Multi-instance support (connect to multiple backends)
The hosted UI at ci.oore.build is UI-only — it connects to the customer's self-hosted daemon. No build data passes through the hosted frontend.
Data flow
Build lifecycle
- Trigger — User clicks "Build" in UI, pushes code (webhook), or calls the API
- Queue — Daemon creates a build record in SQLite with status
queued - Claim — Runner polls for available jobs and claims the build
- Execute — Runner clones the repository, installs Flutter via FVM, runs build commands
- Stream — Runner sends log lines to the daemon in real-time
- Complete — Runner reports success/failure, uploads artifacts
- Store — Daemon stores artifacts in configured storage (local, S3, or R2)
- Download — Users download artifacts via signed, time-limited URLs
Authentication flow
- User clicks "Sign in" in the web UI
- UI calls
GET /v1/auth/oidc/startto get an authorization URL - User authenticates with their OIDC provider
- Provider redirects back with an authorization code
- UI sends the code to
POST /v1/auth/oidc/callback - Daemon exchanges the code for tokens, verifies the ID token, creates a session
- UI receives a session token (24-hour TTL)
Why self-hosted?
Mobile CI requires macOS hardware for iOS and macOS builds (Xcode is macOS-only). Rather than renting Mac hardware from cloud providers, Oore CI runs on your own Mac:
- Signing keys stay on your hardware — no need to upload certificates to a third party
- Build artifacts are local — distribute internally without external hosting
- No per-minute billing — your hardware, your schedule
- Full control — configure OIDC, RBAC, and storage as needed
Why OIDC (default)?
In Remote mode, Oore CI defaults to OIDC (remote_auth_mode=oidc) for authentication on any non-loopback access path. If you deploy behind an identity-aware proxy, you can opt into remote_auth_mode=trusted_proxy so user identity is asserted by a trusted proxy header (with peer validation).
OIDC (default) uses your existing identity provider:
- No password storage — eliminates an entire class of security concerns
- Single sign-on — users authenticate with the same credentials they use everywhere else
- Centralized access control — disable a user in your IdP and they lose access to Oore CI
- Enterprise ready — works with Google Workspace, Okta, Azure AD, Auth0, Keycloak, and any OIDC-compliant provider
The daemon also supports loopback-only local login (no OIDC) for local-first onboarding and local operator access (auto-bootstrap requires Local Only mode).
Technology choices
| Component | Technology | Why |
|---|---|---|
| Backend language | Rust | Performance, memory safety, single binary deployment |
| Web framework | Axum | Async, tower middleware ecosystem, type-safe extractors |
| Database | SQLite | Zero-config, single-file backup, sufficient for single-host |
| Frontend | React 19 + TanStack Router | File-based routing, type-safe, modern React patterns |
| Server state | TanStack Query | Cache management, background refetch, optimistic updates |
| Package manager | Bun | Fast installs, native bundler support |
| Flutter management | FVM | Per-project Flutter version pinning |