Skip to main content

Install (AWS Marketplace)

These steps were verified end to end on Amazon EKS: a clean install with chart 0.2.5 (organization auto-created, sign-in with the generated administrator password), and governance policies through an S3 bucket with chart 0.2.6.

1. Authenticate Helm to the Marketplace registry​

aws ecr get-login-password --region us-east-1 \
| helm registry login 709825985650.dkr.ecr.us-east-1.amazonaws.com \
--username AWS --password-stdin

2. Create the namespace and license secret​

From the directory where you unpacked your license bundle:

kubectl create namespace openbox

kubectl -n openbox create secret generic openbox-license \
--from-file=token=./license.jwt \
--from-file=report-signing-key=./report-signing.key

The secret must be named openbox-license with exactly these two keys.

Create this first

If the secret is missing, the pre-install license check cannot start: its pod waits forever for the volume, and helm install hangs with no error until it times out.

3. Prepare your public URLs​

The console and the API must know the public addresses they are served on. Save this as public-urls.yaml, replacing YOUR_DOMAIN with the domain you pass in step 4:

openbox-fe:
extraEnv:
- { name: VITE_API_URL, value: "https://openbox-api.YOUR_DOMAIN" }
- { name: VITE_CORE_API_URL, value: "https://openbox-core.YOUR_DOMAIN" }
- { name: VITE_WS_URL, value: "https://openbox-api.YOUR_DOMAIN" }
- { name: VITE_KEYCLOAK_BASE_URL, value: "https://identity.YOUR_DOMAIN" }
- { name: VITE_KEYCLOAK_CLIENT_ID, value: "openbox" }
- { name: VITE_KEYCLOAK_SHARED_REALM, value: "openbox" }
openbox-backend:
secret:
stringData:
KEYCLOAK_BASE_URL: "https://identity.YOUR_DOMAIN"
FRONTEND_URL: "https://openbox-eks.YOUR_DOMAIN"
ALLOWED_ORIGINS: "https://openbox-eks.YOUR_DOMAIN"
KEYCLOAK_CLIENT_ROOT_URL: "https://openbox-eks.YOUR_DOMAIN/*"
KEYCLOAK_CLIENT_HOME_URL: "https://openbox-eks.YOUR_DOMAIN/"
KEYCLOAK_CLIENT_REDIRECT_URIS: "https://openbox-eks.YOUR_DOMAIN/*"
KEYCLOAK_CLIENT_WEB_ORIGINS: "https://openbox-eks.YOUR_DOMAIN"
KEYCLOAK_CLIENT_ADMIN_URL: "https://openbox-eks.YOUR_DOMAIN/*"
KEYCLOAK_CLIENT_POST_LOGOUT_REDIRECT_URIS: "https://openbox-eks.YOUR_DOMAIN/*"

Without this file the console cannot reach the API, and Keycloak rejects the browser's login redirect.

Set these right on the first install

The redirect and origin addresses are written into your organization's identity client when the organization is created. Changing them afterwards does not update an organization that already exists.

4. Install the chart​

helm install openbox \
oci://709825985650.dkr.ecr.us-east-1.amazonaws.com/openbox-ai/openbox/charts/openbox \
--version 0.2.6 \
--namespace openbox \
--set global.ingress.domain=YOUR_DOMAIN \
--set global.license.expectedTenantId=YOUR_TENANT_ID \
--set global.seedOrg.adminEmail=YOUR_ADMIN_EMAIL \
--set global.recaptcha.disabled=true \
-f public-urls.yaml
ValueRequiredNotes
global.ingress.domainyesYour DNS domain
global.license.expectedTenantIdyesExactly as issued. The chart ships the placeholder REPLACE_WITH_YOUR_TENANT_ID
global.seedOrg.adminEmailyesYour first administrator, company domain. Missing, malformed, or still the placeholder REPLACE_WITH_ADMIN_EMAIL → the install stops within seconds, before anything is created
global.recaptcha.disabledif no Google keysDefault false (reCAPTCHA on). One value controls both the backend and the console

Passwords for the bundled PostgreSQL, Keycloak and Temporal are generated inside your cluster during install and stored in openbox-gen-* secrets. Do not supply your own — the chart schema rejects inline credentials by design.

Storage class​

If your cluster has no default StorageClass, name one for each volume:

  --set postgresql.primary.persistence.storageClass=YOUR_CLASS \
--set openbox-core.redis.storageClass=YOUR_CLASS \
--set identity-service.persistence.storageClassName=YOUR_CLASS

Note the third key is spelled storageClassName.

Optional: voice agent executor and ElevenLabs monitor​

Both ship disabled. Enable with --set openbox-executor.enabled=true (served at executor.YOUR_DOMAIN) and --set openbox-evri-monitor.enabled=true (served at evri-monitor.YOUR_DOMAIN), and add DNS records for them.

  • The executor does not start until its governance credentials exist. Create them in the console after your organization exists, then:

    kubectl -n openbox create secret generic openbox-executor-governance \
    --from-literal=OPENBOX_API_KEY=obx_live_... \
    --from-literal=OPENBOX_AGENT_DID=did:aip:<uuid> \
    --from-literal=OPENBOX_AGENT_PRIVATE_KEY=...
    kubectl -n openbox rollout restart deployment/openbox-executor-service

    The API key must start with obx_live_ or obx_test_; the DID must be did:aip:<uuid>.

  • The monitor needs the webhook signing secret ElevenLabs issues: store it in a Secret (key ELEVENLABS_WEBHOOK_SECRET) and add --set openbox-evri-monitor.webhookSecret.existingSecret=YOUR_SECRET.

Governance policies (S3 bucket)​

Policies and behavior rules are compiled into one bundle that the backend writes to S3 and the policy engine (OPA) reads. Without a bucket the platform runs, but saving a policy or behavior rule fails, and OPA runs with no policy, so every action is allowed.

  1. Create a private S3 bucket with default encryption. It also stores audit and evidence exports, so dedicate it to this install.

  2. Create two IAM roles that trust your cluster's OIDC provider (IRSA), condition sts.amazonaws.com audience and the service-account subject:

    RoleService account (<namespace>:<name>)Permissions
    OPA readeropenbox:opa-s3-bundle-sas3:GetObject on YOUR_BUCKET/bundle.tar.gz only
    Backend writeropenbox:openbox-backend-roles3:PutObject on YOUR_BUCKET/bundle.tar.gz; s3:PutObject, s3:GetObject, s3:DeleteObject on YOUR_BUCKET/exports/*
  3. Add to the install (or upgrade) command:

      --set global.policyBundle.bucket=YOUR_BUCKET \
    --set global.policyBundle.region=YOUR_BUCKET_REGION \
    --set global.policyBundle.opaRoleArn=arn:aws:iam::ACCOUNT:role/OPA_READER \
    --set global.policyBundle.backendRoleArn=arn:aws:iam::ACCOUNT:role/BACKEND_WRITER

Optional global.policyBundle.keyPrefix (for example openbox/) places the bundle at <prefix>bundle.tar.gz; scope both roles to that key. Leave backendRoleArn empty if the backend gets AWS credentials through EKS Pod Identity. Enabling or changing these values on an upgrade restarts the backend and OPA.

Until the first policy is saved, OPA logs Bundle load failed: server replied with Forbidden on every poll — there is no bundle yet. That stops after the first save.

Without a GPU node​

  --set llamafirewall.enabled=false

5. Wait for convergence​

kubectl -n openbox get pods -w

Allow about 10 minutes. Pods that depend on the database schema or the workflow engine show Init until it is ready; their init container log says what they are waiting for. One Temporal pod may restart once while the services register; repeated restarts mean a real fault — see Troubleshooting.

Confirm the organization was created:

kubectl -n openbox logs -l app.kubernetes.io/name=openbox-backend-service -c create-organization

On the backend's first start this prints organization created; on later starts, organization already exists — nothing to do.

6. Create DNS records​

The ingress load balancer exists only after install, so create records now. Point each hostname at it:

HostnameServes
openbox-eks.YOUR_DOMAINWeb console
openbox-api.YOUR_DOMAINREST API
openbox-core.YOUR_DOMAINCore service
identity.YOUR_DOMAINKeycloak
opa.YOUR_DOMAINPolicy engine

7. Sign in​

Your organization and administrator already exist. Get the generated password:

kubectl -n openbox get secret openbox-seed-admin \
-o jsonpath='{.data.password}' | base64 -d; echo

Open https://openbox-eks.YOUR_DOMAIN and sign in with YOUR_ADMIN_EMAIL and that password. Change it after the first sign-in.

To choose the password yourself, create a secret with a password key before installing and add --set global.seedOrg.existingSecret=YOUR_SECRET_NAME.

Upgrading​

helm upgrade openbox \
oci://709825985650.dkr.ecr.us-east-1.amazonaws.com/openbox-ai/openbox/charts/openbox \
--version NEW_VERSION --namespace openbox \
--reset-then-reuse-values -f public-urls.yaml

Use --reset-then-reuse-values, not --reuse-values: the latter keeps the old chart's defaults and silently ignores settings a new version introduces.

Uninstalling​

helm uninstall openbox --namespace openbox

Generated secrets and volumes are kept on purpose, so a reinstall over the same volumes still has matching credentials. To remove all data, delete the PersistentVolumeClaims together with the openbox-gen-* and openbox-seed-admin secrets:

kubectl -n openbox delete pvc --all
kubectl -n openbox get secret -o name | grep -E 'openbox-gen-|openbox-seed-admin' | xargs kubectl -n openbox delete
Never delete one without the other

Deleting the secrets but keeping the volumes leaves the database with passwords that no longer match: PostgreSQL crash-loops and the only recovery is deleting the volume.