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.

Configuration

Everything is configured through a Helm values file. Below is a working starting point — copy it, change the marked values, and pass it with -f.

The values reference documents every setting the chart accepts, and the configuration reference lists every value and credential you may have to supply, with who produces each. The configurator writes this file from your answers instead, together with the gitignored credentials file.

edisyl.values.yaml
env: production
images:
# The chart's own defaults already point at the registry your role can pull
# from, with a tag pinned per app. The pull secret is the one thing it
# cannot supply — ECR allows no anonymous pull. Omit it only if you use a
# kubelet credential provider.
pullSecret: edisyl-ecr
config:
appEnv: production
nodeEnv: production
# One line. Every public URL derives from it. Each app is served at
# <app>.<domain>, so this value gives api.example.com, auth.example.com,
# and the web application at edisyl.example.com. Do not include an app
# label here — "edisyl.example.com" would put the web app at
# edisyl.edisyl.example.com.
domain: example.com
# Object storage. The provider is an enum: s3 | s3compatible. `s3` means
# REAL AWS — using it for anything else produces storage that renders
# cleanly, starts healthy and writes nowhere. Use `s3compatible` for MinIO,
# Ceph, Cloudflare R2's S3 endpoint or any other S3 API.
#
# For real AWS S3: set the region, leave objectStorageUrl empty, and prefer
# a pod IAM role over the two keys under secrets.managed below. For anything
# else: objectStorageUrl and both keys are required — there is no
# instance-role fallback outside AWS.
objectStorageProvider: s3
objectStorageBucket: your-bucket
objectStorageRegion: us-east-1
objectStorageUrl: ""
# The sender address, an identity verified in SES. Leave it blank to run
# without email — see Email below.
emailFrom: platform@example.com
bootstrap:
# No owner exists until at least one admin email is set. With none, nothing
# owner-gated can be created. Use an address you will actually sign in as,
# on one of the domains under deploymentIdp below.
adminEmails: ["admin@example.com"]
# The organization this install runs as, created on the first install and
# ignored afterwards (rename it in the app). Required: a fresh install
# refuses to render without it. The slug is 3 to 100 of [a-z0-9-], not
# "system".
organization:
name: Example Corp
slug: example-corp
# The SAML identity provider your users sign in through. It is the only
# sign-in path, so the chart refuses to render without it. Exactly one of
# metadataUrl or metadataXml — see Authentication.
deploymentIdp:
metadataUrl: "https://idp.example.com/app/saml/metadata"
domains: [example.com]
# Your own PostgreSQL 16: the application database here, and one database
# each for auth, Inngest and the gateway below. The application database must
# have the `vector` extension AVAILABLE: the first migration runs CREATE
# EXTENSION vector, and a server without it fails minutes into the install
# rather than at render.
database:
source: managed
# The bundled LLM gateway keeps its own database and role, never the
# application's. Name a Secret carrying its connection as discrete
# host/port/username/password/dbname keys. On the bundled clusters, set
# source: cnpg instead.
bifrost:
database:
source: existing-secret
existingSecret:
name: edisyl-bifrost-db
secrets:
provider: k8s-native
managed:
# Keep the real URL in a gitignored values file passed with a second -f,
# not in the file you commit — see Secrets and signing keys.
edisyl-database:
data:
DATABASE_URL: "postgresql://user:pass@host:5432/edisyl"
# The object store's credentials, and nothing else: provider, bucket,
# region and URL are config above. Blank on AWS S3 to use a pod IAM role.
edisyl-object-storage:
data:
OBJECT_STORAGE_ACCESS_KEY_ID: ""
OBJECT_STORAGE_SECRET_ACCESS_KEY: ""
inngest:
# Inngest's run history, in its own database: Secret edisyl-inngest-db, key
# uri, which you create. On the bundled clusters, set source: cnpg instead.
database:
source: existing-secret
# The bundled Inngest subchart does not read the install namespace. If you
# install anywhere other than a namespace called "edisyl", this must match.
namespace: { create: false, name: edisyl }
edisyl:
auth:
# The auth service's own database. Without the bundled cluster you
# provision it — a database, a role that owns it, and a Secret holding its
# connection URI — and name that Secret here. See Using your own
# infrastructure.
database:
secretName: mono-auth-db
secretKey: uri

The auth signing material and the gateway key are not in this file. They go in gitignored files passed with further -f flags — see Secrets and signing keys. The auth, Inngest and Bifrost/LLM database Secrets are created with kubectl before the install — see Using your own infrastructure.

Configuration lives in values, secrets in the store

Section titled “Configuration lives in values, secrets in the store”

Since chart 0.46.0 a secrets.managed group holds credentials and nothing else. Everything that is not a credential — the object store’s provider, bucket, region and URL, your Redis host, port, user and TLS setting, the sender address, the Sentry DSNs, the GitHub App id — is a value under config.*, rendered into the edisyl-config ConfigMap. Where the chart runs a service itself (bundled Redis, MinIO, the LLM gateway) it derives the address and you set nothing. A configuration key left in a managed Secret’s data, keys or remoteKeys fails the render, naming the config.* path to move it to, so a store behind external-secrets only ever carries secrets. A blank optional config.* key is left out of the ConfigMap rather than rendered as "", so the apps apply their own defaults.

config.deploymentTenancy defaults to dedicated: one organization’s own install, where every organization owner can set the LLM gateway’s provider API keys, bind its models and see deployment health. shared is Edisyl’s own multi-organization posture, where provider administration and health stay with the deployment owner, and the only one that serves the pack catalogue; an install that runs several organizations sets shared or leaves it blank. A self-hosted install keeps the default.

PostgreSQL and object storage are both off by default. A chart that stands up a database and an object store unless told otherwise hands you a production datastore you never chose, on whatever StorageClass happens to be default, holding the only copy of your data.

So the file above names what the chart cannot guess: the Postgres databases, an object store, the sender address, the identity provider and the organization. It refuses to render until each is coherent, and every refusal names the key to set.

Authentication is not a bundle at all. The chart always runs the platform’s own auth service, and your users sign in through the SAML identity provider you declare under deploymentIdp, which the chart registers for you — see Authentication.

Bundled Redis and Inngest are still on. Redis holds no durable state you would mourn, and the chart generates its password. Inngest keeps its run history in Postgres, in a database of its own. If you replace Redis with your own, it must have RediSearch and RedisJSON, and a password.

To use the bundled datastores instead — an evaluation install, say:

cnpg:
enabled: true
# One cluster per database, each sized on its own. Unset keys keep the
# chart's defaults (two instances each; 460Gi and 24Gi of memory for all
# four). storageClass "" uses the cluster's default; a name that does not
# exist leaves the PVCs Pending forever.
clusters:
services: { storageClass: "", storageSize: 100Gi }
database:
source: cnpg
inngest:
database:
# The bundled cluster provisions Inngest's database too.
source: cnpg
minio:
enabled: true
config:
# Blank: with MinIO the provider is derived as s3compatible, and `s3` is
# refused. The URL and path-style addressing are derived too.
objectStorageProvider: ""
# Must be a bucket MinIO creates (minio.buckets).
objectStorageBucket: edisyl-files
bifrost:
database:
# The bundled cluster provisions the gateway's database and role.
source: cnpg

cnpg.enabled and database.source are separate on purpose: the clusters existing and the applications being pointed at them are separate decisions. The chart refuses the combinations that disagree rather than rendering one that half-works — including bifrost.database.source: cnpg or inngest.database.source: cnpg with cnpg.enabled false. With the bundled clusters the auth service’s database is provisioned too, so the edisyl.auth.database block above is not needed. Sizing guidance is in Prerequisites.

Value Why
images.pullSecret ECR allows no anonymous pull; unless you use a kubelet credential provider
config.domain Every public URL derives from it, as <app>.<domain>
config.objectStorage* Nothing stateful is bundled; the provider and bucket have no default
bootstrap.adminEmails Empty means no owner exists in the database
bootstrap.organization A fresh install refuses to render without it
deploymentIdp SAML through it is the only sign-in path
database.source and edisyl-database Name your Postgres or turn the bundled one on
bifrost.database.* The bundled gateway needs a database decision
edisyl.auth.database The auth service’s database, unless the bundled cluster provisions it
inngest.database.source Where Inngest’s run history lives; unset without the bundled clusters, it is SQLite and lost on restart
inngest.namespace.name Must match your install namespace

A release does not always build every app from the same commit, so the chart pins a tag per app and no app inherits a global one. images.tag is a fallback nothing currently uses, which means setting it alone changes nothing.

To pin a different build than the chart’s default, override per app:

edisyl:
api:
image: { tag: prd-<sha> }
worker:
image: { tag: prd-<sha> }

The version matrix publishes the full block, every app’s tag, ready to copy for each release. The registry and the tags move together — these tags exist under the chart’s default registry and nowhere else, so overriding one without the other fails the pull.

Each first-party app — api, auth, edisyl, worker and parse — takes a list of extra environment variables under edisyl.<app>.env, in the Kubernetes env shape: a name with a value, or with a valueFrom. The chart adds them to the container as written, after its own variables, and a variable set here wins over one of the same name in edisyl-config or a managed Secret. Use it for a variable the chart has no value for, or when Edisyl asks you to.

The chart’s own variables are PORT and two it reads from the app’s image tag: IMAGE_TAG, the tag itself (prd-9c0dff5), and SENTRY_RELEASE, <app>@<tag> (api@prd-9c0dff5), which error reports are grouped by. Both follow a tag you override under Per-app image tags. An entry of the same name here replaces the chart’s.

edisyl:
api:
env:
- { name: "<VARIABLE>", value: "<value>" }

Setting the list replaces the chart’s default list; Helm does not merge lists. Three apps have defaults, and an override has to copy them:

  • worker sets INNGEST_SERVE_ORIGIN to http://worker:6002, the in-cluster address Inngest syncs the worker’s functions from. A worker.env without it drops it.
  • auth sets GOTRUE_API_HOST, GOTRUE_DB_DRIVER, GOTRUE_JWT_EXP, and the GOTRUE_OAUTH_SERVER_* settings MCP connections run through. Variables the chart owns for the auth service — its security settings and the URLs it derives from config.domain — cannot be set here; an entry naming one is dropped.
  • parse, off by default, sets OMP_NUM_THREADS to 1.

api and edisyl have no default list. helm show values on the chart version you install prints each default list in full, ready to copy.

The API sends email, such as invitations, from config.emailFrom through SES. The sender alone decides whether email goes out:

  • Set, the API sends from it. It must be an identity verified in SES.
  • Blank, the deployment sends no email. An invitation reports that it was not emailed, and the members page offers its link for the owner to share.

config.emailEnabled renders as EMAIL_ENABLED, which the API does not read. Leave it false; setting it true with a blank sender is refused by the render. A blank sender needs chart 0.56.0 or later — earlier API images refuse to start without one.

commonLabels puts your own Kubernetes labels on every resource the chart renders, pod templates included — so they are what a cost report, a network policy or an admission controller sees:

commonLabels:
team: platform
cost-center: "4210"

Selectors never include them, so adding or changing one later is an ordinary rolling update. The chart’s own keys (app, app.kubernetes.io/*, helm.sh/chart, platform.edisyl.com/env) cannot be overridden; setting one fails the render.

The bundled subcharts render their own resources and never see this value. To label those too, repeat the map under the keys each one reads: inngest.commonLabels for Inngest, and both minio.additionalLabels (the workload) and minio.podLabels (its pods) for MinIO. The configurator writes all of them for you.

The Inngest server keeps every run’s spans, step outputs, events and history in its Postgres database and never deletes any of it, so left alone that database grows until its disk is full, and a full disk stops every function. Since chart 0.63.0 a nightly CronJob, inngest-retention, deletes everything belonging to runs that started more than inngest.retention.days ago; the Inngest UI keeps that many days of history. Queue and in-flight run state live in Redis and are never touched: a run older than the window that is still sleeping loses its history in the UI, not its execution.

inngest:
retention:
enabled: true # false keeps every run, and the database grows without bound
days: 7 # a whole number, 1 or more
schedule: "17 3 * * *" # cron, in the cluster's time zone
batchSize: 5000 # rows deleted per transaction
  • It runs only on Postgres: the bundled inngest-db cluster, or Secret edisyl-inngest-db on your own server. It reads the database URI from the INNGEST_POSTGRES_URI entry of inngest.inngest.extraEnv, so an override of that list has to keep the entry as a secretKeyRef; a literal value renders no CronJob. Under SQLite there is nothing to prune.
  • The first run can be long. On a database that has never been pruned it deletes weeks of history, one short transaction per batch while Inngest keeps writing. A run stops after three hours, and the next night carries on where it stopped. On first run it also builds one index on Inngest’s traces table, edisyl_retention_traces_run_id.
  • Its image is a psql client, inngest.retention.image, which defaults to postgres:16-alpine pinned by digest. Mirror it along with the others if your cluster pulls only from an internal registry.

The chart also turns off Inngest’s step metadata, with EXPERIMENTAL_STEP_METADATA: "false" in inngest.inngest.extraEnv. It writes an extra timing span into Postgres for every step attempt, and nothing in Edisyl reads it.

Every generation and embedding call goes through Bifrost, an LLM gateway that holds your provider credentials so no application does. Since chart 0.47.0 it is the only gateway and on by default: bifrost.enabled is true, and the apps’ LLM_GATEWAY_URL is derived from it (http://edisyl-bifrost:8080/v1), not set by you. What it needs from you, when the chart runs it, is a database decision — the starting file above makes it — and the virtual key the apps send, which goes in your credentials file: see Secrets and signing keys.

The configurator’s LLM gateway choice, on its Infrastructure tab, chooses between running it in this install and using one another deployment runs.

Configure its providers before the deployment serves traffic. A fresh gateway knows no provider, so every inference call fails until an owner adds one on Edisyl’s provider-settings page and proves it answers.

Things to know about it:

  • One replica, always. Bifrost’s open-source build does not synchronize budgets or rate-limit counters between processes, so the chart runs a single-replica StatefulSet and exposes no replica count. Every upgrade has a short window with no gateway, and with all inference routed through it that window is an inference outage. On a Karpenter fleet, set bifrost.podAnnotations: { karpenter.sh/do-not-disrupt: "true" } so consolidation does not add more of them.
  • Its own database. The gateway migrates its own schema and never shares the application’s Postgres. With the bundled cluster the chart provisions it; with your own Postgres you do — see Using your own infrastructure.
  • The encryption key cannot be rotated. Every stored provider credential is encrypted under BIFROST_ENCRYPTION_KEY, and Bifrost cannot re-encrypt them. The chart generates it once and reads it back; back up the edisyl-bifrost-encryption Secret.
  • Request logs keep no bodies. Each call logs model, tokens, cost, latency and status for 30 days, on the gateway’s volume. bifrost.logs.content: true adds the prompts and answers; bifrost.logs.retentionDays sets the window.
  • It has its own ServiceAccount, edisyl-bifrost, so a cloud workload identity can be bound to the gateway alone. On EKS a Pod Identity association needs nothing in values; IRSA and GKE Workload Identity go in bifrost.serviceAccount.annotations. A Bedrock provider configured with blank access keys then authenticates as the pod.

Nothing publishes the gateway by default: the chart renders a ClusterIP Service and a headless edisyl-bifrost-headless Service (for a load balancer inside your network that registers pod IPs), and no Ingress. The management console is published only if you name an ingress class under bifrost.console — and that class must be a private one, which the chart cannot check for you. Console and inference share one port, so publishing it publishes provider administration with it; on a public class the only thing left guarding every vendor credential is one shared password. See the values reference for the full block.

A deployment can use a gateway another deployment runs — a staging install pointed at production’s, say — without running one of its own. Set bifrost.external.url to that gateway’s management address and turn the bundled one off:

bifrost:
enabled: false
external:
url: http://bifrost.internal.example:8080

Inference goes there too: LLM_GATEWAY_URL is that address plus /v1. No gateway pods, database or encryption key render. Providers belong to the deployment that runs the gateway, so here Edisyl’s gateway settings list its providers and bind this deployment’s own model tiers, but refuse to save or remove a provider credential. The two settings are exclusive — the render refuses both at once, or a URL without an http or https scheme.

The management credential and the runtime key are the external gateway’s own, so the chart cannot generate them: see Secrets and signing keys.

Every bundled dependency — PostgreSQL, Redis, object storage, the job runner — can be turned off and replaced with a managed service you already run. If you are installing on a cloud, read Using your own infrastructure before you install: it is easier to start there than to migrate a database out of the cluster later.

Third-party components lists what is on by default.

Read this before installing. The chart generates secrets once and reuses them by looking up the existing Secret in the cluster. Argo CD normally renders with helm template against no cluster, so that lookup returns empty on every sync and every generated value is regenerated — including ENCRYPTION_KEY, which orphans any data encrypted under the previous one, and BIFROST_ENCRYPTION_KEY, which orphans every provider credential the LLM gateway holds.

Under Argo CD you must supply these values explicitly, through secrets.managed.*.data or secrets.provider: external-secrets, rather than relying on generation.

Report incorrect code

Please provide a detailed description of the incorrect code.