Required Secrets
Lakehousecat follows a secret-based configuration model: all sensitive values
(API keys, access tokens, cloud credentials) are read by the operator from pre-created
Kubernetes secrets. They are never placed in the Lakehousecat custom resource (CR).
This page lists every secret the operator can consume, how to create it, and what happens when one is missing.
The CR is a plain Kubernetes object — it ends up in kubectl get -o yaml, in your GitOps
repository, in etcd, and in backups. Putting credentials there would expose them in all of
those places. Keeping them in dedicated secrets lets you manage, rotate, and protect them with
standard Kubernetes tooling (RBAC, encryption-at-rest, external secret managers).
How the operator handles missing secrets
The operator never aborts a deployment because a secret is missing — it degrades gracefully:
- The application always deploys.
- A feature whose secret is missing is simply skipped, and the operator logs a warning and
sets a status condition on the
Lakehousecatresource. - For pre-configured models, a missing model secret skips only that model — every other model and the rest of the platform come up normally.
This means you can deploy first and add optional secrets later (see Adding or rotating a secret later).
Secret overview
All secrets live in the instance namespace (spec.customer.namespace), except the
handshake secret, which lives in the operator namespace (lhc-operator).
| Secret | Required? | Used for | Keys |
|---|---|---|---|
lhc-handshake-secret | Required | License handshake (all tiers) | handshake-key |
lhc-model-<id>-secret | Optional | Pre-configured model credentials (one per model) | provider-specific (see below) |
lhc-superset-mapbox-secret | Optional | Superset map visualizations | MAPBOX_API_KEY |
lhc-storage-s3-secret | Optional | External S3 object storage credentials | access-key, secret-key |
lhc-oauth-<provider>-secret | Optional* | SSO / OAuth provider credentials | CLIENT_ID, CLIENT_SECRET |
License handshake secret (required)
Get your handshake key from portal.lakehousecat.com, then create the secret in the operator namespace:
kubectl create secret generic lhc-handshake-secret \
--from-literal=handshake-key=YOUR_HANDSHAKE_KEY_FROM_PORTAL \
-n lhc-operator
Reference it from the CR:
spec:
license:
handshakeSecretName: "lhc-handshake-secret"
Model credentials secrets
Pre-configuring models in the CR is a convenience feature — the operator deploys the listed models automatically after the platform is up. You can always add models later through the application UI instead.
Each model's provider settings are read from a secret named lhc-model-<id>-secret (where
<id> is the model's id), or from a custom name via spec.models[].credentialsSecretName.
The CR itself carries only non-sensitive routing fields:
spec:
models:
models:
- id: "chat"
name: "azure-openai-chat"
providerType: "Azure" # OpenAI | Anthropic | Azure | AWS | Google | xAI
modelType: "Chat" # Chat | Speech-to-Text | Text-to-Speech | Image-Generation
isDefault: true
isEnabled: true
# credentials come from secret lhc-model-chat-secret
Set the backend-default model from the CR
isDefault only controls which model users see pre-selected in the UI. To also set the
backend default — the model used platform-wide for backend processing, including semantic
extraction (analyzing datasource schemas and generating descriptions) — add isBackendDefault: true to a model entry:
spec:
models:
models:
- id: "chat"
name: "azure-openai-chat"
providerType: "Azure"
modelType: "Chat"
isDefault: true
isBackendDefault: true # this model becomes the backend default for Chat
isEnabled: true
This has the same effect as toggling Set as default for Backend in the application UI (see Create a Provider Model — Step 5), but lets you configure it declaratively at deployment time.
Only one model per modelType (e.g. one for Chat, one for Speech-to-Text) may have
isBackendDefault: true. If your CR accidentally lists more than one for the same modelType,
the operator does not silently pick the last one — it deploys only the first model (in the
order they appear in spec.models.models) as the backend default and reports the conflict on
the instance status:
kubectl get lakehousecat <instance-name> -n <instance-namespace> -o jsonpath='{.status.conditions}' | jq
Look for a condition of type ModelBackendDefaultConflict — its message names the winning model
and the ones that were ignored. Fix the CR (remove isBackendDefault: true from the extra
entries) and re-trigger a reconcile to clear it.
Secret keys per provider
| Provider | Required keys | Optional keys |
|---|---|---|
| OpenAI | API_KEY, MODEL_ID | ORGANIZATION_ID |
| Anthropic | API_KEY, MODEL_ID | — |
| Azure | API_KEY, ENDPOINT, DEPLOYMENT_NAME, API_VERSION | REGION, STT_LOCALES |
| AWS | ACCESS_KEY_ID, SECRET_ACCESS_KEY, REGION_NAME, MODEL_ID | — |
API_KEY, PROJECT_ID | MODEL_ID, STT_LANGUAGE_CODES (comma-separated) | |
| xAI | API_KEY, MODEL_ID | XAI_API_BASE_URL, XAI_STT_LANGUAGE, XAI_STT_FORMAT |
Create a model secret
Using kubectl directly (Azure example for model id chat):
kubectl create secret generic lhc-model-chat-secret \
-n <instance-namespace> \
--from-literal=API_KEY=YOUR_AZURE_KEY \
--from-literal=ENDPOINT=https://your-resource.openai.azure.com/ \
--from-literal=DEPLOYMENT_NAME=chat \
--from-literal=API_VERSION=2024-02-01 \
--from-literal=REGION=swedencentral
Or with the helper script shipped in the operator repository:
NAMESPACE=<instance-namespace> MODEL_ID=chat PROVIDER=Azure \
API_KEY=YOUR_AZURE_KEY \
ENDPOINT=https://your-resource.openai.azure.com/ \
DEPLOYMENT_NAME=chat API_VERSION=2024-02-01 REGION=swedencentral \
./cli/create-model-secret.sh
If a model secret is missing or incomplete, that single model is skipped during deployment. Fix the secret and re-trigger a reconcile (see below) to deploy it.
Mapbox secret (Superset maps)
Superset map charts need a Mapbox access token. Enable the feature in the CR and provide the token via the secret:
spec:
superset:
mapbox:
enabled: true # token comes from secret lhc-superset-mapbox-secret
kubectl create secret generic lhc-superset-mapbox-secret \
-n <instance-namespace> \
--from-literal=MAPBOX_API_KEY=pk.YOUR_MAPBOX_TOKEN
Or with the helper script:
NAMESPACE=<instance-namespace> MAPBOX_API_KEY=pk.YOUR_MAPBOX_TOKEN \
./cli/create-mapbox-secret.sh
If mapbox.enabled is true but the secret is absent, the platform starts normally and maps
stay inactive until you add the token.
External S3 storage secret
By default Lakehousecat deploys in-cluster SeaweedFS and needs no storage credentials. To use an external S3-compatible object store instead, provide the credentials via a secret and reference it from the CR:
spec:
objectStorage:
type: s3
region: "eu-west-1"
bucketPrefix: "mycompany-lhc"
credentialsSecretName: "lhc-storage-s3-secret"
kubectl create secret generic lhc-storage-s3-secret \
-n <instance-namespace> \
--from-literal=access-key=AKIA... \
--from-literal=secret-key=...
The same secret/keys apply to the legacy spec.storage.credentialsSecretName field.
Custom S3-compatible endpoints
Leaving endpoint unset targets native AWS S3. Set it to point at any other S3-compatible
endpoint instead:
spec:
objectStorage:
type: s3
endpoint: "https://storage.googleapis.com" # Google Cloud Storage via its S3-Interop API
region: "us-east-1"
bucketPrefix: "mycompany-lhc"
credentialsSecretName: "lhc-storage-s3-secret" # GCS: HMAC access/secret key pair
This works regardless of which cloud (or on-premise environment) the Kubernetes cluster itself runs on — the cluster only needs network access to the endpoint and a valid access/secret key pair. This is the supported path for hybrid / multi-cloud setups, e.g. a cluster running on Azure AKS configured to store objects in AWS S3.
Azure Blob Storage does not speak the S3 protocol and cannot be used as an objectStorage.endpoint
target. Lakehousecat does not support Azure Blob Storage as an object storage backend.
OAuth / SSO secrets
OAuth providers (Google, Microsoft Entra ID, generic OIDC, Auth0, AWS Cognito) are being
migrated to the same secret-reference model: provider credentials will come from
lhc-oauth-<provider>-secret (keys CLIENT_ID, CLIENT_SECRET), while non-sensitive fields
(domain, tenant, discovery URL) stay in the CR.
kubectl create secret generic lhc-oauth-google-secret \
-n <instance-namespace> \
--from-literal=CLIENT_ID=your-client-id \
--from-literal=CLIENT_SECRET=your-client-secret
Until the migration is complete, follow the provider configuration shown in the model/auth guides. This page will be updated with the final field layout per provider.
Adding or rotating a secret later
The operator reconciles on changes to the Lakehousecat CR. To enable a feature after the
initial deployment:
- Create (or update) the secret in the instance namespace.
- Edit the CR to enable the feature (e.g. set
mapbox.enabled: true, or add aspec.modelsentry). This triggers a reconcile.
# 1. create/rotate the secret
kubectl create secret generic lhc-superset-mapbox-secret \
-n <instance-namespace> \
--from-literal=MAPBOX_API_KEY=pk.NEW_TOKEN \
--dry-run=client -o yaml | kubectl apply -f -
# 2. trigger a reconcile (e.g. toggle the feature, or re-apply the CR)
kubectl apply -f lakehousecat.yaml
Some values (e.g. the Mapbox token consumed by Superset) are injected as environment variables and are only picked up when the pod starts. After rotating such a secret, the operator rolls the affected component on the next reconcile. Models are re-synced via API and need no restart.
Automating secret creation in a pipeline
For production, create all required secrets before deploying the Lakehousecat CR — as a
dedicated stage in your CI/CD pipeline (GitLab or GitHub), pulling values from your secret
manager (AWS Secrets Manager, Google Secret Manager, Azure Key Vault, …). The operator assumes
the secrets already exist and wires them in on the first reconcile.
Reference deployment repositories with ready-made pipelines for AWS EKS, Google GKE, Azure AKS, and on-premise are planned — they will create these secrets as part of the deployment flow.