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.
Values layering
Section titled “Values layering”Every installation uses three layers, in this order:
- The chart’s built-in
values.yamldefaults. - One cloud preset:
values-aws.yaml,values-gcp.yaml, orvalues-azure.yaml. - Two untracked overlays that you own:
<environment>.<provider>.values.local.yamlfrom Step 2 for cloud wiring.<environment>.values.local.yamlfrom 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.
What the chart installs
Section titled “What the chart installs”- The application services and the embedding service.
- Database migrations, run automatically before the application starts.
- Scheduled background jobs, run by a bundled workflow controller (Argo Workflows). A first install applies its
custom resource definitions automatically; an upgrade from a release that predates it applies them by hand first,
as in the “Upgrading from the CronJob engine” section of the chart’s
INSTALL.md. - 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.
Authenticate and pull the release
Section titled “Authenticate and pull the release”Use the GHCR credential supplied by Arbium:
echo '<github-package-token>' | helm registry login ghcr.io \ --username <github-username-or-bot-account> \ --password-stdin
mkdir -p ~/arbium-installcd ~/arbium-installhelm pull oci://ghcr.io/try-caret/charts/chaindb \ --version <release-version> \ --untar --untardir chaindb-<release-version>The chart is unpacked into chaindb-<release-version>, so a release you pull
later never lands on top of this one.
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.
Create the shared application values
Section titled “Create the shared application values”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 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: falseSentry 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 scheduled-job 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 Roots desktop app can send main-process JavaScript crashes, but not
renderer events, sessions, screenshots, or native dumps. Set
telemetry.sentry.desktopDsn to the Sentry DSN it should report to, and the
deployment hands it to every device the next time the app starts, with nothing to change in
your device management. With the relay and ingress both enabled, devices send
through this deployment’s own address instead of directly to Sentry.
Request data, user identity, the transaction name, and free-form tags are
removed, as are console breadcrumbs and the query string of any outgoing URL
recorded as a breadcrumb. Exception messages, stack-frame source context and
the remaining breadcrumbs are kept: an error stripped of its message cannot be
acted on. An error message can therefore quote the text that caused it, which
may include captured content. Tracing and Sentry console-log forwarding are
disabled.
Organizations are always enabled. Every self-hosted startup runs organization setup before the service becomes ready. With a write-capable signed license and valid tenant-scoped OIDC configuration, it creates one organization using the license’s customer name. Restarts reuse it and reconcile licensed capacity; later license-name changes do not rename it. There is no opt-out flag.
The first eligible person who signs in through the Admin dashboard’s configured OIDC application claims the administrator role. Restrict application assignment and, when configured, require the initial administrator group. Claiming requires a write-capable license and a fresh Admin sign-in. Automated uploads and normal product sign-on cannot claim this role.
Upgrade note: organizations and membership-based sharing are no longer optional
Section titled “Upgrade note: organizations and membership-based sharing are no longer optional”Organization setup and membership-based memory sharing are always enabled. Eligible active members can search their organization or teams within administrator search permissions. Raw screen content and profiles remain personal. People without eligible membership retain personal memory (including analysts), unless administrator policy denies access; explicit shared searches require eligible membership. Automatic organization setup does not add memberships for existing sign-on users, and matching email domains and ordinary sign-on do not grant shared access.
Starting with chart 0.6.66, remove
rootsApi.env.CHAINDB_ORGANIZATION_SHARING_ENABLED from complete values files and
any manually configured environment. It is no longer read: an old "false" does
not keep sharing off. There is no deployment opt-out. Verify membership coverage,
administrator search permissions, privacy/access disclosures and any required
user notice before upgrading. Administrator permissions can restrict API
access to personal or team searches; a no-access rule continues to deny all tools
even when sharing membership is unavailable. These API permissions do not change
older stored-memory SQL policies.
Upgrading also changes database-backed access to older stored memory. By
default, the chart applies all pending database migrations before starting
upgraded services. If the structured-membership migration has not yet been
applied, older stored-memory sharing changes from matching email domains to
active organization memberships. Installations already on 0.6.65 retain those
policies; this release removes the API rollout flag without changing migration
SQL. There is no switch to defer only the policy change. migrations.runOnStartup=false skips all startup migrations and is
not a safe per-change gate; it does not undo changes already applied. Approve
coverage, privacy/access disclosures, user notice and migration ordering before
the upgrade.
Remove config.organizationsEnabled from your values and
CHAINDB_ORGANIZATIONS_ENABLED from any manually configured environment.
Neither setting is read anymore; leaving an old false does not disable
organizations. Upgrade with complete values files, not --reuse-values.
Before upgrading, check config.auth.method: oidc, a valid external HTTPS
config.auth.oidcIssuerUrl (no whitespace, at most 2,048 characters, not a built-in
/auth/v1 issuer), the correct audience and supported asymmetric algorithms
(RS256 or ES256, not HS256). Check Admin application assignment/group
restrictions. For an installation without an organization, the write-capable
signed license needs a non-blank customer name of at most 200 characters and a
positive integer user limit no greater than 2,147,483,647. For an existing
organization with memberships, retain its mapped issuer.
Setup runs before any application requests are served. Invalid configuration prevents the whole main service from becoming ready, not just the Admin dashboard; its startup log supplies a sanitized failure reason without printing credentials or license values. Fix that setting before retrying. Without a write-capable license, no organization is created and Admin access remains unavailable.
Shared searches now require eligible active memberships in the same active
organization, not matching email domains. Until memberships are provisioned,
people retain personal memory only, including analysts; explicit team and
organization searches are refused. A search permission of none still denies
all tools. Claim the first administrator through the Admin dashboard, then
provision memberships through your trusted SCIM connection or explicit
authorized invitations/operator actions. Normal sign-on does not grant
membership. For SCIM installations, verify membership coverage after the
post-claim reconciliation. This upgrade does not infer memberships from email
addresses or transfer ownership of previously recorded activity.
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.
Optional: require SCIM-provisioned users
Section titled “Optional: require SCIM-provisioned users”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-bearerWho can use Arbium and who is an admin are not Helm values. Once the release is installed, set them once from inside the cluster. Until you do, nobody can sign in or capture:
cat > access-groups.json <<'EOF'{"spec":{"containers":[{"name":"psql","image":"postgres:16-alpine", "command":["psql","$(DATABASE_URL)","-v","ON_ERROR_STOP=1","-c", "select * from public.set_access_groups(array['<users-group-object-id>'], array['<admins-group-object-id>'], '<your name>')"], "env":[{"name":"DATABASE_URL","valueFrom":{"secretKeyRef":{"name":"chaindb-runtime","key":"DATABASE_URL"}}}]}]}}EOFkubectl -n arbium run access-groups --rm --attach --restart=Never \ --image=postgres:16-alpine --overrides="$(cat access-groups.json)"chaindb-runtime is the chart’s default runtime Secret; if your values point
DATABASE_URL at another Secret, use that name instead.
List several groups as array['<id>', '<id>']. Use array[]::text[] for the
admin groups until you enable the fleet console. The command prints the groups
now in effect. After that, an admin changes them in the dashboard under
Organization → Access.
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.
Optional: enable the fleet console
Section titled “Optional: enable the fleet console”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
admin: enabled: true existingSecret: admin-ui-oidc entra: tenantId: <entra-tenant-id> adminGroupId: "" authz: mode: scimFor chart versions with a separate Arbium Admin registration, its Web redirect URI must be exactly:
https://arbium.<your-domain>/admin/api/auth/callbackIf you set the groups before enabling the console, run the set_access_groups
command above again with the admin group listed.
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.
Choose CPU or GPU embedding
Section titled “Choose CPU or GPU embedding”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: 4GiFor 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: trueThe Azure baseline is CPU-only. Add and validate an AKS GPU pool before enabling GPU mode there.
Render and review
Section titled “Render and review”Set <provider> to aws, gcp, or azure and use the same three files for
lint, render, and install. For a later upgrade, use the provider file from the release you install:
helm lint ./chaindb-<release-version>/chaindb \ --namespace arbium \ -f ./chaindb-<release-version>/chaindb/values-<provider>.yaml \ -f <environment>.<provider>.values.local.yaml \ -f <environment>.values.local.yaml
helm template arbium ./chaindb-<release-version>/chaindb \ --namespace arbium \ -f ./chaindb-<release-version>/chaindb/values-<provider>.yaml \ -f <environment>.<provider>.values.local.yaml \ -f <environment>.values.local.yaml \ > /tmp/arbium-rendered.yamlReview 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-pullimage pull Secret. - The rendered
OIDC_ISSUER_URL,OIDC_AUDIENCE, andOIDC_CLIENT_IDvalues are non-empty. - Optional SCIM and fleet-console resources are either fully configured or disabled.
Install
Section titled “Install”helm upgrade --install arbium ./chaindb-<release-version>/chaindb \ --namespace arbium \ --create-namespace \ --timeout 15m \ -f ./chaindb-<release-version>/chaindb/values-<provider>.yaml \ -f <environment>.<provider>.values.local.yaml \ -f <environment>.values.local.yamlThe 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:
kubectl get externalsecret -n arbiumkubectl get pods -n arbium -wkubectl rollout status deployment/chaindb-edge-fns -n arbium --timeout=15mFinish provider ingress and TLS
Section titled “Finish provider ingress and TLS”Read the ALB hostname and point your DNS record at it:
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.
Google Cloud
Section titled “Google Cloud”DNS should already point to the reserved static address. Wait for the Google managed certificate:
kubectl get managedcertificate chaindb-tls -n arbium -wAfter 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:
kubectl apply -n arbium -f manifests/tls-certificate.yamlkubectl get certificate chaindb-tls -n arbium -wThe certificate should become Ready=True after DNS resolves and Let’s Encrypt
completes HTTP-01 validation.
Confirm authentication configuration
Section titled “Confirm authentication configuration”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
Section titled “Migrations”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: falseRun that pipeline before the upgraded services start. From the first release
whose migrations include 20261023210000_mcp_token_revocations, the API checks
a database table on every connector request (it is how a connector
token is withdrawn), so with startup migrations off, or with roots-api removed
from migrations.workloads, connector requests answer “Could not check access
right now” until the migrations have run. Most connector clients retry on their
own; some may ask people to sign in again. Apply the migrations first to avoid
the gap.
Upgrading the chart
Section titled “Upgrading the chart”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.
Upgrading a release that predates the bundled workflow controller. Helm
installs a chart’s custom resource definitions only on the first install, never
on an upgrade, so apply them by hand from the new chart before the
helm upgrade, or the upgrade fails with no matches for kind "CronWorkflow":
kubectl --kubeconfig <path> --context <name> apply --server-side --force-conflicts \ -f ./chaindb-<release-version>/chaindb/crds/Then follow the “Upgrading from the CronJob engine” section of the chart’s
INSTALL.md: it also covers the values to remove, the namespace the release must
be installed into, and the moment to pick so no scheduled job is running.
The chart makes each serving pod keep answering for 10 seconds after Kubernetes
starts to stop it, so new requests that are still being routed to the old pod
during a rolling update are not refused, and gives it 40 seconds in total to
finish what it is doing. The first upgrade to a version with this setting still
drops some calls, because the pods it replaces do not have it yet. Clients that
keep connections open may still see a reset when the old pod closes idle
connections. A rollout is not slower unless the cluster is short of capacity,
because each old pod keeps its CPU and memory for up to those 10 seconds plus
its shutdown; a node drain waits for it. This needs Kubernetes 1.30 or newer.
Tune it with gracefulStop.preStopSleepSeconds and
gracefulStop.terminationGracePeriodSeconds (one setting for all pods). The
10-second sleep must be longer than your load balancer takes to stop sending
new requests to a stopping pod, and the load balancer’s connection draining
(backendConfig.drainingTimeoutSec on Google Cloud; once you enable
backendConfig the chart sets it to 40 seconds, until then GKE’s own default
applies) should be at least the 40-second total, so that requests already in
flight can finish.
Check the release before and after the upgrade
Section titled “Check the release before and after the upgrade”When the release check is set up (see Run the release check, which also shows how to create the check account), wrap the upgrade with it:
-
Run the check before the upgrade. It must pass. If it fails, do not upgrade: a failing starting point hides what the upgrade changes. Fix the cause first.
-
Note the revision that is deployed now, from
helm history arbium -n arbium. -
Run the upgrade as above.
-
Run the check again. If it fails, run it once more. If it fails twice in a row with exit 3, 4, 5, 6 or 7 (a real failure; exit 2 and 8 are not a broken release, see the exit codes in Read the result; on the Argo path, a Failed run whose result line lists a failed part that is not
"judged":false), roll back to the revision you noted and run the check a third time to confirm that the previous release serves again:Terminal window helm rollback arbium <revision> -n arbium --wait --timeout 20m
If you run the check as a Helm test, helm test runs the test of the revision
that is deployed, so the run before the upgrade uses the old chart’s check.
Renew the connector token first if it has less than 14 days left.
A rollback puts back the previous chart’s workloads and settings. It does not reverse changes the new release made to the database. The first upgrade that turns the check on has no “before” run, because the check arrives with that upgrade.
Uninstall
Section titled “Uninstall”Uninstall the application before destroying the provider foundation:
helm uninstall arbium -n arbiumkubectl delete namespace arbiumContinue to 4. Verify deployment.