Skip to content

Search these docs from your AI tool

  • Antigravity
    {
      "mcpServers": {
        "edisyl-docs": {
          "serverUrl": "https://docs.edisyl.com/mcp"
        }
      }
    }
  • Claude CodeCLI
    claude mcp add edisyl-docs https://docs.edisyl.com/mcp --transport http --scope user
  • CodexCLI
    codex mcp add --url https://docs.edisyl.com/mcp edisyl-docs
  • Cursor
    {
      "mcpServers": {
        "edisyl-docs": {
          "type": "http",
          "url": "https://docs.edisyl.com/mcp"
        }
      }
    }
  • Gemini CLI
    gemini mcp add --transport http edisyl-docs https://docs.edisyl.com/mcp
  • Goose
    {
      "extensions": {
        "edisyl-docs": {
          "enabled": true,
          "name": "edisyl-docs",
          "type": "streamable_http",
          "uri": "https://docs.edisyl.com/mcp",
          "envs": {},
          "env_keys": [],
          "headers": {},
          "description": "",
          "timeout": 300,
          "bundled": null,
          "available_tools": []
        }
      }
    }
  • JetBrains AI Assistant
    {
      "mcpServers": {
        "edisyl-docs": {
          "type": "http",
          "url": "https://docs.edisyl.com/mcp"
        }
      }
    }

    Paste into Settings → Tools → AI Assistant → Model Context Protocol → Add → As JSON. Direct file writing isn’t supported — JetBrains stores this per-version as XML.

  • Junie (JetBrains)
    {
      "mcpServers": {
        "edisyl-docs": {
          "url": "https://docs.edisyl.com/mcp"
        }
      }
    }
  • OpenCode
    {
      "mcp": {
        "edisyl-docs": {
          "type": "remote",
          "url": "https://docs.edisyl.com/mcp"
        }
      }
    }
  • VS CodeCLI
    code --add-mcp '{"name":"edisyl-docs","type":"http","url":"https://docs.edisyl.com/mcp"}'
  • Windsurf
    {
      "mcpServers": {
        "edisyl-docs": {
          "serverUrl": "https://docs.edisyl.com/mcp"
        }
      }
    }

    Supports both stdio and native HTTP connections.

Values reference

Edisyl umbrella chart — defaults. This file is the schema + sane defaults; an operator's own values file (see examples/) overrides per environment. Structure: images — shared registry + global tag (per-app override allowed) secrets — runtime backend: k8s-native (default) | external-secrets edisyl — the 5 first-party services (templates/apps/<name>.yaml, sharing the "edisyl.app" helper) config — non-secret env (edisyl-config) cnpg — CloudNativePG Cluster CR inngest/minio/redis — optional infra (subchart deps + our glue) Namespace: every object this chart renders (first-party or subchart) lands in `.Release.Namespace` — whatever `helm install -n <ns>` targets. There is no separate namespaces.app/namespaces.create knob; create the namespace yourself, or pass `--create-namespace` to `helm install` (see the README's Installing section). `edisyl.ns` in _helpers.tpl is the single accessor.

Generated from the chart's own values.yaml at chart 0.36.0 — 301 documented paths out of 787 total. Per-app duplicates and upstream subchart internals are folded; see each section’s note. The same file ships inside the chart you pull, sohelm show values gives you the raw form.

Images

imagesobject{5 keys}

No description in values.yaml.

images.registrystring944311261080.dkr.ecr.us-east-1.amazonaws.com/edisyl-prd

The hub ECR registry (account 944311261080), `edisyl-prd` namespace. This is the registry a self-hosted client is actually granted — per-client IAM roles give read-only pull on `edisyl-prd/mono-*` and on the chart itself (terraform-client-access). It replaces the private Depot registry, which was the authoritative internal one and which no customer could pull from. The registry and the tag MOVE TOGETHER. These tags exist under this namespace and nowhere else, so overriding one without the other is an ImagePullBackOff rather than a clean error. ECR never allows anonymous pull and its tokens expire after 12 hours, so images.pullSecret is required in practice here — see ecrRefresh below, which keeps that secret alive.

images.tagstringprd-b857899

FALLBACK ONLY, and nothing uses it today: every first-party app states its own edisyl.<app>.image.tag below, and a per-app tag wins over this one. It stays as the answer for an app added later that forgets to — and tests/render.sh fails if any app relies on it, so that is a loud state rather than a silent inheritance of whatever number happens to sit here. PINNED, not rolling. `dev` used to be the default, which meant a customer install silently tracked whatever had last been built — and no `dev` tag exists under edisyl-prd anyway, so it was an ImagePullBackOff rather than a clean error. The IDP reads none of this: it sets env-keyed full references (edisyl.<app>.image.{dev,stg,prd}) which win outright over the composed default — see edisyl.image. These are the CUSTOMER defaults only.

images.pullPolicystringIfNotPresent

No description in values.yaml.

images.pullSecretstring""

optional regcred name

images.pullSecretAuthobject{4 keys}

Create that secret FROM VALUES instead of by hand (default off). With this off, the imagePullSecret is a manual kubectl step that must happen before the first install — the pre-install migrate hook pulls a first-party image. With it on, the password lives in your own gitignored values file (the same secrets.local.yaml pattern the rest of this chart's credentials use) and `helm install -f` seeds the secret for you. The IDP does not need this: the platform supplies pull credentials to its spokes outside the chart. Left off, nothing renders. For ECR, this is a SEED, not a steady state: the password is an authorization token that expires in 12 hours, so the chart refuses an ECR registry here unless ecrRefresh is also on, and hands the secret over to the refresher on install rather than re-pasting a dead token on every upgrade.

images.pullSecretAuth.enabledbooleanfalse

No description in values.yaml.

images.pullSecretAuth.registrystring""

defaults to images.registry

images.pullSecretAuth.usernamestring""

AWS for ECR

images.pullSecretAuth.passwordstring""

keep in a gitignored values file, never a committed one

ECR pull-secret refresher (OFF by default)

ecrRefreshobject{11 keys}

ECR authorization tokens expire after 12 HOURS, so an imagePullSecret holding one stops working overnight. The failure is delayed, not immediate: running pods keep running, and the first sign is an ImagePullBackOff on the next restart, rollout or scale-up — hours after the cause. Enable this only when pulling first-party images from ECR with a secret. A registry with static credentials, or one the nodes already reach through an instance role, needs none of it. THIS DOES NOT CREATE THE SECRET FOR THE FIRST TIME. The initial imagePullSecret is a prerequisite of installing at all — the pre-install migrate hook pulls a first-party image before anything here could run. The README carries the one-off command, and the AWS-side binding it needs. Consequently the CronJob's RBAC is get/patch/update on that ONE secret name, with no create verb at all: it never legitimately creates a Secret, and the namespace it runs in also holds the database credentials and the JWT signing keys.

ecrRefresh.enabledbooleanfalse

No description in values.yaml.

ecrRefresh.secretNamestring""

The secret to keep alive. Defaults to images.pullSecret, which is the one every app actually mounts; set this only to maintain a differently-named secret.

ecrRefresh.registrystring""

The registry the token authenticates against. Defaults to images.registry, and is refused at render if that is not an ECR host — an ECR token authenticates against nothing else. Set explicitly for a private mirror or an interface endpoint.

ecrRefresh.regionstring""

REQUIRED. The registry's own region: an authorization token is regional.

ecrRefresh.roleArnstring""

Cross-account pull, as Edisyl-hosted customers use: the role in the registry owner's account that this assumes. Leave empty when the pod's own identity already holds ECR read in the same account. The pod's identity comes from IRSA (serviceAccountAnnotations below) or EKS Pod Identity (an association created outside Helm). With Pod Identity, make that association with --disable-session-tags: Pod Identity attaches TRANSITIVE session tags, and a transitive tag carried into this second AssumeRole requires the destination trust policy to allow sts:TagSession. A client role that allows only sts:AssumeRole fails with an AccessDenied naming neither tags nor the reason.

ecrRefresh.externalIdstring""

A confused-deputy guard on that role, not a secret — it is useless unless the role's own trust policy also requires it.

ecrRefresh.schedulestring0 */4 * * *

Every FOUR hours against a twelve-hour token: a missed run still leaves four hours of margin. At six, a single missed run lands the next attempt exactly on the expiry.

ecrRefresh.serviceAccountstringecr-refresh

No description in values.yaml.

ecrRefresh.serviceAccountAnnotationsobject{}

EKS IRSA names the role here (eks.amazonaws.com/role-arn); Pod Identity leaves it empty.

ecrRefresh.imagestringalpine/k8s:1.31.1

Must be pullable WITHOUT the secret this maintains, or an expired secret leaves you unable to run the thing that fixes it.

ecrRefresh.resourcesobject{2 keys}

No description in values.yaml.

ecrRefresh.resources.requestsobject{"cpu":"50m","memory":"128Mi"}

No description in values.yaml.

ecrRefresh.resources.requests.cpustring50m

No description in values.yaml.

ecrRefresh.resources.requests.memorystring128Mi

No description in values.yaml.

ecrRefresh.resources.limitsobject{"memory":"256Mi"}

No description in values.yaml.

ecrRefresh.resources.limits.memorystring256Mi

No description in values.yaml.

Labels

envstring""

platform.edisyl.com/env label (local|qa|...)

commonLabelsobject{}

Applied to every resource this chart renders, including pod templates. NOT applied to subchart resources (Inngest, MinIO): those take inngest.commonLabels and minio.additionalLabels. The chart's own label keys (app, app.kubernetes.io/*, helm.sh/chart, platform.edisyl.com/env, edisyl.com/team) cannot be overridden and the render fails if one is set here. Selectors never include these, so adding or changing them later is a normal rolling update.

Platform injection (IDP)

platformobject{"env":"","domain":"","tls":"","team":""}

Set by the IDP's ApplicationSet as --set parameters — which beat every values file, so a deployment can never self-assert its environment (the platform golden-chart tenancy pattern). ALL EMPTY BY DEFAULT: self-host renders are byte-identical with this block untouched. env — dev|stg|prd. Becomes THE environment truth (image env-key selection, APP_ENV derivation, store config, labels); the top-level `env` above remains a self-host label fallback. domain — when set, the env-variant config DERIVES from it and WINS over config.* values: APP_ENV (dev→development, stg→staging, prd→production), API_PUBLIC_URL/WEB_PUBLIC_URL/WS URLs (<scheme>://<effective-api|edisyl-label>.<domain>), and every enabled app ingress host (<effective-app-label>.<domain>). platform facts — deriving them removes an entire class of per-env values files and misconfigurations. A self-hoster may also set these two values and get the same derivation. edisyl.<app>.subdomain — optional BARE DNS LABEL composed with the injected domain; defaults to the app key. It is never a full hostname. Production drops the environment from DNS, so the same label works unchanged in dev, staging, and production. tls — "true" → https scheme AND ingress TLS blocks WITHOUT a secretName (the controller-wildcard form, e.g. NGINX Inc wildcardTLS); "" / "false" → http and no TLS block unless the app sets ingress.tlsSecret (the cert-manager per-host form).

platform.envstring""

No description in values.yaml.

platform.domainstring""

No description in values.yaml.

platform.tlsstring""

No description in values.yaml.

platform.teamstring""

tenancy label stamped on every resource when injected

Secrets backend

secretsobject{4 keys}

No description in values.yaml.

secrets.providerstringk8s-native

k8s-native — plain Secrets populated from the values below. The default, because we cannot know what secret manager a self-hosting customer runs. external-secrets — ExternalSecret CRs from a store the operator names.

secrets.externalSecretsobject{5 keys}

No description in values.yaml.

secrets.externalSecrets.storeNamestring""

Name an EXISTING store — or leave empty and set createStore.project to have the chart render one (IDP posture: the platform secret-manager project + the platform-injected env config + the per-env reader token; see templates/secretstore.yaml). storeName then defaults to <namespace>-doppler.

secrets.externalSecrets.createStoreobject{"project":""}

No description in values.yaml.

secrets.externalSecrets.createStore.projectstring""

No description in values.yaml.

secrets.externalSecrets.storeKindstringClusterSecretStore

No description in values.yaml.

secrets.externalSecrets.refreshIntervalstring1m

No description in values.yaml.

secrets.externalSecrets.syncWavestring-3

No description in values.yaml.

secrets.managedobject{35 keys}

The logical secrets the apps consume, grouped per integration so each app receives only the families it uses. `keys` share the local/remote name; `remoteKeys` maps local → remote; `templateData` adds literal keys.

secrets.managed.edisyl-databaseobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-database.keysarray["DATABASE_URL","DATABASE_POOL_SIZE"]

DATABASE_POOL_SIZE is connections per pod, so a tier's real ceiling is this times its replica count and has to leave room for every other client on the instance. It rides the secret path rather than the ConfigMap because it differs per tier and the platform store is already per-environment; the suite's app env is a YAML list Helm replaces rather than merges, so a per-env override there would drop its neighbours. api requires it and carries no fallback — a default there would let a broken store-to-chart path run the pool at some other number silently. So this default is the one a static install gets, and it must not be blank or omitted.

secrets.managed.edisyl-redisobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-redis.keysarray[6 items]

mono requires the DISCRETE fields, not just the URL — verified against the deployed image's own schema: REDIS_HOST/USER/PASSWORD are z.string() and REDIS_PORT is z.coerce.number(), all required; REDIS_TLS is optional. aws/ecs.tf routes all six for the same reason. Supplying only REDIS_URL crash-loops api on env validation before it ever opens a connection. Blank values are filled from the bundled Redis by templates/secrets.yaml.

secrets.managed.edisyl-redis.data.REDIS_PASSWORDstring""

Deliberately not listed in `data`: filled from the single generated $redisPw so it cannot diverge from what Redis actually enforces.

secrets.managed.edisyl-redis-latticeobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-redis-lattice.keysarray["LATTICE_REDIS_URL"]

lattice reads Redis under a namespaced var (aws/ecs.tf remaps it too).

secrets.managed.edisyl-redis-authobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-redis-inngestobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-redis-inngest.keysarray["INNGEST_REDIS_URI"]

Inngest's queue/live-run backing Redis. A DEDICATED secret, not a key bolted onto edisyl-redis: edisyl-redis is already in api's envFrom, so adding a key there would leak an unused INNGEST_REDIS_URI into api's env too. Consumed only via secretKeyRef (inngest.inngest.extraEnv), never envFrom — same treatment as edisyl-redis-auth's REDIS_PASSWORD.

secrets.managed.edisyl-auth-clientobject{3 keys}

No description in values.yaml.

secrets.managed.edisyl-auth-client.keysarray[]

Where the fleet's auth service is, and the keys it verifies against — the CONSUMER side. edisyl-auth is the other end: what the auth service signs WITH, mounted on that pod alone. No URL here. Where the auth service is, is derived into edisyl-config from the host this chart publishes for it, so the address and the service cannot disagree — and it is a public hostname with no business in a credential bundle in the first place. remoteKeys, so the store holds both auth services' keys under names that say which is which — one store, two sets, and the prefix is what keeps a hosted key from being handed out as a self-hosted one.

secrets.managed.edisyl-auth-service-keyobject{3 keys}

No description in values.yaml.

secrets.managed.edisyl-auth-service-key.keysarray[]

A service_role token, and service_role is one of the roles the auth service accepts on its admin API — so this reaches the same surface AUTH_ADMIN_TOKEN does. Only edisyl reads it, for the dev-gated signup path; the api never does. Kept out of edisyl-auth-client for that reason: that one mounts on both apps, and one of them is a browser-facing server with no call for an admin credential.

secrets.managed.edisyl-auth-adminobject{3 keys}

No description in values.yaml.

secrets.managed.edisyl-auth-admin.keysarray[]

The api's credential for the auth service's ADMIN API. Its own secret because it mounts on the api alone — edisyl reaches the same auth service but never its admin surface, and a browser-facing server has no business holding a credential that can register an identity provider. Where the api dials is NOT here: AUTH_INTERNAL_URL is derived into edisyl-config from the auth Service this chart renders, so it cannot disagree with the service the credential is for — a disagreement that 401s every admin call with no other symptom.

secrets.managed.edisyl-inngestobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-authobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-auth.keysarray[5 items]

The Google client is here rather than in env because the secret is a credential. Its redirect URI is platform-derived, not configured — it must be https://<auth host>/auth/v1/callback and registered on that client in Google Cloud console, which matches redirect URIs exactly.

secrets.managed.edisyl-auth.data.GOTRUE_SAML_PRIVATE_KEYstring""

DER, base64. Signs the AuthnRequests an identity provider verifies. Independent of the JWT signing key, so rotating it ends no session.

secrets.managed.edisyl-encryptionobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-mono-api-keyobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-token-hmacobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-provider-webhookobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-llm-gatewayobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-llm-gateway.keysarray["LLM_GATEWAY_API_KEY","LLM_GATEWAY_URL"]

The LLM gateway api and worker reach for inference. The key and the URL come from one secret so they cannot be renamed apart: an install pointed at an in-cluster gateway that picked up only the key would fall back to the hosted api.portkey.ai default for the URL and egress its prompts there. LLM_GATEWAY_URL carries a Portkey address, and every variant of the app's gateway config reads it — including the Bedrock one, which would take it as an endpoint override and send signed Bedrock requests to Portkey. That is unreachable today because this chart offers no way to select a gateway other than Portkey. Whatever adds LLM_GATEWAY_PROVIDER here has to make this URL conditional on it in the same change.

secrets.managed.edisyl-bifrostobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-bifrost.keysarray[3 items]

The bundled gateway's MANAGEMENT credential — the one that can read and write provider configuration, and therefore reach every upstream vendor key the deployment holds. Mounted in exactly two places: the gateway itself, which enforces it, and the api, which is the only thing that administers it. Never the worker and never edisyl — a browser-facing server has no business holding a credential that can enumerate provider secrets, the same rule edisyl-auth-admin follows. Deliberately NOT the same credential as LLM_GATEWAY_API_KEY above. That one is a virtual key scoped to inference and cannot read provider configuration; keeping them apart is what stops a leaked runtime key from becoming a vendor-credential leak. Renders only where bifrost.enabled is true (templates/secrets.yaml): under external-secrets an unreferenced ExternalSecret demands keys the store has no reason to carry and never syncs. BIFROST_RUNTIME_KEY is the third key and is NOT a management credential: it is the virtual key templates/bifrost.yaml seeds, scoped to inference. The applications infer with it, and the administration surface presents the same key to prove a provider it just saved actually answers, since a test is an inference request with the credential under test. Generated with the `sk-bf-` prefix Bifrost needs to read a virtual key out of an `Authorization: Bearer` header — without it the key only travels in Bifrost's own x-bf-vk header, which an OpenAI-shaped client never sends.

secrets.managed.edisyl-bifrost-encryptionobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-bifrost-encryption.keysarray["BIFROST_ENCRYPTION_KEY"]

Argon2id-derived AES key for the provider credentials Bifrost stores in its Postgres. Its OWN secret, mounted on the gateway alone: the api administers providers over the management API and has no reason to hold the key their ciphertext is under. ROTATING THIS ORPHANS EVERY STORED CREDENTIAL — Bifrost cannot re-encrypt them, so the providers have to be entered again. That is why it is generated once and read back (edisyl.generatedSecret), and why an Argo CD install must supply it explicitly rather than let the chart generate it: Argo renders with `helm template`, where the read-back finds nothing and a fresh key appears on every sync.

secrets.managed.edisyl-voyageobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-slackobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-slack.keysarray["SLACK_BUG_REPORTS_BOT_TOKEN"]

api only: POST /v1/bug-reports relays an internal bug report into the engineering Slack channel with this bot token (chat:write, files:write, users:read.email). Absent or blank, that one route answers 503 and nothing else in the image cares — mono declares it optional, so the api still boots.

secrets.managed.edisyl-perplexityobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-perplexity.keysarray["PERPLEXITY_API_KEY"]

api only: the search_web tool calls Perplexity's Agent API directly with this key. Absent or blank, the api does not register search_web and nothing else in the image cares — mono declares it optional, so the api still boots.

secrets.managed.edisyl-githubobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-github.keysarray["GITHUB_APP_ID","GITHUB_APP_PRIVATE_KEY"]

api only: the GitHub App identity the api signs installation tokens with. Both keys or neither — the api builds its GitHub credentials from the pair and holds none when either is blank, so every GitHub path then answers github_not_configured: creating or testing a github connection, and every read a pack makes against GitHub. mono declares them optional, so the api still boots without them. An installation is bound on the create path, which asks the App which installations it holds. A deployment without these credentials cannot bind one at all, so a pack bundle naming a github connection has no way to be completed there.

secrets.managed.edisyl-latticeobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-object-storageobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-object-storage.keysarray[8 items]

Bundled MinIO's edisyl-files bucket (see minio.buckets below). PROVIDER is `s3compatible`, NOT `s3`. mono's enum is ["r2", "s3", "s3compatible"], and `s3` means real AWS — with MinIO's credentials and no endpoint that is exactly the silent misconfiguration this used to ship: storage that looks wired and writes nowhere. ENDPOINT / ACCESS_KEY_ID / SECRET_ACCESS_KEY / FORCE_PATH_STYLE are left blank and filled by templates/secrets.yaml from the bundled MinIO, reusing the SAME generated root credentials MinIO itself enforces. Path-style addressing is required: MinIO does not do virtual-host buckets. For AWS S3 instead: set PROVIDER to `s3`, clear ENDPOINT and FORCE_PATH_STYLE, set REGION and BUCKET, and either supply ACCESS_KEY_ID/SECRET_ACCESS_KEY or use a pod IAM role.

secrets.managed.edisyl-query-results-s3object{2 keys}

No description in values.yaml.

secrets.managed.edisyl-query-results-s3.keysarray["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY"]

AWS_ACCESS_KEY_ID/SECRET are left blank here on purpose: templates/ secrets.yaml fills them in with the SAME generated rootUser/ rootPassword as edisyl-minio-root (below). An operator pointing this at an external S3-compatible store instead sets these two explicitly. These two are the ONLY keys here. mono#1462 removed QUERY_RESULTS_S3_BUCKET/QUERY_RESULTS_S3_REGION from api's env schema: query results now live under a `query-results/` prefix in the tier's own OBJECT_STORAGE_BUCKET, so there is no second bucket to name. The pair below stays because the AWS SDK reads it out of the process environment as one step of the ambient credential chain, which is how S3ResultStorage and the shared object-storage provider now both resolve. STILL BROKEN AGAINST BUNDLED MinIO, and still not fixable in this chart. mono's S3ResultStorage (apps/api/src/services/queries/s3-result-storage.ts) constructs `new S3Client({ region })` with no endpoint and no forcePathStyle, so these credentials are presented to REAL AWS and every query-result write fails on InvalidAccessKeyId. Env vars cannot rescue it: AWS_ENDPOINT_URL_S3 would reach the host but leaves virtual-host addressing, and `<bucket>.edisyl-minio.<ns>.svc` does not resolve. The fix is in mono (endpoint + forcePathStyle on that client).

secrets.managed.edisyl-cloudflareobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-cloudflare.keysarray[6 items]

CLOUDFLARE_R2_*: required by the apps only when OBJECT_STORAGE_PROVIDER is r2, which reads NONE of the OBJECT_STORAGE_* credential keys — R2 derives its endpoint from the account id and takes its own pair. They were absent entirely, which made provider r2 unreachable from this chart however the values were set.

secrets.managed.edisyl-sentryobject{3 keys}

No description in values.yaml.

secrets.managed.edisyl-sentry.omitWhenBlankarray[3 items]

These are z.url().optional() in mono, so ABSENT passes and "" fails ("Invalid URL") — an empty optional URL crash-loops api. Omit the key entirely when no DSN is supplied rather than rendering it blank. Note this is the exact INVERSE of the OTEL_* keys in edisyl-config, which must be present-and-empty because @mono/telemetry gates on them being set. Empty and absent are different in both directions; neither is a safe default.

secrets.managed.edisyl-resendobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-sesobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-ses.keysarray["SES_FROM_EMAIL"]

SES_FROM_EMAIL is z.string() and REQUIRED in the deployed image's schema — no email-format validation, so any non-empty string satisfies it. It is absent from aws/secrets.tf's /app keyset too, so the ECS deployment may share this gap. The default is a deliberately unroutable placeholder (.invalid is an IANA-reserved TLD that can never resolve), because outbound mail is NOT configured by this chart. Set a real sender before enabling any flow that emails users — password reset, magic links, invites. SES_REGION defaults to us-east-1 in mono and SES_CONFIGURATION_SET is optional, so neither is required here.

secrets.managed.edisyl-integrations-socialobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-rudderstackobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-snowflakeobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-hubspotobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-realtimeobject{2 keys}

No description in values.yaml.

secrets.managed.edisyl-minio-rootobject{"keys":["rootUser","rootPassword"],"data":{}}

Bundled MinIO's root credentials. Generated (never a literal default), and reused verbatim — never regenerated independently — as edisyl-query-results-s3's AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY below, so the app can actually authenticate against the bundled bucket with MinIO's own root user.

secrets.literalobject{}

Plain Secrets rendered as-is regardless of provider. Shape: <name>: { data: {k: v} }

Probe defaults (overridable per app)

probeDefaultsobject{2 keys}

Promoted from the template so envs/services can tune without editing helpers. A service overrides by setting the matching keys under its probe:. No initContainer mechanism here (Task 13 removed it along with api's own prisma-migrate initContainer): migrations run in exactly one place, the edisyl-migrate pre-install/pre-upgrade bootstrap hook. Re-adding a per-app initContainer would resurrect the exact race this task fixed.

probeDefaults.readinessobject{"initialDelaySeconds":5,"periodSeconds":10}

No description in values.yaml.

probeDefaults.readiness.initialDelaySecondsnumber5

No description in values.yaml.

probeDefaults.readiness.periodSecondsnumber10

No description in values.yaml.

probeDefaults.livenessobject{"initialDelaySeconds":30,"periodSeconds":30}

No description in values.yaml.

probeDefaults.liveness.initialDelaySecondsnumber30

No description in values.yaml.

probeDefaults.liveness.periodSecondsnumber30

No description in values.yaml.

First-party apps (edisyl.<service>)

All 5 first-party apps (api, auth, edisyl, worker, lattice) share the schema below, shown once as `edisyl.<app>`. Set a key on one app to override it for that app only.

edisylobject{6 keys}

Each entry → Deployment (+ Service/Ingress/PDB/SA as configured), rendered by the shared "edisyl.app" helper via templates/apps/<name>.yaml. A `tmp` emptyDir is always mounted (restricted PSS); extraVolumes/Mounts add more. Per-service probe overrides: probe.readiness.* / probe.liveness.* / timeoutSeconds. SCHEDULING AND SCALING (0.9.0) — two per-service knobs, both optional: spreadPods (default TRUE, no key needed) Renders a soft topologySpreadConstraint over kubernetes.io/hostname so a service's replicas prefer different nodes. On before you ask for it, because the alternative was the observed default: every replica of every user-facing service on ONE node, where `replicas: 2` bought nothing against node loss. ScheduleAnyway, so it can never make a pod unschedulable. Set false to opt out, or set topologySpreadConstraints to replace it wholesale. autoscaling (default OFF) A CPU-target HPA. When enabled the Deployment omits `replicas` so the HPA owns pod count — see the helper for why that matters under Argo CD. maxReplicas is REQUIRED when enabled: an unbounded HPA can exhaust the cluster. Read the sizing trap in the helper before picking a target — utilization is measured against requests.cpu, so a single-threaded service with requests.cpu "2" tops out near 50% and a 70% target never fires. autoscaling: enabled: true minReplicas: 2 maxReplicas: 6 targetCPUUtilizationPercentage: 60 rollout (default OFF; 0.19.0, PLT-59) Renders the service as an Argo Rollout instead of a Deployment. The pod template, selector, labels, PDB and sync-wave are untouched; only the kind, the strategy and — when autoscaling is on — the HPA's target move, and they move together (an HPA still aimed at the Deployment would fight the Rollout controller over a workload it had scaled to zero). strategy is REQUIRED when enabled and is passed through verbatim. It is a Rollout strategy (canary or blueGreen), so it never collides with the Deployment-only `strategy:` key, which is ignored on this path. The chart ships no default steps: weights, pauses, traffic routing and analysis are an environment's decision, not a chart's. Needs the Argo Rollouts CRDs and controller in the cluster. Off, nothing Rollout-shaped renders and no CRD is required — which is why a self-host install never sees this. progressDeadlineAbort (default TRUE; 0.20.0) is what turns a failed update into a rollback: when the canary has not progressed within progressDeadlineSeconds (default 600), the controller aborts and returns traffic to the stable ReplicaSet on its own. Set false and a bad image leaves the Rollout Degraded with stable still serving and a human expected to press abort — safe, but it is exactly the manual-cleanup posture this feature replaces. rollout: enabled: true progressDeadlineSeconds: 600 # optional progressDeadlineAbort: true # optional; the default strategy: canary: steps: - setWeight: 20 - pause: { duration: 5m } Needs a metrics.k8s.io source (metrics-server) in the cluster; without one the HPA reports <unknown> and never scales.

edisyl.authSourcestringhosted

Exactly one set of secrets renders and mounts, so moving an environment and moving it back are the same one-line change, and there is no state where a URL from one auth service sits beside keys signed by another.

edisyl.apiobject{16 keys}

No description in values.yaml.

edisyl.<app>.enabledbooleanvaries by app

No description in values.yaml.

edisyl.<app>.replicasnumbervaries by app

No description in values.yaml.

edisyl.<app>.imageobjectvaries by app

No description in values.yaml.

edisyl.<app>.image.namestringvaries by app

No description in values.yaml.

edisyl.<app>.image.tagstringvaries by app

No description in values.yaml.

edisyl.<app>.portnumbervaries by app

No command override → use the image's own ENTRYPOINT/CMD. Current images are node-based (`node --import ./dist/instrumentation.js dist/index.js`); don't hardcode a runtime (`bun`) here — it drifts when the image changes.

edisyl.<app>.serviceAccountstringvaries by app

No description in values.yaml.

edisyl.<app>.needsDatabasebooleantrue

Matches aws/ecs.tf's infra_routed_secrets: only api and worker get a live DB credential. edisyl/lattice are internet-facing and must not carry one in their env even inertly — see edisyl.app in _helpers.tpl.

edisyl.<app>.readOnlyRootFilesystembooleantrue

No description in values.yaml.

edisyl.<app>.podAnnotationsobject{"prometheus.io/scrape":"false"}

No description in values.yaml.

edisyl.<app>.podAnnotations.prometheus.io/scrapestringfalse

No description in values.yaml.

edisyl.<app>.probeobjectvaries by app

No description in values.yaml.

edisyl.<app>.probe.kindstringvaries by app

No description in values.yaml.

edisyl.<app>.probe.pathstring/health

No description in values.yaml.

edisyl.<app>.probe.timeoutSecondsnumber3

No description in values.yaml.

edisyl.<app>.resourcesobjectvaries by app

No description in values.yaml.

edisyl.<app>.resources.requestsobjectvaries by app

No description in values.yaml.

edisyl.<app>.resources.requests.cpustringvaries by app

No description in values.yaml.

edisyl.<app>.resources.requests.memorystringvaries by app

No description in values.yaml.

edisyl.<app>.resources.limitsobjectvaries by app

No description in values.yaml.

edisyl.<app>.resources.limits.memorystringvaries by app

No description in values.yaml.

edisyl.<app>.envFromobjectvaries by app

No initContainer: migrations run in exactly one place — the edisyl-migrate pre-install/pre-upgrade bootstrap hook (bootstrap.yaml). An initContainer here would race the hook to migrate the same database.

edisyl.<app>.envFrom.configMapstringedisyl-config

No description in values.yaml.

edisyl.<app>.envFrom.secretsarrayvaries by app

No description in values.yaml.

edisyl.<app>.serviceobjectvaries by app

No description in values.yaml.

edisyl.<app>.service.enabledbooleantrue

No description in values.yaml.

edisyl.<app>.service.portnumbervaries by app

No description in values.yaml.

edisyl.<app>.ingressobjectvaries by app

No description in values.yaml.

edisyl.<app>.ingress.enabledbooleanvaries by app

No description in values.yaml.

edisyl.<app>.ingress.hoststring""

No description in values.yaml.

edisyl.<app>.ingress.tlsSecretstringvaries by app

No description in values.yaml.

edisyl.<app>.ingress.annotationsobjectvaries by app

No description in values.yaml.

edisyl.<app>.ingress.annotations.cert-manager.io/cluster-issuerstringletsencrypt-prod

No description in values.yaml.

edisyl.<app>.ingress.annotations.kubernetes.io/tls-acmestringtrue

No description in values.yaml.

edisyl.<app>.ingress.annotations.nginx.ingress.kubernetes.io/proxy-body-sizestring100m

No description in values.yaml.

edisyl.<app>.ingress.annotations.nginx.ingress.kubernetes.io/proxy-read-timeoutstringvaries by app

No description in values.yaml.

edisyl.<app>.ingress.annotations.nginx.ingress.kubernetes.io/proxy-send-timeoutstring600

No description in values.yaml.

edisyl.<app>.ingress.annotations.nginx.ingress.kubernetes.io/proxy-bufferingstringoff

/mcp always answers over an unbounded chunked text/event-stream with no Content-Length (api's StreamableHTTPTransport never sets enableJsonResponse) — a long-lived response an ingress should forward as it arrives rather than buffer. Both forms ship since a consumer's controller flavor (community ingress-nginx vs. F5's nginx.org) is unknown to this chart; each ignores the other's key.

edisyl.<app>.ingress.annotations.nginx.org/proxy-bufferingstringfalse

No description in values.yaml.

edisyl.<app>.pdbobjectvaries by app

No description in values.yaml.

edisyl.<app>.pdb.enabledbooleanvaries by app

No description in values.yaml.

edisyl.<app>.pdb.minAvailablenumber1

No description in values.yaml.

edisyl.<app>.autoscalingobject{4 keys}

OFF by default; the IDP enables it per environment. maxReplicas is required when enabled. Target 60 (not the usual 70) because api's event loop is single-threaded: against requests.cpu "2" the utilization ceiling is ~50-75%, so a higher target would never fire.

edisyl.<app>.autoscaling.enabledbooleanfalse

No description in values.yaml.

edisyl.<app>.autoscaling.minReplicasnumber2

No description in values.yaml.

edisyl.<app>.autoscaling.maxReplicasnumber6

No description in values.yaml.

edisyl.<app>.autoscaling.targetCPUUtilizationPercentagenumber60

No description in values.yaml.

edisyl.<app>.rolloutobject{"enabled":false}

OFF by default (0.19.0, PLT-59); the IDP enables it per environment, with that environment's strategy. Spelled out on this service in particular because it is the one that autoscales, and keeping the HPA's target in step with the workload kind is the whole point of the gate — see the rollout note in the schema comment above.

edisyl.<app>.rollout.enabledbooleanfalse

No description in values.yaml.

edisyl.authobject{16 keys}

The authentication service (GoTrue). OFF by default: an operator opts in, and the IDP contract turns it on per environment. Standing it up changes nothing on its own — AUTH_PUBLIC_URL still points wherever it pointed, so nothing authenticates against this until that moves.

edisyl.auth.extraRedirectUrlsarray[]

Redirect targets for apps served from this release's namespace that this chart does NOT deploy. Appended verbatim to GOTRUE_URI_ALLOW_LIST, which is chart-owned and therefore unreachable through edisyl.auth.env — this list is the only way in. Entries must be absolute and end in `**` (GoTrue globs the whole redirect URL, query string included); the template refuses anything else rather than let a sign-in fail silently. extraRedirectUrls: - https://admin.example.com/auth/callback**

edisyl.auth.samlobject{"enabled":true}

Its own instance, since GoTrue owns the whole database and migrates it on boot. Named once here and read by both the Deployment and the bootstrap Job: two settings could point them at different databases, which is the boots-and-serves-errors state auth-bootstrap.yaml documents. The secret carries a complete, correctly-encoded URI. It is injected by the template rather than listed in `env` below, because Helm replaces that list wholesale — an environment overriding one variable would otherwise drop the database connection entirely. WHERE THE SECRET COMES FROM is `create` (below the two names). Unset — the default — it follows cnpg.enabled: with the bundled cluster running, this chart provisions everything itself (templates/auth-db.yaml: a mono_auth login role via a CNPG managed role, a declarative Database it owns, and this connection Secret); without one, the Secret must arrive from outside — the IDP posture, a Postgres claim's connection secret. Set `create: false` to bring your own database even alongside bundled CNPG; `create: true` without CNPG refuses to render (platform-guards). Which sign-in mechanisms this environment offers. Both are reachable from values on purpose: an environment being migrated onto federation needs the path it is leaving available to fall back to, and no break-glass exists yet — turning SAML on and the social provider off in one step would make a misconfigured provider a lockout rather than a retry.

edisyl.auth.saml.enabledbooleantrue

No description in values.yaml.

edisyl.auth.googleobject{"enabled":false}

No description in values.yaml.

edisyl.auth.google.enabledbooleanfalse

No description in values.yaml.

edisyl.auth.databaseobject{"secretName":"mono-auth-db","secretKey":"uri"}

No description in values.yaml.

edisyl.auth.database.secretNamestringmono-auth-db

No description in values.yaml.

edisyl.auth.database.secretKeystringuri

No description in values.yaml.

edisyl.auth.ingress.pathstring/auth/v1/

The client SDK builds every route as new URL("auth/v1", baseUrl) while GoTrue serves at root. The path routes; the annotation strips. Both are required — the path alone would forward /auth/v1/token unchanged and every call would 404. The path ends at a slash, and has to. The controller emits `rewrite ^<path>(.*)$ <rewrite>$1 break;`, so the capture holds everything after the path verbatim and the separator must be contributed exactly once — by the path or by the rewrite target, never both and never neither. `/auth/v1` against `rewrite=/` doubles it into `//token`; `/auth/v1/` against `rewrite=/api` elides it into `/apitoken`. Either way the request reaches the right pod and 404s on every route, so the service, the ingress and the rewrite all read as correct while nothing signs in. Setting this path is the whole declaration: the template derives BOTH controller vendors' rewrites from it — F5's nginx.org/rewrites against this literal prefix, and the community controller's use-regex plus rewrite-target against a second, capture-carrying path. Neither vendor is named here, because a chart cannot tell which one is installed: both are conventionally class "nginx". This used to carry nginx.org/rewrites alone, which made an F5 NGINX controller an unstated requirement of this service. On the community controller that key is silently inert, so the prefix was forwarded intact and every auth route 404d — with the pod Ready, the Service endpointed and the Ingress admitted.

edisyl.auth.envarray[6 items]

No description in values.yaml.

edisyl.edisylobject{12 keys}

No description in values.yaml.

edisyl.workerobject{15 keys}

No description in values.yaml.

edisyl.worker.serviceAccountAnnotationsobject{}

Customer-owned cloud identity binding. EKS IRSA uses eks.amazonaws.com/role-arn here; EKS Pod Identity leaves this empty and creates its association outside Helm.

edisyl.worker.envarray[1 items]

No description in values.yaml.

edisyl.latticeobject{14 keys}

No description in values.yaml.

edisyl.lattice.argsarray[]

distroless Go entrypoint

edisyl.lattice.envarray[4 items]

No description in values.yaml.

Non-secret config (edisyl-config)

configobject{16 keys}

No description in values.yaml.

config.appEnvstringproduction

Must be development | staging | production — the frontend throws otherwise.

config.nodeEnvstringproduction

No description in values.yaml.

config.logLevelstringinfo

No description in values.yaml.

config.webPublicUrlstring""

Derived from config.domain; a default here would be our host, not theirs.

config.domainstring""

Empty on purpose. Every public URL derives from this, so a default would hand an operator who never set one a working-looking render pointed at somebody else's domain. Absent, the required-key failures below name what to set.

config.staffSsoProviderIdstring""

The identity provider whose sessions the api accepts as staff, as `sso:<uuid>`. Empty until a provider is registered against this environment's auth service, which is what mints the uuid — see the STAFF_SSO_PROVIDER_ID comment in templates/configmap.yaml.

config.apiPublicUrlstring""

In-cluster URLs use SHORT service names (no namespace segment): every consumer pod shares the release namespace, so DNS search paths resolve them in any install namespace — no per-namespace overrides to forget. No safe default: see the comment beside API_PUBLIC_URL in templates/configmap.yaml. Set per environment (e.g. "https://api.stg.edisyl.com").

config.portApistring8080

No description in values.yaml.

config.defaultDatasourceIdstring00000000-0000-0000-0000-000000000000

No description in values.yaml.

config.databasePoolSizenumber25

Connections per api pod. api requires it and carries no fallback, so this default is what an install gets when it names nothing; a deployment sizes it against its own instance and replica count.

config.inngestDevstring0

No description in values.yaml.

config.inngestBaseUrlstringhttp://inngest:8288

No description in values.yaml.

config.otelobject{3 keys}

Empty = telemetry off. Set serviceName + endpoint to enable OTLP export.

config.otel.serviceNamestring""

No description in values.yaml.

config.otel.endpointstring""

No description in values.yaml.

config.otel.resourceAttributesstring""

No description in values.yaml.

config.sentryEnabledbooleanfalse

No description in values.yaml.

config.rudderstackEnabledbooleanfalse

No description in values.yaml.

config.usageExportobject{6 keys}

Self-hosted only. Hosted environments leave this disabled, so the worker never assumes a client role.

config.usageExport.enabledbooleanfalse

No description in values.yaml.

config.usageExport.clientKeystring""

No description in values.yaml.

config.usageExport.bucketstring""

No description in values.yaml.

config.usageExport.regionstringus-east-1

No description in values.yaml.

config.usageExport.assumeRoleArnstring""

No description in values.yaml.

config.usageExport.externalIdstring""

No description in values.yaml.

Database source

databaseobject{2 keys}

How api/worker and the bootstrap hook Jobs obtain DATABASE_URL. Deployment postures diverge HERE, in one value, rather than in per-template conditionals: cnpg — bundled CNPG below (self-host default). Requires cnpg.enabled: true; the render fails otherwise. existing-secret — a connection Secret that already exists in the release namespace (the IDP posture: a platform Postgres claim writes it, key `uri` — the same key CNPG uses). The credential never passes through values. managed — DATABASE_URL from the edisyl-database managed Secret (set its value under secrets.managed, or deliver it via secrets.provider: external-secrets). The render fails if it would be blank.

database.sourcestringmanaged

managed, not cnpg. The bundled database is OFF by default (see cnpg.enabled below), so the default posture is a Postgres you already run and name here. managed — DATABASE_URL supplied in secrets.managed.edisyl-database.data, i.e. your own gitignored values file. The self-host default. existing-secret — a connection Secret already in the namespace, e.g. what a platform Postgres claim writes. The IDP posture. cnpg — the bundled cluster, which you must also enable. Requirements on an external Postgres, none of which the chart can check: Postgres 16, the `vector` extension AVAILABLE (mono's first migration runs CREATE EXTENSION vector), and — if you enable the self-hosted auth service — a second database and role for it, since only the bundled cluster can create those itself.

database.existingSecretobject{"name":"","key":"uri"}

No description in values.yaml.

database.existingSecret.namestring""

No description in values.yaml.

database.existingSecret.keystringuri

No description in values.yaml.

CNPG Cluster

cnpgobject{13 keys}

No description in values.yaml.

cnpg.enabledbooleanfalse

OFF by default. A chart that ships a database unless told otherwise hands an operator a production datastore they never chose, on the cluster's default StorageClass, holding the only copy of their data — and the three-instance default below provisions 348Gi to do it. Bringing your own Postgres is the expected posture; the bundled cluster is for a local or evaluation install. Enabling this is not the only step: set database.source: cnpg as well. The two are separate because the cluster existing and the apps being pointed at it are separate decisions, and the chart refuses the combinations that disagree.

cnpg.namestringedisyl-pg

No description in values.yaml.

cnpg.appSecretNamestringedisyl-pg-app

CNPG generates a `<name>-app` Secret (username/password/dbname/host/port/ uri/jdbc-uri) at cluster-bootstrap time. edisyl.app injects DATABASE_URL from its `uri` key — see _helpers.tpl "edisyl.app".

cnpg.instancesnumber3

No description in values.yaml.

cnpg.imageNamestringghcr.io/cloudnative-pg/postgresql:16-standard-bookworm

Must be a CNPG operand image WITH pgvector: mono's first migration runs CREATE EXTENSION vector, and CNPG starts happily on an image that lacks it, so the failure lands minutes later in the migrate hook rather than here. CloudNativePG's own `standard` flavor carries pgvector (plus pgaudit and pg-failover-slots) and is public and multi-arch — nothing to build or push. The `minimal` flavor does NOT; neither does the plain `:16` tag. What this flavor drops is the barman-cloud binaries, which only the legacy in-Cluster `barmanObjectStore` backup path uses — this chart configures none. If you need extensions beyond these, build your own from chart/images/cnpg-pgvector.Dockerfile and point this at it. UPGRADING an install made before chart 0.26.0 (when this defaulted to a self-built bullseye image): read the README's "Upgrading an existing cluster onto this default" — a glibc change can reorder non-C collations.

cnpg.storageClassstring""

Empty = use the cluster's default StorageClass. Never default this to a legacy-cluster-specific class name (e.g. "ceph-block") — a customer cluster doesn't have it, and the PVCs stay Pending forever. Set this explicitly if your cluster's default class isn't what you want CNPG's volumes on.

cnpg.storageSizestring100Gi

No description in values.yaml.

cnpg.walStorageSizestring16Gi

No description in values.yaml.

cnpg.enableSuperuserAccessbooleanfalse

No description in values.yaml.

cnpg.managedRolesarray[]

No description in values.yaml.

cnpg.extraDatabasesarray["auth","inngest"]

No description in values.yaml.

cnpg.extensionsarray["vector"]

Extensions created in the app DB at init (as superuser) — pgvector isn't a "trusted" extension, so the app user can't CREATE it during migrations.

cnpg.parametersobject{3 keys}

No description in values.yaml.

cnpg.parameters.max_connectionsstring200

No description in values.yaml.

cnpg.parameters.shared_buffersstring2GB

No description in values.yaml.

cnpg.parameters.effective_cache_sizestring6GB

No description in values.yaml.

Optional infra (conditional subchart deps + our glue)

These are upstream subcharts. Only the switches and glue this chart owns are documented here — for the rest, see each project’s own values documentation.

bootstrapobject{5 keys}

enabled flags drive Chart.yaml conditions. Database bootstrap, mirroring shared/docker-compose.yml's migrate/seed.

bootstrap.enabledbooleantrue

No description in values.yaml.

bootstrap.seedbooleantrue

db:seed on/off. Self-host default ON (helm post-install = first install only). Argo CD reruns hooks on EVERY sync — no first-install-only phase exists — and db:seed is not rerun-safe by contract, so IDP deployments set this false. Owner seeding (adminEmails) is unaffected: it lives in sync-system-content.

bootstrap.backoffLimitnumber3

No description in values.yaml.

bootstrap.adminEmailsarray[]

Emails made OWNERS of the system org (idempotent; pre-creates the user row so first sign-in links by email). Leave empty and no owner exists — nothing owner-gated can be created. Set at least one for a usable install.

bootstrap.resourcesobject{2 keys}

No description in values.yaml.

bootstrap.resources.requestsobject{"cpu":"200m","memory":"512Mi"}

No description in values.yaml.

bootstrap.resources.requests.cpustring200m

No description in values.yaml.

bootstrap.resources.requests.memorystring512Mi

No description in values.yaml.

bootstrap.resources.limitsobject{"memory":"1Gi"}

No description in values.yaml.

bootstrap.resources.limits.memorystring1Gi

No description in values.yaml.

inngestobject{10 keys}

Self-hosted Inngest. Runs `inngest start` (durable single-node), which signs and validates traffic exactly like Inngest Cloud — hence INNGEST_DEV=0 in the ConfigMap and the shared keys from edisyl-inngest. Chart 0.3.1 is the latest published inngest-helm; only the image is bumped. DURABILITY, stated plainly: - Queue state and in-flight/live run state DO survive an Inngest pod restart: INNGEST_REDIS_URI (below, via extraEnv/secretKeyRef) points it at our bundled edisyl-redis, reusing the SAME generated password as REDIS_URL/LATTICE_REDIS_URL (never a second, divergent one). - Function RUN HISTORY (past events, runs, step results) does NOT survive a restart. INNGEST_POSTGRES_URI is deliberately left unset: this subchart hardcodes its data volume to an emptyDir with no PVC option, so without an external Postgres it falls back to SQLite on that same ephemeral storage — see INNGEST_SQLITE_DIR in extraEnv below, without which that fallback does not merely lose history, it fails to start at all. We do not compose one from CNPG's edisyl-pg-app ourselves — its `uri` key percent-encodes the password, which is direct evidence a CNPG-generated password CAN contain URI-reserved characters (@ : / ? # [ ] %), and a hand-built $(VAR) string substitution has no escaping, so it would corrupt the URI and fail at connect time in a way that looks like a wrong password. The clean fix (a CNPG managed role for `inngest` with its own generated password) needs bootstrap/managed-roles ordering this chart doesn't solve yet: postInitApplicationSQL runs at cluster bootstrap, before managed roles reconcile, so `CREATE DATABASE inngest OWNER inngest` would fail against a role that doesn't exist yet. An operator who needs durable run history must set inngest.inngest.postgres.uri themselves. NOTE: this subchart hardcodes its resource names to `inngest` — there is no nameOverride/fullnameOverride. The Service is therefore `inngest`, which is what config.inngestBaseUrl must point at.

inngest.enabledbooleantrue

No description in values.yaml.

inngest.imageobject{3 keys}

No description in values.yaml.

inngest.namespaceobject{"create":false,"name":"edisyl"}

The subchart's OWN namespace.name (default "inngest") and namespace.create (default true) are independent of this chart's namespace (every first-party object follows .Release.Namespace — see edisyl.ns in _helpers.tpl) — left alone, it creates a SECOND Namespace object and puts every resource there instead of alongside the rest of this release — so `inngest` short-name DNS (config.inngestBaseUrl) would resolve nowhere. `name: edisyl` matches this chart's default install namespace; if you `helm install -n <other-namespace>`, override this ONE value to match (the only namespace-coupled value left — the subchart offers no way around it).

inngest.commonLabelsobject{}

The subchart renders its own resources and never sees the top-level commonLabels. Repeat that map here to label them too — and on the platform this MUST carry edisyl.com/team matching platform.team, or tenant admission rejects the subchart's workloads (templates/platform-guards.yaml).

inngest.serviceobject{"type":"ClusterIP","port":8288,"connPort":8289}

No description in values.yaml.

inngest.ingressobject{"enabled":false}

No description in values.yaml.

inngest.postgresqlobject{"enabled":false}

Disable the subchart's OWN internal Postgres/Redis. These two flags default to TRUE upstream — leaving them alone silently starts a SECOND Postgres and a SECOND Redis alongside CNPG and our bundled Redis (verified: `inngest-postgresql` and `inngest-redis` Services appear). See the DURABILITY note above for what actually backs Inngest instead.

inngest.redisobject{2 keys}

enabled:false here is the chart default; consumers may turn it on (mono does). The sizing below only applies when they do.

inngest.resourcesobject{2 keys}

The upstream chart sets NO memory limit on inngest (limits: {}). Tenant namespaces on the platform reject that (bound-tenant-memory, PLT-182).

inngest.inngestobject{5 keys}

No description in values.yaml.

inngest.inngest.sdkUrlarray["http://worker:6002/inngest"]

Inngest syncs functions FROM the worker and re-syncs every 60s to pick up redeploys. Must be in-cluster DNS, not a public hostname. SINGULAR key.

inngest.inngest.extraEnvarray[4 items]

eventKey/signingKey/redis.uri are plain-string values upstream — a chart-GENERATED value (a password, a URL embedding one) can never go there, since values are static data, not templates. Inject all three from chart-rendered Secrets via extraEnv instead.

minioobject{13 keys}

Bundled object storage. ON BY DEFAULT, and wired end to end: secrets.managed.edisyl-object-storage gets PROVIDER=s3compatible plus this MinIO's in-cluster endpoint, path-style addressing, and the same generated root credentials MinIO itself enforces. The historical gap (final-fix-wave Ruling 21 / C13) was PROVIDER=s3 with no endpoint — real AWS, presented MinIO's credentials: storage that looked configured and wrote nowhere. That is closed; PROVIDER is s3compatible and templates/secrets.yaml fills the endpoint. `existingSecret` points at edisyl-minio-root (in secrets.managed, above) instead of letting the subchart generate its own — so THIS chart owns the value and can mirror it into edisyl-query-results-s3's AWS_ACCESS_KEY_ID/ AWS_SECRET_ACCESS_KEY. Both rootUser/rootPassword are chart-generated (never a literal default) via the same generate-once machinery as ENCRYPTION_KEY etc. — see templates/secrets.yaml's $minioRootUser/$minioRootPassword. For AWS S3 or another S3-compatible store instead, set enabled: false and configure secrets.managed.edisyl-object-storage yourself. That is also the IDP posture (examples/idp.values.yaml): with this false, NOTHING below renders and every OBJECT_STORAGE_*/AWS_* key comes from the platform store.

minio.enabledbooleanfalse

OFF by default. Same reasoning as cnpg above: an object store nobody asked for, holding the only copy of uploaded files. Point the chart at S3, R2 or your own S3-compatible store instead — see secrets.managed.edisyl-object-storage, whose keys the chart now REFUSES to leave half-set when this is off. Enable this for a local or evaluation install. Note before turning it on for anything real: MinIO's server images are AGPL-relicensed and upstream is archived, and this pins subchart 5.4.0.

minio.modestringstandalone

No description in values.yaml.

minio.replicasnumber1

No description in values.yaml.

minio.additionalLabelsobject{}

Same as inngest.commonLabels: the subchart's own labels knobs, because the top-level commonLabels stops at this chart's resources. Two of them, because the MinIO chart reads additionalLabels on its Deployment or StatefulSet metadata only and podLabels on the pod templates — set both to the same map or the pods go unlabelled. Same platform rule about edisyl.com/team, too.

minio.podLabelsobject{}

No description in values.yaml.

minio.existingSecretstringedisyl-minio-root

No description in values.yaml.

minio.fullnameOverridestringedisyl-minio

Pins every subchart resource to "edisyl-minio" regardless of the Helm release name. templates/secrets.yaml composes OBJECT_STORAGE_ENDPOINT as "http://edisyl-minio.<ns>.svc:9000"; WITHOUT this the subchart derives its Service name from the RELEASE name instead ("myrelease-minio"), so that endpoint resolves nowhere and every write fails — silently, since the apps start fine and only fail on first use. ⚠ MIGRATION — READ BEFORE UPGRADING an existing install whose release is NOT named "edisyl". This renames every MinIO resource, the PVC included. The old PVC is named in no manifest after the rename, and the subchart sets no `helm.sh/resource-policy: keep` on it, so HELM DELETES IT on upgrade — and if its StorageClass reclaims with Delete (the common default), the underlying volume and its contents go with it. This is data loss, not an orphaned volume to reclaim later. In most such installs the bucket is empty anyway: OBJECT_STORAGE_ENDPOINT pointed at a name that never resolved, so nothing could write. The exception is real — an operator who noticed and overrode the endpoint to the release-derived name has a working MinIO with live data, and is exactly the person this rename hurts. If that is you: back the buckets up (`mc mirror`), or set `minio.fullnameOverride` to your CURRENT MinIO name and override secrets.managed.edisyl-object-storage.data.OBJECT_STORAGE_ENDPOINT to match, which keeps your volume and still fixes the release-name coupling.

minio.bucketsarray[2 items]

Bucket names here MUST match what the apps ask for — secrets.managed.edisyl-object-storage.data.OBJECT_STORAGE_BUCKET. Nothing at runtime couples them: a rename on either side renders cleanly, starts healthy, and 404s on first write. templates/platform-guards.yaml fails the render instead. `query-results` is no longer asked for by any app — mono#1462 moved query results into OBJECT_STORAGE_BUCKET under a `query-results/` prefix. It stays created because existing installs hold data in it (purge: false) and dropping it from this list would not delete it anyway, only orphan it.

minio.persistenceobject{"enabled":true,"size":"50Gi"}

A real install must not disable this — bucket contents would not survive a pod restart otherwise.

minio.ingressobject{"enabled":false}

No description in values.yaml.

minio.consoleIngressobject{"enabled":false}

No description in values.yaml.

minio.postJobobject{1 keys}

Restricted-PSS hardening for the post-install bucket Job. The subchart ships NO securityContext on it at all, so in a namespace labelled `pod-security.kubernetes.io/enforce: restricted` admission rejects the Job outright — MinIO comes up healthy and the buckets simply never exist, which reads as "MinIO is broken" rather than "the Job was denied". postJob.securityContext is the POD level (the subchart templates it as `omit ... "enabled"`, so arbitrary keys pass straight through); makeBucketJob.containerSecurityContext is the mc container. readOnly root is safe here: mc writes only to /etc/minio/mc and /tmp, both emptyDirs the subchart already mounts. NOTE this covers the JOB only. The MinIO SERVER container still ships `containerSecurityContext.readOnlyRootFilesystem: false` upstream, so the namespace as a whole still cannot enforce `restricted` — see the README's Pod Security Standards section.

minio.makeBucketJobobject{2 keys}

No description in values.yaml.

redisobject{5 keys}

Bundled Redis. One redis:8 StatefulSet — see templates/redis.yaml for why no replicas knob exists. Set enabled: false and supply REDIS_URL to use a managed Redis (the only HA option today).

redis.enabledbooleantrue

No description in values.yaml.

redis.imagestringredis:8

No description in values.yaml.

redis.storageClassstring""

No description in values.yaml.

redis.storageSizestring8Gi

No description in values.yaml.

redis.resourcesobject{2 keys}

No description in values.yaml.

redis.resources.requestsobject{"cpu":"200m","memory":"1Gi"}

No description in values.yaml.

redis.resources.requests.cpustring200m

No description in values.yaml.

redis.resources.requests.memorystring1Gi

No description in values.yaml.

redis.resources.limitsobject{"memory":"2Gi"}

No description in values.yaml.

redis.resources.limits.memorystring2Gi

No description in values.yaml.

Bifrost LLM gateway

bifrostobject{10 keys}

No description in values.yaml.

bifrost.enabledbooleanfalse

OFF by default, and enabling it does NOT move any traffic. Three controls stay separate on purpose: bifrost.enabled runs the gateway edisyl.api's management credential lets the admin page configure it secrets.managed.edisyl-llm-gateway decides where inference actually goes The first two are this block; the third is untouched by it. A deployment can therefore stand the gateway up, configure providers through Edisyl's administration page and prove real inference through it while the application keeps answering on the gateway it already used — which is the only way to verify a gateway before betting production traffic on it.

bifrost.externalobject{"url":""}

A gateway this deployment administers but does not run. Set `url` to its management address and leave `enabled` false: the api gets BIFROST_MANAGEMENT_URL and the edisyl-bifrost admin credential, and no gateway pods, database or encryption key render. The two are exclusive — a deployment either runs its gateway or points at someone else's. With chart-managed Secrets, all three edisyl-bifrost keys must be supplied: they are the external gateway's own admin credential and one of its virtual keys, so generated values would never match it. Inference still goes wherever edisyl-llm-gateway points, as with the bundled gateway.

bifrost.external.urlstring""

No description in values.yaml.

bifrost.imagestringmaximhq/bifrost:v2.2.0@sha256:12d6bbab32b44e3bb88869c6f5ca9304b53a9c43cb55c57a29f851037bcc19e1

DIGEST-pinned, not a bare tag. This process holds every upstream provider credential the deployment has (encrypted at rest with the key below, which it receives in its environment), so the usual "a Docker Hub tag is conventionally immutable" is not good enough — the same standard the auth-prep-db hook's postgres image is held to. OCI index digest, so it stays multi-arch (linux/amd64 and linux/arm64, verified with `crane`). Pinned to v2.2.0 because that is the release whose Voyage embedding wire format was verified live against a real provider. Do not move it to a newer release without re-running that check.

bifrost.serviceobject{"port":8080}

No description in values.yaml.

bifrost.service.portnumber8080

No description in values.yaml.

bifrost.storageClassstring""

A StatefulSet with ONE replica, and no replicas value is exposed — deliberately, and for a different reason than bundled Redis. Bifrost's OSS build does not synchronize live state between processes that share a Postgres configuration store: provider configuration, budgets and rate-limit counters are held per-process and reconciled from the database only at startup. Two replicas are therefore two gateways that disagree about what a virtual key is allowed to spend. Upstream says so explicitly (https://docs.getbifrost.ai/deployment-guides/how-to/multinode). A StatefulSet is also what makes the UPGRADE safe: its rolling update terminates the old pod before creating the new one, where a Deployment's default surge would briefly run two. That brief overlap is exactly the unsupported state, so it must not be reachable by an ordinary `helm upgrade`. The cost is a short window with no gateway, which is correct at Milestone A (nothing routes inference here yet) and is the thing to revisit before it carries production traffic.

bifrost.storageSizestring8Gi

Holds the request/usage log database only — the configuration and the provider credentials live in Postgres. Sized for logs, not for config.

bifrost.resourcesobject{2 keys}

No description in values.yaml.

bifrost.resources.requestsobject{"cpu":"200m","memory":"1Gi"}

No description in values.yaml.

bifrost.resources.requests.cpustring200m

No description in values.yaml.

bifrost.resources.requests.memorystring1Gi

No description in values.yaml.

bifrost.resources.limitsobject{"memory":"2Gi"}

No description in values.yaml.

bifrost.resources.limits.memorystring2Gi

No description in values.yaml.

bifrost.envLabelstring""

Short label shown in Bifrost's own management UI sidebar, so an operator with two of these open can tell them apart. Derived from the environment when the platform injects one; state it here for a self-host install. Bifrost caps it at 10 characters.

bifrost.consoleobject{4 keys}

── The management console ───────────────────────────────────────────────── Bifrost's dashboard and its management API answer on the same port as inference, so anything that publishes this Service publishes provider administration with it. That is why the chart renders no Ingress by default: the api reaches the gateway by cluster DNS and nothing else needs to. An operator who wants the console reachable names a PRIVATE ingress class here. Edisyl uses `tailscale`, which gives the operator's own Ingress controller a tailnet device and serves it at https://<host>.<tailnet>.ts.net with tailnet certificates, so the console is reachable by employees and by nobody on the public internet. Bifrost's own admin credential still applies on top of that. Two gates, and both are deliberate. Network membership is the first, and it is the one that cannot be brute-forced; the shared admin credential is the second. Pointing this at a public ingress class would leave only the second, guarding every vendor credential the deployment holds — so do not. This publishes the WHOLE gateway, inference included. That is harmless on a private network and is not a way to give another environment an inference endpoint: that needs a route that exposes /v1 and nothing else, which is a separate decision.

bifrost.console.enabledbooleanfalse

No description in values.yaml.

bifrost.console.ingressClassNamestring""

No default. A class name is deployment-specific, and guessing one would either render an Ingress no controller claims or, worse, hand it to whichever controller answers for an empty class — which on this fleet is the public one.

bifrost.console.hoststring""

The single DNS label the class turns into a hostname. Under the Tailscale operator it becomes the device name and the MagicDNS name, so it has to be unique in the tailnet — `bifrost-spoke-prd`, not `bifrost`.

bifrost.console.annotationsobject{}

No description in values.yaml.

bifrost.databaseobject{3 keys}

── The gateway's own Postgres ───────────────────────────────────────────── Bifrost owns its schema and migrates it on boot, so it gets its own database and role — never the application's. The chart never puts it in mono's database: a gateway that can read and write application tables is a boundary this exists to draw, and mono's migrations own that schema. cnpg — the bundled cluster provisions a `bifrost` database and role (templates/bifrost-db.yaml). The self-host and evaluation posture. existing-secret — a Secret already in the namespace carries the connection, e.g. what a platform Postgres claim writes. The IDP posture. Discrete keys rather than a URI, unlike database.existingSecret above: Bifrost's configuration takes host/port/user/password/db_name as separate fields and has no DSN input, so a URI would have to be taken apart at runtime by something. The defaults below are the key names a platform Postgres claim's connection Secret already writes.

bifrost.database.sourcestringcnpg

No description in values.yaml.

bifrost.database.existingSecretobject{2 keys}

No description in values.yaml.

bifrost.database.existingSecret.namestring""

No description in values.yaml.

bifrost.database.existingSecret.keysobject{5 keys}

No description in values.yaml.

bifrost.database.sslModestringrequire

`require` for a platform-provisioned Postgres reached over the network; the bundled in-cluster cluster is reached over the pod network and the chart overrides this to `prefer` there. Bifrost requires the field.

Report incorrect code

Please provide a detailed description of the incorrect code.