Config Reference (client.json)
The entire deployment is driven by one JSON config validated before anything is created. Validation aggregates all problems in one pass (placeholder CHANGE_ME values, malformed ARNs, unsupported regions/versions) and refuses to synthesize — a wrong config fails in seconds, not mid-deploy. Secret values never appear here; secrets are always referenced by Secrets Manager ARN.
Identity & placement
| Field | Provided by | Format / rule |
|---|---|---|
account | Client | 12-digit AWS account ID |
region | Client | Must be in the EKS Auto Mode allow-list (see prerequisites §1) |
Networking (imported, never created)
| Field | Provided by | Format / rule |
|---|---|---|
vpcId | Client | vpc-…, must exist in region |
privateSubnetIds | Client | ≥ 2 distinct subnet IDs in distinct AZs, NAT egress required |
publicSubnetIds | Client | Required (≥ 2) only when ingressPublic: true |
extraSecurityGroupIds | Client | ≥ 1 SG the client owns — the app never creates security groups |
DNS & cluster shape
| Field | Provided by | Format / rule |
|---|---|---|
hostedZoneId | Client | Route53 public zone in the same account |
domain | Client | FQDN; the zone's name or a subdomain of it (checked against the actual zone) |
k8sVersion | Client (or default) | 1.31–1.36; chart floor is 1.27 |
adminRoleArns | Client | ≥ 1 IAM role ARN granted cluster admin via access entries |
ingressPublic | Client decision | Default true → internet-facing NLB restricted to allowedCidrs. Set false for an internal NLB (VPN/peering only), where no allowlist is needed |
allowedCidrs | Client | PUBLIC egress CIDRs allowed to reach the ingress, enforced as NLB security-group rules. Required when the ingress is public — an empty list fails the deploy rather than allowing everything. 0.0.0.0/0 and private/RFC1918 ranges are both rejected |
clusterEndpointPublicAccess | Client decision | Default true; false = deploys must run inside your network |
kmsKeyArn | Client (optional) | CMK for EKS secrets envelope encryption (control-plane, not the app key) |
iamPermissionsBoundaryArn | Client (optional) | Attached to every role the app creates |
Registry, app config & app secrets
| Field | Provided by | Format / rule |
|---|---|---|
chartSource | Agreement | ghcr (default) or marketplace — which build of the chart is installed. Must match the bundle you were given; the deployment names the mismatch and the fix |
ghcrSecretArn | Client stores, vendor issues token | Secrets Manager ARN whose value is a complete .dockerconfigjson. Required for ghcr; ignored for marketplace, whose images are pulled with the nodes' IAM role (your account must be subscribed to the listing) |
coreEnv / backendEnv | Client | Non-secret env values merged into each service's environment (e.g. SES_FROM_EMAIL, SES_ROLE_ARN, S3_REGION, CONTROL_PLANE_URL, GITHUB_APP_ID). A plain key/value map — validation rejects secret-looking keys, because anything here is plaintext in config and in the CloudFormation template |
There is no .env file. Every value reaches the apps through one path — the environment (envFrom) — so there are no file-vs-env precedence rules to reason about.
AWS credentials are not a client input. The deployment creates an IAM user, access key, and assumable role per service and injects them automatically (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_ROLE_ARN for core; those plus KMS_ACCESS_KEY_ID/KMS_SECRET_ACCESS_KEY/KMS_ROLE_ARN/KMS_REGION/KMS_SYMMETRIC_KEY_ID for the backend). Do not put them in coreEnv/backendEnv — the generated values win on collision.
Frontend
| Field | Provided by | Format / rule |
|---|---|---|
recaptchaSiteKey | Client (Google console) | Public site key, injected into the frontend at runtime. Its allowed-domains list must cover your domain, or the widget renders but every submit fails verification |
recaptchaSecretKey | Client (Google console) | Private key, delivered to the backend. Must be set together with the site key — a site key alone fails every verification. Note: this is the one secret stored in plaintext config (and therefore in the CloudFormation template); it is a deliberate exception so the reCAPTCHA pair stays a single obvious input |
recaptchaDisabled | Client decision | true switches reCAPTCHA off in both the backend and the frontend |
Set either the key pair or recaptchaDisabled: true — validation rejects both together and neither. reCAPTCHA is on by default, so an install with no keys and no opt-out would come up with a sign-in page that always fails.
Org seeding (optional with ghcr, required with marketplace)
| Field | Provided by | Format / rule |
|---|---|---|
seedOrgEmail | Client | Admin email; the org ID becomes the domain after @ (lowercased). Public email domains (gmail, outlook, …) are rejected. When set, the backend creates the org + admin user on first start; the generated password lands in k8s Secret openbox-seed-admin (retrieval command in stack outputs). The password is not pre-expired — change it after signing in |
Application KMS (created automatically — no config)
The deployment always creates the backend's symmetric application key: the backend cannot run without one, so it is not a client input. Alias is install-scoped (alias/openbox-backend-<your-domain>), rotation off, RETAIN on stack delete so encrypted data outlives the stack.
No key-policy grants are configured: a CDK-created key carries the account-root statement, so your backend's IAM principal can use it as long as its own IAM policy allows kms:*. If your SCPs require explicit key-policy grants instead, add them out-of-band against the exported ARN.
The key ARN is a stack output; put it in the backend env file as KMS_SYMMETRIC_KEY_ID (with KMS_REGION = deploy region).
License
| Field | Provided by | Format / rule |
|---|---|---|
licenseSecretArn | Client stores, vendor issues | ARN of the raw license token. Required unless poc |
licenseTenantId | Vendor | Required unless poc |
licenseControlPlaneUrl | Vendor | https:// URL; required unless poc or licenseMode: "airgap" |
licenseMode | Agreement | connected (default) or airgap |
poc | Agreement | true waives the license gate and uses the evaluation chart profile. Not available with chartSource: "marketplace" |
OPA policy bundle (bucket created automatically)
The deployment always creates the policy-bundle bucket (openbox-opa-policies-<account>-<region>, versioned, RETAIN) and the IAM role OPA assumes to read it. Authentication is IRSA / web identity — there are no static S3 keys to issue, store, or rotate.
| Field | Provided by | Format / rule |
|---|---|---|
opaBundleEnabled | Operator decision | Default false. The bucket starts empty, and pointing OPA at a missing bundle.tar.gz leaves it with no policy while every probe still looks healthy — so OPA runs on its built-in policy until you upload a bundle (upload command is a stack output) and set this to true. |
On the CDK path the bucket and OPA's read role are created for you; the client does not pre-create S3. The backend, however, builds the policy bundle itself on every policy or behavior-rule save and must be able to write it to this bucket. That wiring (a backend write role and the chart's global.policyBundle) is not in the CDK yet — until it ships, confirm with us before relying on governance policies on a CDK install.
What the deployment outputs back to you
Stack outputs after a successful run: the service URLs, the OPA bundle bucket name and its upload command (frontend, API, identity, OPA, core), the ingress NLB hostname, the backend KMS key ARN (if created), and the command to read the generated Keycloak admin password from the cluster. Passwords for bundled Postgres/Redis/Keycloak are generated in-cluster and never leave it.