Skip to main content

Self-hosting

You can run Unveilr yourself — as a SaaS instance for your org, or fully inside your own cloud.

Integration environment

make setup # dependencies + empty schema; creates no tenant data
# configure WorkOS/OIDC, URLs, and service secrets in .env
make verify-config
make dev # API :8080, gateway :8081, console :3000

Open http://localhost:3000 and sign in through the configured identity provider. A tenant is created only from a validated organization-bound token; there is no seeded tenant or privileged local session.

Production components

ComponentTechNotes
Admin APIFastAPI/v1/* — repos, findings, AI-BOM, evidence, tenant administration
ConsoleNext.jsthe web UI
GatewayFastify/TSruntime agent governance (optional)
DatabaseAurora / Postgresmulti-tenant with row-level security
EvidenceS3 Object Lockimmutable audit storage (COMPLIANCE mode)

Deploy to AWS

Terraform lives in infra/aws (CloudFront/WAF/ALB → ECS Fargate; Aurora Postgres; S3 Object Lock; KMS). In short:

cp infra/aws/terraform.tfvars.example infra/aws/terraform.tfvars
# Set account, region, ACM certificate, domain, and real workos_client_id.
export WORKOS_API_KEY='sk_...'
export WORKOS_CLIENT_ID='client_...'
export WORKOS_WEBHOOK_SECRET='wh_...'
./deploy_manual_aws.sh --env prod --region us-east-1

The deploy sequence creates or restores the durable GitHub App secret first, creates the WorkOS secret container, validates real credentials, and only then rolls ECS services. GitHub credentials never enter Terraform state. The secret uses the AWS-managed Secrets Manager key and survives application-stack and KMS teardown.

For the full production cutover (SSO, DNS, private binary CDN, persona acceptance matrix), follow the monorepo runbook docs/PRODUCTION_APIS.md (private repo) and the customer-facing Production APIs page. The source monorepo is not made public for CLI install.

Database migrations

cd apps/api
./.venv/bin/alembic upgrade head

Migrations are idempotent and inspect-based, so they apply cleanly to both fresh and previously-migrated databases.

Identity (SSO / SCIM)

Production auth uses WorkOS AuthKit — hosted login, Organizations for tenant onboarding, SSO (SAML/OIDC to Okta / Entra ID / Google), and SCIM provisioning. Set the WorkOS keys and the matching API/gateway issuer and JWKS. Without them the console shows a configuration error and the services fail closed. An AWS-native alternative is Amazon Cognito for API/gateway clients; the bundled web console currently uses WorkOS AuthKit for browser sessions.

Secure by default (required for production)

The API and gateway fail closed in every supported human-operated environment. Automated tests are the only place where local identity seams are accepted. Before starting an integration environment:

  • Do not enable ALLOW_DEV_HEADERS. X-Unveilr-Tenant is test-only and is not a supported integration path.
  • Configure a real identity provider: set OIDC_JWKS_URL (WorkOS/Cognito/your IdP) and OIDC_ISSUER so tokens are validated with asymmetric signatures.
  • Set GATEWAY_CONTROL_PLANE=api and a strong shared GATEWAY_INTERNAL_TOKEN. File snapshots are test-only.
  • Use PostgreSQL and FIELD_ENCRYPTION_KEY in staging and production.

If any of these are unsafe, startup reports exactly what to fix. For machine/CI access, mint tenant-bound service tokens (Settings → API Tokens).

Configuration

Env varPurpose
DATABASE_URLPostgreSQL for staging/production; SQLite is developer-only.
API_URLConsole → API endpoint.
EVIDENCE_DIR / S3 settingsWhere evidence is written.
WORKOS_CLIENT_ID / WORKOS_API_KEYRequired browser identity configuration.
OIDC_ISSUER / OIDC_JWKS_URLSigned token validation for API and gateway.
GATEWAY_CONTROL_PLANEMust be api.
GITHUB_APP_ID / GITHUB_APP_SLUGExact identity of the organization-owned GitHub App.
GITHUB_PRIVATE_KEY / GITHUB_WEBHOOK_SECRETSecret-manager values required before repository installation is enabled.
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRETOAuth identity used only to verify the installing user; the temporary user token is revoked and never stored.
GITHUB_APP_SECRET_IDDurable secret target used by the super-admin manifest callback.
GITHUB_APP_ROLLOUT_CLUSTER / GITHUB_APP_ROLLOUT_SERVICEAPI-only rollout target after automatic credential publication.
UNVEILR_DETECTION_RULES_PATHOverride detection rules (advanced).

AuthKit middleware in self-hosted containers

The console runs AuthKit middleware in the Next.js Node.js runtime. This is required for ECS and other container deployments where WorkOS secrets are injected when the process starts. The production build checks both the runtime and route coverage and fails if the middleware is accidentally emitted as an Edge bundle.

If sign-in fails and the web logs say withAuth is not covered by AuthKit middleware:

  1. Deploy a current web image and roll every web task.
  2. Confirm the web task has WORKOS_API_KEY, WORKOS_CLIENT_ID, WORKOS_COOKIE_PASSWORD, and the exact public callback URI.
  3. Confirm .next/server/functions-config-manifest.json lists /_middleware with runtime: "nodejs".
  4. Visit /logout or clear old AuthKit cookies before retrying /login.

Do not restore development headers or a fallback administrator session to work around this error; production identity remains fail-closed.

After deployment, set a tenant-bound UNVEILR_API_TOKEN and run make verify-runtime to check service readiness and authenticated organization access.

For initial GitHub App creation, a platform super-admin opens Settings → GitHub App and starts the manifest flow. GitHub generates all six credentials; the signed callback validates and stores them and requests the API rollout. GET /v1/github/status then performs an authenticated GitHub preflight and returns only readiness, installation state, public slug, and connected account names. The console button remains disabled until GitHub confirms that the configured App identity and private key belong together. The setup callback then uses OAuth with PKCE to confirm that the installing user can access the reported installation; its user token is revoked immediately and is never persisted. Selected repositories synchronize automatically.

Reconnecting after an infrastructure rebuild

Current deployments preserve unveilr-<env>/github-app outside Terraform. If an older teardown already purged it, keep the same GitHub App and generate a new private key and client secret in GitHub; update the webhook secret on both sides if its previous value is unavailable. Run scripts/wire_github_app_aws.sh --roll: it verifies the App, discovers the public client ID, and securely prompts for the client secret without echoing or storing it in shell history. This recovery command is necessary for an existing App because GitHub's manifest flow creates new Apps; it cannot recover one-time credentials for an App that already exists.

If the database was rebuilt rather than restored, its workspace binding was also deleted even though the installation still exists at GitHub. In each affected workspace, use Add or update GitHub access to verify and rebind the existing installation. A second App is neither required nor recommended.