Configuration Guide
This guide provides the production parameter map for DevOps teams deploying Control Core. It focuses only on customer-operable settings.
Configuration domains
- Control Plane runtime and ingress
- Bouncer registration and resource mapping
- Database and Redis connectivity
- GitHub controls repository sync
- Security controls and key rotation
- Licensing and telemetry behavior
Control Plane required parameters
Minimum set:
CONTROL_PLANE_PUBLIC_URLDATABASE_URLREDIS_URLSECRET_KEYJWT_SECRET_KEYGITHUB_REPO_URLGITHUB_BRANCHGITHUB_TOKEN
Recommended additions:
CONTROL_PLANE_HEALTH_URLCORS_ORIGINSLOG_LEVELBACKUP_SCHEDULE
Bouncer required parameters
Minimum set:
BOUNCER_IDBOUNCER_NAMEBOUNCER_TYPEENVIRONMENT(sandboxorproduction)PAP_API_URLAPI_KEYRESOURCE_NAMERESOURCE_TYPETARGET_HOSTORIGINAL_HOST_URLSECURITY_POSTURE
Recommended additions:
DEPLOYMENT_PLATFORMBOUNCER_PUBLIC_URLLOG_LEVELHEARTBEAT_INTERVAL
Bouncer and resource discovery mapping
For clean resource registration:
- Keep
RESOURCE_NAMEstable for Sandbox and Production counterparts. - Set
RESOURCE_TYPEto actual workload category (api,webapp,database,ai-agent,mcp-server). - Use internal address in
TARGET_HOST. - Use externally reachable URL in
ORIGINAL_HOST_URL. - Use environment-specific
API_KEY.
GitHub controls sync configuration
Control Plane must have:
GITHUB_REPO_URL(controls repository)GITHUB_BRANCH(deployment branch)GITHUB_TOKEN(least-privilege token)
Operational guardrails:
- Branch protection enabled for controls branch.
- Token stored in secret manager, not committed files.
- Repo access test included in deployment validation.
Token requirements
Personal Access Token must grant read+write to the controls repository:
- Classic PAT:
reposcope (full Contents read/write so the Control Bridge can pull and push policy bundles). - Fine-grained PAT: select the controls repository and grant
Contents: Read and write,Metadata: Read-only, andWebhooks: Read and writeif you want commit-driven sync. - Token must not be expired and the underlying GitHub user must have
Maintainer+ role on the repository.
The Control Plane stores the token encrypted at rest (AES-256-GCM via the
configured Secrets backend). The Test Connection button in
Settings → Controls Repository performs an authenticated GET against
the repo's default branch ref before saving.
Repository structure
When the Control Plane writes a control to GitHub, it places the file in a folder derived from the Protected Resource and the control's environment + status. Bouncers watch the folders that match their assigned resource. This layout lets the same controls repository host policies for many resources without cross-traffic.
your-controls-repo/
└── policies/
└── <resource-slug>/
├── sandbox/
│ ├── enabled/
│ │ └── policy_<id>.rego
│ ├── disabled/
│ │ └── policy_<id>.rego
│ ├── draft/
│ │ └── policy_<id>.rego
│ └── archived/
└── production/
├── enabled/
├── disabled/
└── archived/
Folder path format:
policies/<resource-slug>/<environment>/<state>/policy_<id>.rego
<resource-slug>— Protected Resource name normalized (lowercase, hyphens).<environment>—sandboxorproduction.<state>—enabled,disabled,draft, orarchived.<id>— control ID from the Control Plane database.
The Control Plane creates the folder structure automatically the first time you save a control. You do not need to pre-create folders. When a control moves between states (enable → disable, draft → enabled), the Control Plane writes the file to the new folder AND removes the stale copy from the previous folder during the same sync — so the policy bundle never carries two copies of the same control.
Setup checklist
- Create a GitHub repository (public or private) for controls.
- Generate a PAT with the scopes above.
- In Settings → Controls Repository, enter the repository URL, branch, and token, then click Test Connection.
- Save Settings — the Control Plane writes the folder layout when you save your first control.
- Deploy bouncers — they automatically watch their assigned resource folders via the Control Bridge.
Helm values pattern
global:
controlPlanePublicUrl: "https://controlplane.customer-domain.com"
controlPlane:
env:
DATABASE_URL: "postgresql://<user>:<password>@<db-host>:5432/<db-name>"
REDIS_URL: "redis://:<password>@<redis-host>:6379/0"
SECRET_KEY: "<strong-random-secret>"
JWT_SECRET_KEY: "<strong-random-jwt-secret>"
GITHUB_REPO_URL: "https://github.com/<org>/<controls-repo>"
GITHUB_BRANCH: "main"
GITHUB_TOKEN: "<github-token>"
bouncer:
env:
BOUNCER_ID: "bouncer-sandbox-01"
BOUNCER_NAME: "Sandbox API Bouncer"
BOUNCER_TYPE: "sidecar"
ENVIRONMENT: "sandbox"
PAP_API_URL: "https://controlplane.customer-domain.com"
API_KEY: "<sandbox-api-key>"
RESOURCE_NAME: "Customer API"
RESOURCE_TYPE: "api"
TARGET_HOST: "customer-api.default.svc.cluster.local:8080"
ORIGINAL_HOST_URL: "https://api.customer-domain.com"
SECURITY_POSTURE: "deny-all"
Docker Compose env pattern
CONTROL_PLANE_PUBLIC_URL=https://controlplane.customer-domain.com
DATABASE_URL=postgresql://<user>:<password>@<db-host>:5432/<db-name>
REDIS_URL=redis://:<password>@<redis-host>:6379/0
SECRET_KEY=<strong-random-secret>
JWT_SECRET_KEY=<strong-random-jwt-secret>
GITHUB_REPO_URL=https://github.com/<org>/<controls-repo>
GITHUB_BRANCH=main
GITHUB_TOKEN=<github-token>
BOUNCER_ID=bouncer-sandbox-01
BOUNCER_NAME=Sandbox API Bouncer
BOUNCER_TYPE=sidecar
ENVIRONMENT=sandbox
PAP_API_URL=https://controlplane.customer-domain.com
API_KEY=<sandbox-api-key>
RESOURCE_NAME=Customer API
RESOURCE_TYPE=api
TARGET_HOST=customer-api:8080
ORIGINAL_HOST_URL=https://api.customer-domain.com
SECURITY_POSTURE=deny-all
Security and domain baseline
- TLS 1.2+ on all external domains.
- Customer-managed certificates and secret rotation.
- Private network-only database and Redis.
- Environment-scoped API keys with regular rotation.
- Least-privilege deployment identities.
Licensing and telemetry
- Trial default: 90 days.
- Extension codes applied in Settings -> Subscription.
- Telemetry endpoint is configurable; offline extension workflow remains available.
Authentication (login methods)
Audience: Platform / security administrators. Time: 15–30 min per IdP.
Control Core shows exactly two login methods on /login:
- Enterprise SSO — delegates to the identity provider you configure below. Also renders "Continue with Google" and "Continue with Microsoft" buttons that all use the single OIDC connection you configure (pick one IdP at deploy time).
- Password or Passkey — local
ccadminand any locally-created users, plus a "Use saved passkey on this device" button that uses a registered WebAuthn credential.
Bootstrap admin (ccadmin)
The ccadmin user is seeded from CC_BUILTIN_ADMIN_USER / CC_BUILTIN_ADMIN_PASS at database
initialisation. The password is bcrypt-hashed and the plaintext value is never stored.
- First sign-in forces a password change.
- While SSO is configured, only
ccadmincan still create users locally — all other provisioning flows through your IdP.
Passkey registration
- After a successful password sign-in, Control Core prompts the user to save a passkey on that device. This uses WebAuthn platform authenticators (Face ID, Touch ID, Windows Hello, hardware keys).
- Registration and authentication are verified server-side against the challenge, RP ID, and origin — clients cannot pre-sign their own credentials.
- Set
FRONTEND_URLto the canonical public URL. Control Core derives the RP ID and expected origin from it. For multi-origin setups (e.g. staging + prod), also setPASSKEY_RP_IDandPASSKEY_ALLOWED_ORIGINS(comma-separated).
Identity provider onboarding
All providers below use the single generic OIDC variable set. Configure one provider.
Troubleshooting: after changing any
OIDC_*variable, restart the PAP API (docker compose restart pap-api). Then open Settings -> User Management -> SSO Configuration and click Test connection to validate the discovery document before asking users to sign in.
Microsoft Azure AD / Entra ID
- In the Entra portal, register a new application.
- Add a Web redirect URI:
https://controlplane.<your-domain>/auth/sso/callback. - Under Certificates & secrets, create a client secret.
- Under App roles, define roles such as
ControlCorePolicyAdmin,ControlCoreViewer. - Under Token configuration, add the
rolesoptional claim to the ID token. - Set environment variables:
OIDC_CLIENT_ID= application (client) IDOIDC_CLIENT_SECRET= client secret valueOIDC_ISSUER_URL=https://login.microsoftonline.com/<tenant_id>/v2.0OIDC_REDIRECT_URI=https://controlplane.<your-domain>/auth/sso/callbackOIDC_ROLE_CLAIM=roles
Okta
- In Okta admin, create an OIDC — Web Application integration.
- Sign-in redirect URI:
https://controlplane.<your-domain>/auth/sso/callback. - Grant types: authorization code only.
- Assign groups and enable the
groupsclaim. - Set:
OIDC_CLIENT_ID,OIDC_CLIENT_SECRETOIDC_ISSUER_URL=https://<your-okta>.okta.com/oauth2/defaultOIDC_REDIRECT_URIOIDC_ROLE_CLAIM=groups
Auth0
- Create a Regular Web Application.
- Add the callback URL
https://controlplane.<your-domain>/auth/sso/callback. - Add an Action or Rule that emits a namespaced roles claim, e.g.
https://controlcore.io/roles. - Set:
OIDC_CLIENT_ID,OIDC_CLIENT_SECRETOIDC_ISSUER_URL=https://<tenant>.auth0.com/OIDC_REDIRECT_URIOIDC_ROLE_CLAIM=https://controlcore.io/roles
PingID / PingOne
- In PingOne, create an OIDC web application.
- Grant types: authorization code.
- Redirect URI:
https://controlplane.<your-domain>/auth/sso/callback. - Add a custom
rolesclaim. - Set
OIDC_*withOIDC_ISSUER_URLofhttps://auth.pingone.com/<env_id>/as.
Google Workspace
- In Google Cloud Console, create an OAuth 2.0 Client ID of type Web application.
- Add the authorised redirect URI
https://controlplane.<your-domain>/auth/sso/callback. - Restrict to your Workspace domain through the Admin console.
- Map groups via the Admin SDK or a federated claim and use
groupsas the claim. - Set:
OIDC_CLIENT_ID,OIDC_CLIENT_SECRETOIDC_ISSUER_URL=https://accounts.google.comOIDC_REDIRECT_URIOIDC_ROLE_CLAIM=groups
Role mapping (IdP group → Control Core role)
Map IdP roles or groups to the Control Core roles below. Mapping is managed in the admin UI
(Settings → User Management → SSO Configuration → Role mapping) and persisted to the
database. The environment variable OIDC_ROLE_MAPPING_JSON, when set, overrides the UI table
at runtime.
| Control Core role | What it can do |
|---|---|
super_admin | Full administrative access, including user management |
policy_admin | Author/promote controls, manage PIPs |
security_analyst | Read controls, review audit logs, replay denials |
resource_manager | Manage protected resources and bouncers |
developer | Author controls in sandbox environments |
viewer | Read-only access |
Example env-var override:
OIDC_ROLE_CLAIM=roles
OIDC_ROLE_MAPPING_JSON={"ControlCorePolicyAdmin":"policy_admin","ControlCoreViewer":"viewer"}
Users without a matching role default to viewer.
Enforcement
- While SSO is enabled (
AUTH_METHOD_SSO_ENABLED=trueand valid OIDC/SAML env), the Create User button in the Users tab is disabled andPOST /auth/usersreturns HTTP 409 — except for the built-inccadminaccount. - Turning SSO off makes
ccadminthe only account that can add users manually. - SAML 2.0 is documented here for completeness; the SAML callback is gated behind
AUTH_METHOD_SSO_SAML_ENABLED=trueand may return 501 in some builds.