Skip to content

3. Configure & install Helm

Owner: Cloud Ops · Time: ~45 min
You’ll finish with: the application chart installed, every pod Ready, and authentication configured against your Entra tenant.
Before you start: provider overlay from Step 2 saved in ~/arbium-install · tenant ID and agent client ID from the walkthrough’s Entra steps · GHCR credential.

This step is identical on AWS, Google Cloud, and Azure.

Every installation uses three layers, in this order:

  1. The chart’s built-in values.yaml defaults.
  2. One cloud preset: values-aws.yaml, values-gcp.yaml, or values-azure.yaml.
  3. Two untracked overlays that you own:
    • <environment>.<provider>.values.local.yaml from Step 2 for cloud wiring.
    • <environment>.values.local.yaml from this page for application, Entra, and optional fleet-console configuration.

Later files win. Keep cloud-specific values in the provider overlay and shared application values in the final overlay.

  • The application services and the embedding service.
  • Database migrations, run automatically before the application starts.
  • Scheduler CronJobs.
  • ConfigMaps, Services, service accounts, and optional network policies.
  • External Secrets resources that read your cloud secret store.
  • The shared Ingress routes.
  • Optional SCIM provisioning and the fleet console.
  • Optional HPA, PDB, and telemetry components.

Terraform owns cloud infrastructure; Helm owns everything inside Kubernetes.

Use the GHCR credential supplied by Arbium:

Terminal window
echo '<github-package-token>' | helm registry login ghcr.io \
--username <github-username-or-bot-account> \
--password-stdin
mkdir -p ~/arbium-install
cd ~/arbium-install
helm pull oci://ghcr.io/try-caret/charts/chaindb \
--version <release-version> \
--untar

The chart’s appVersion selects the matching runtime image tags. Do not override image repositories or tags unless you mirror images into ECR, Artifact Registry, or ACR.

Save this as ~/arbium-install/<environment>.values.local.yaml beside the provider overlay. Substitute your HTTPS host and Entra values. Keep this file out of source control.

global:
namespace: arbium
environment: <environment>
publicBaseUrl: https://arbium.<your-domain>
config:
deployment: selfHosted
organizationsEnabled: true
edgeFnsUrl: https://arbium.<your-domain>
edgeFnsAnonKey: self-host-local
verifyJwt: "true"
auth:
method: oidc
label: Customer SSO
publicBaseUrl: https://arbium.<your-domain>
provisioningMode: signon
oidcIssuerUrl: https://login.microsoftonline.com/<entra-tenant-id>/v2.0
oidcAudience: <arbium-agent-client-id>
oidcAllowedAlgs: RS256
oidcClientId: <arbium-agent-client-id>
oidcScopes: openid,email,profile,offline_access
oauthRedirectUrl: caretcapture://auth-callback
embedder:
enabled: true
mode: http
replicaCount: 1
scheduler:
enabled: true
migrations:
runOnStartup: true
admin:
enabled: false
scim:
enabled: false
telemetry:
sentry:
enabled: false

Sentry is opt-in. When telemetry.sentry.enabled is true, chart-managed Secrets must also set telemetry.sentry.dsn; Helm fails the render otherwise. External-Secret installs must provide a SENTRY_DSN key in the shared runtime Secret. CaptureLake, the edge functions, roots-api, Admin, SCIM, the embedder, and the LLM proxy send error events. CaptureLake sends CronJob check-ins and aggregate backlog and freshness metrics; CaptureLake, the edge functions, the embedder, and the LLM proxy send sampled latency metrics, while roots-api sends unsampled MCP latency metrics. The separately configured Roots desktop app can send main-process JavaScript crashes, but not renderer events, sessions, screenshots, or native dumps. Request data, credentials, user identity, free-form tags, breadcrumbs, exception messages, and stack-frame source context are removed. Tracing and Sentry console-log forwarding are disabled.

organizationsEnabled is an explicit opt-in for first organization creation. Leaving it at the chart default (false) performs no organization mutation through an upgrade, even with a valid license. Creation uses the customer name from the signed license. Once the organization exists, neither license-name changes nor turning the flag off rename or disable it; capacity reconciliation continues. The first eligible person who signs in through the configured OIDC provider becomes the organization administrator. Automated agents that upload activity data cannot claim this role.

The OIDC issuer must use your Entra tenant and the v2 endpoint. The audience and client ID are the bare Arbium Agent application GUID — not api://<client-id>, not an object ID. IT sets requestedAccessTokenVersion = 2 during app registration. oauthRedirectUrl is the desktop app’s own return address; IT registers it on the Arbium Agent app under Mobile and desktop applications. oidcScopes is what the deployment publishes at /functions/v1/auth-config and what the Windows desktop app asks Entra for; IT grants admin consent to exactly these. The Windows desktop app needs no Application ID URI or exposed scope — its token’s audience is the client ID itself. After the first administrator joins, changing the configured issuer prevents startup. Restore the prior issuer until an explicit audited recovery workflow is available.

The publishable edgeFnsAnonKey is not an administrative secret. The scheduler, enrollment, JWT, license, registry, SCIM, and Admin OIDC values remain in the cloud secret store configured by the provider overlay.

Use this when you want an explicit Entra group allowlist for agent access. The provider overlay must map scim-bearer to the SCIM token in the cloud secret store.

config:
auth:
provisioningMode: scim
scim:
enabled: true
existingSecret: scim-bearer
scimUserGroupIds: <users-group-object-id>[,<another-group-object-id>]
scimAdminGroupIds: ""

Give IT the Tenant URL https://arbium.<your-domain>/scim/v2 and the SCIM bearer token through the approved secure channel. Entra provisioning must map SCIM externalId from the Entra objectId for both users and groups.

The fleet console shares the same hostname and is served under /admin. It requires SCIM provisioning and at least one admin group, and the provider overlay must map the scim-bearer Secret from the selected cloud secret store.

Current dashboard releases sign in through the Arbium Agent app as a public client: IT adds the console’s callback, https://arbium.<your-domain>/admin/api/auth/oidc/callback, to that registration in the walkthrough’s Configure sign-in step. Chart versions that still use a separate Arbium Admin registration also need the admin-ui-oidc Secret and the Web redirect URI below.

config:
auth:
provisioningMode: scim
scim:
enabled: true
existingSecret: scim-bearer
scimUserGroupIds: <users-group-object-id>
scimAdminGroupIds: <admins-group-object-id>[,<another-admins-group-object-id>]
admin:
enabled: true
existingSecret: admin-ui-oidc
entra:
tenantId: <entra-tenant-id>
adminGroupId: ""
authz:
mode: scim

For chart versions with a separate Arbium Admin registration, its Web redirect URI must be exactly:

https://arbium.<your-domain>/admin/api/auth/callback

The chart generates and persists the console’s session secret through External Secrets. Do not put the Admin client secret or a session secret in the values file.

CPU mode works on every supported cloud and is the baseline for small installs:

embedder:
enabled: true
gpu:
enabled: false
resources:
requests:
cpu: "1"
memory: 2Gi
limits:
cpu: "2"
memory: 4Gi

For AWS or Google Cloud, enable GPU mode only after the provider guide’s GPU node pool is ready. The cloud preset supplies the provider-specific selectors and tolerations:

embedder:
enabled: true
gpu:
enabled: true

The Azure baseline is CPU-only. Add and validate an AKS GPU pool before enabling GPU mode there.

Set <provider> to aws, gcp, or azure and use the same three files for lint, render, install, and future upgrades:

Terminal window
helm lint ./chaindb \
-f ./chaindb/values-<provider>.yaml \
-f <environment>.<provider>.values.local.yaml \
-f <environment>.values.local.yaml
helm template arbium ./chaindb \
--namespace arbium \
-f ./chaindb/values-<provider>.yaml \
-f <environment>.<provider>.values.local.yaml \
-f <environment>.values.local.yaml \
> /tmp/arbium-rendered.yaml

Review the rendered resources before applying. Confirm:

  • Every image is versioned for the intended release.
  • The External Secrets provider matches your cloud.
  • The database connection method matches the provider guide.
  • ingress.host, global.publicBaseUrl, and the Entra Admin redirect host agree.
  • OIDC uses the v2 issuer, bare client GUID, and RS256.
  • Workloads reference the ghcr-pull image pull Secret.
  • The rendered OIDC_ISSUER_URL, OIDC_AUDIENCE, and OIDC_CLIENT_ID values are non-empty.
  • Optional SCIM and fleet-console resources are either fully configured or disabled.
Terminal window
helm upgrade --install arbium ./chaindb \
--namespace arbium \
--create-namespace \
--timeout 15m \
-f ./chaindb/values-<provider>.yaml \
-f <environment>.<provider>.values.local.yaml \
-f <environment>.values.local.yaml

The chart creates the arbium namespace. External Secrets then materializes the runtime and GHCR pull Secrets. Pods can briefly report ImagePullBackOff while the pull Secret is being created; Kubernetes retries automatically.

Watch the rollout:

Terminal window
kubectl get externalsecret -n arbium
kubectl get pods -n arbium -w
kubectl rollout status deployment/chaindb-edge-fns -n arbium --timeout=15m

Read the ALB hostname and point your DNS record at it:

Terminal window
kubectl get ingress chaindb -n arbium \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}{"\n"}'

Use a Route 53 alias or a CNAME. The ACM validation record from Step 2 remains separate from this application record.

DNS should already point to the reserved static address. Wait for the Google managed certificate:

Terminal window
kubectl get managedcertificate chaindb-tls -n arbium -w

After it becomes Active and HTTPS works, change kubernetes.io/ingress.allow-http to "false" in the GCP provider values and run the Helm command again.

Apply the prepared Certificate after the namespace exists:

Terminal window
kubectl apply -n arbium -f manifests/tls-certificate.yaml
kubectl get certificate chaindb-tls -n arbium -w

The certificate should become Ready=True after DNS resolves and Let’s Encrypt completes HTTP-01 validation.

Terminal window
kubectl -n arbium get configmap chaindb-config \
-o jsonpath='{.data.OIDC_ISSUER_URL}{"\n"}{.data.OIDC_AUDIENCE}{"\n"}{.data.OIDC_ALLOWED_ALGS}{"\n"}'

Expected: your v2 issuer URL, the Arbium Agent client GUID, and RS256. If they differ, correct the values file and run helm upgrade again.

Migrations run through the version-matched chaindb-migrations init container: pending migrations apply before each application pod starts, and versions already recorded in schema history are skipped. No separate migration Job is required.

migrations.workloads lists which deployments gate their startup on that run — by default edge-fns, scim, admin-ui and roots-api. A listed workload will not start while the database or the migrations image is unavailable, so if a pod is wedged in Init during an outage, check its migrations container first. Drop a name to let that workload start without confirming the schema.

If an approved external pipeline owns migrations, set:

migrations:
runOnStartup: false

Authenticate to GHCR, pull the new chart into a new directory, review the release notes and values changes, then repeat the render and install commands with the same overlays. Always pin the new chart version; never track latest.

Uninstall the application before destroying the provider foundation:

Terminal window
helm uninstall arbium -n arbium
kubectl delete namespace arbium

Continue to 4. Verify deployment.