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
| Component | Tech | Notes |
|---|---|---|
| Admin API | FastAPI | /v1/* — repos, findings, AI-BOM, evidence, tenant administration |
| Console | Next.js | the web UI |
| Gateway | Fastify/TS | runtime agent governance (optional) |
| Database | Aurora / Postgres | multi-tenant with row-level security |
| Evidence | S3 Object Lock | immutable 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-Tenantis test-only and is not a supported integration path. - Configure a real identity provider: set
OIDC_JWKS_URL(WorkOS/Cognito/your IdP) andOIDC_ISSUERso tokens are validated with asymmetric signatures. - Set
GATEWAY_CONTROL_PLANE=apiand a strong sharedGATEWAY_INTERNAL_TOKEN. File snapshots are test-only. - Use PostgreSQL and
FIELD_ENCRYPTION_KEYin 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 var | Purpose |
|---|---|
DATABASE_URL | PostgreSQL for staging/production; SQLite is developer-only. |
API_URL | Console → API endpoint. |
EVIDENCE_DIR / S3 settings | Where evidence is written. |
WORKOS_CLIENT_ID / WORKOS_API_KEY | Required browser identity configuration. |
OIDC_ISSUER / OIDC_JWKS_URL | Signed token validation for API and gateway. |
GATEWAY_CONTROL_PLANE | Must be api. |
GITHUB_APP_ID / GITHUB_APP_SLUG | Exact identity of the organization-owned GitHub App. |
GITHUB_PRIVATE_KEY / GITHUB_WEBHOOK_SECRET | Secret-manager values required before repository installation is enabled. |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | OAuth identity used only to verify the installing user; the temporary user token is revoked and never stored. |
GITHUB_APP_SECRET_ID | Durable secret target used by the super-admin manifest callback. |
GITHUB_APP_ROLLOUT_CLUSTER / GITHUB_APP_ROLLOUT_SERVICE | API-only rollout target after automatic credential publication. |
UNVEILR_DETECTION_RULES_PATH | Override 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:
- Deploy a current web image and roll every web task.
- Confirm the web task has
WORKOS_API_KEY,WORKOS_CLIENT_ID,WORKOS_COOKIE_PASSWORD, and the exact public callback URI. - Confirm
.next/server/functions-config-manifest.jsonlists/_middlewarewithruntime: "nodejs". - Visit
/logoutor 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.