Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 49 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ Deploy Dakera on Kubernetes in seconds. Two install paths: OCI registry (no repo

| | Dakera server | Chart version | Where |
|---|---|---|---|
| **Latest** | **v0.12.0** | **0.12.0** (and later 0.12.x) | `main` (this branch), the Helm repository and the OCI registry |
| **Latest** | **v0.12.1** | **0.12.1** (and later 0.12.x) | `main` (this branch), the Helm repository and the OCI registry |
| Previous | v0.11.x (last: v0.11.108) | 0.11.x | the [`release/0.11`](https://github.com/Dakera-AI/dakera-helm/tree/release/0.11) branch |

`main` and the latest chart are **v0.12.0**. The v0.11 chart is kept on the
`main` and the latest chart are **v0.12.1**. The v0.11 chart is kept on the
`release/0.11` branch, unchanged, so a v0.11 setup stays findable and working.
To stay on v0.11, pin the old chart version, for example:

Expand All @@ -35,7 +35,7 @@ helm install dakera dakera/dakera --version 0.11.107 ...
set `--set dakera.image.tag=0.11.108` to run the final v0.11 server.
`helm search repo dakera/dakera --versions` lists every published version. The
chart version always equals the Dakera server version it deploys. Upgrading from
v0.11 to v0.12.0: read [charts/dakera/CHANGELOG.md](charts/dakera/CHANGELOG.md)
v0.11 or v0.12.0 to v0.12.1: read [charts/dakera/CHANGELOG.md](charts/dakera/CHANGELOG.md)
and the server's
[UPGRADE.md](https://github.com/Dakera-AI/dakera/blob/main/docs/v0.12/UPGRADE.md).

Expand All @@ -56,7 +56,7 @@ Pin a specific version:

```bash
helm install dakera oci://ghcr.io/dakera-ai/dakera-helm/dakera \
--version 0.12.0 \
--version 0.12.1 \
--namespace dakera --create-namespace \
--set dakera.rootApiKey=<your-key> \
--set minio.rootPassword=<your-password>
Expand Down Expand Up @@ -84,6 +84,15 @@ helm upgrade dakera dakera/dakera --namespace dakera \
--set minio.rootPassword=<your-password>
```

### Upgrading from chart 0.12.0

`helm upgrade` deploys server 0.12.1; nothing is migrated by hand. Full-text indexes still on the v0.11
analyzer are re-analysed in the background at the first start, and a changed `DAKERA_MODEL` re-embeds the
store in the background. **Clusters:** every node now needs the same `DAKERA_CLUSTER_SECRET` (a node without
it exits with code 78) and the chart refuses cluster mode without it; switch all nodes in one window (see
"Cluster mode" below). Memory totals drop and session totals rise in the statistics endpoints. Details:
[charts/dakera/README.md, "Upgrading from chart 0.12.0"](charts/dakera/README.md#upgrading-from-chart-0120).

### Upgrading from chart 0.11.x

Tested from 0.11.107 (server 0.11.108) to 0.12.0 on one release (kind): `helm upgrade` switches the
Expand Down Expand Up @@ -167,7 +176,7 @@ variables, constraints, sizing and verification per feature, is

| Feature | Values | Example file | Costs | Constraints | Verify (`GET /v1/capabilities`) |
|---|---|---|---|---|---|
| **Multilingual** (bge-m3, per-language full-text, CJK bigrams, query languages, per-request `lang`) | `features.multilingual.*` (`fulltextLanguage`, `cjkBigrams`, `queryLang`, `maxSeqLength`) | `values-multilingual.yaml` | bge-m3 ~570 MB (+ ORT copy), CPU ONNX only, truncation 2048 tokens | **Fresh store**; `config.tiered: "0"`; not with late interaction / vision; one-way for v0.11 | `default_model` = `bge-m3` |
| **Multilingual** (bge-m3, per-language full-text, CJK bigrams, query languages, per-request `lang`) | `features.multilingual.*` (`fulltextLanguage`, `cjkBigrams`, `queryLang`, `maxSeqLength`) | `values-multilingual.yaml` | bge-m3 ~570 MB (+ ORT copy), CPU ONNX only, truncation 2048 tokens | Fresh store, or an existing one re-embedded in the background (0.12.1); `config.tiered: "0"`; not with late interaction / vision; one-way for v0.11 | `default_model` = `bge-m3` |
| **Attachments + speech to text** (Whisper, five models) | `features.multimodal.*` (`attachmentMaxBytes`, `memHighWaterFraction`, `resources`) | `values-multimodal.yaml` | see the model table (whisper-tiny.en ~151 MB); 8Gi / 4 cores (the measured configuration: 530 MiB anonymous with every model, +1.9 GiB image peak) | Any store; WAV only (multilingual by default, language auto-detected); media jobs wait up to 10 s for memory, then `503` + `Retry-After` | `attachments.enabled` |
| **Image / page indexing, visual recall** (colmodernvbert) | `features.vision.*` (`resources`) | `values-vision.yaml` | ~966 MB (+ ORT copy), conversion reserves ~1 GB, ~10.7 s per page on CPU, one page at a time | **A dedicated store: its own release** (own MinIO / bucket and volume); `config.tiered: "0"`; PNG only; not with the text models; one-way for v0.11 | `vision.enabled`, `scoring.late_interaction.lane` = `visual` |
| **Multi-vector records** | `features.records.*` (`maxVectors`, `maxBytes`) | `values-records.yaml` | extras stored beside the primary vector; default limits 4096 vectors / 8 MiB per record | Any store; at most 8 extra representations per record | `records.enabled` |
Expand All @@ -177,7 +186,7 @@ variables, constraints, sizing and verification per feature, is

```bash
# multilingual search on a fresh install (from a clone of this repository, for the example file)
helm install dakera oci://ghcr.io/dakera-ai/dakera-helm/dakera --version 0.12.0 -n dakera --create-namespace \
helm install dakera oci://ghcr.io/dakera-ai/dakera-helm/dakera --version 0.12.1 -n dakera --create-namespace \
--set dakera.rootApiKey=$(openssl rand -hex 32) --set minio.rootPassword=$(openssl rand -hex 16) \
-f charts/dakera/examples/values-multilingual.yaml
# several combinable features: examples/values-combined.yaml (multilingual + multimodal + records + rabitq)
Expand All @@ -195,13 +204,13 @@ dakera:
features:
multilingual:
enabled: true
fulltextLanguage: de # new namespace indexes; or multilingual (no stemming), zh, ja, ko, th, ...
fulltextLanguage: de # or multilingual (no stemming), zh, ja, ko, th, ...
queryLang: auto # en de fr es it pt nl, or auto (per query)
```

Requests may also send `"lang"`. A fresh store only (see below). Verify: `default_model` is `bge-m3`, `fulltext_language`
and `query_languages` on `/v1/capabilities`. Re-analyse an existing namespace: `POST /admin/fulltext/reindex`
`{"namespace": "...", "rebuild": true}`. The model is ~570 MB, not in the image; truncation defaults to 2048 tokens
Requests may also send `"lang"`. On an existing store the change of model re-embeds it in the background (see below).
Verify: `default_model` is `bge-m3`, `fulltext_language` and `query_languages` on `/v1/capabilities`. Existing full-text
indexes built with another analyzer are re-analysed in the background at the next start. The model is ~570 MB, not in the image; truncation defaults to 2048 tokens
(`maxSeqLength`: it supports 8192, at about 4 GiB of attention scores per layer per row).

### Multimodal: attachments and speech to text
Expand Down Expand Up @@ -325,14 +334,19 @@ in the pod (`kubectl exec deploy/<release>-dakera -- dakera models list`).

### Before you switch one on

- **Multilingual, late interaction and vision change the embedding model or the lane.** The store records its model and a
pod with another one exits at startup (the log names both). Use a fresh store, or migrate: acknowledge the change for ONE
rollout with `extraEnv` `DAKERA_ALLOW_MODEL_CHANGE=1`, pull the model, re-embed with
`POST /admin/namespaces/migrate-dimensions` (`{"target_dimension": 1024, "reembed_same_dimension": true}` for
bge-large to bge-m3), then remove it. Take a backup first. `dakera downgrade` (the `rollback` Job) refuses a
store on bge-m3, colbert-small or the visual lane.
- **Multilingual**: `fulltextLanguage` applies to **new** namespace indexes; re-analyse an existing one with
`POST /admin/fulltext/reindex {"namespace": "...", "rebuild": true}` (global admin key).
- **Multilingual, late interaction and vision change the embedding model or the lane.** The store records its model.
Take a backup first.
- **Multilingual** on an existing store (0.12.1): the pod starts and re-embeds the store with bge-m3 in the background,
one agent namespace at a time; until its turn each namespace is answered with the model its memories are in, so
recall stays correct. Progress: `/health` `embedding_model_change`, `/v1/capabilities` `reembed_pending`. No flag.
- **Late interaction and vision** are not re-embedded in the background (nor is a change under `DAKERA_TIERED=1`, or
from a recorded model this build does not know): a pod with another model exits at startup (the log names both).
Use a fresh store, or migrate: acknowledge the change for ONE rollout with `extraEnv` `DAKERA_ALLOW_MODEL_CHANGE=1`,
pull the model, re-embed with `POST /admin/namespaces/migrate-dimensions`
(`{"target_dimension": <dim>, "reembed_same_dimension": true}`), then remove it.
- `dakera downgrade` (the `rollback` Job) refuses a store on bge-m3, colbert-small or the visual lane.
- **Multilingual**: `fulltextLanguage` applies to every index; an existing one built with another analyzer is
re-analysed in the background at the next start (0.12.1).
- **Vision**: deploy it as its own release (`helm install dakera-vision ...`): the agent namespaces hold page
vectors, never put it over a text store.
- Check what a running server has on: `GET /v1/capabilities` (any key with Read scope) and `GET /health`.
Expand All @@ -352,16 +366,17 @@ Ready 25 s after install.
- Pre-pull before the pod serves: `dakera.models.pull: [configured]` (or a list of names).
- Keep the cache across pod restarts: `dakera.models.persistence.enabled: true`.
- Air-gapped: seed that volume from a host that can reach the Hub
(`docker run --rm -v <volume>:/app/models ghcr.io/dakera-ai/dakera:0.12.0 models pull --dir /app/models <model>`,
(`docker run --rm -v <volume>:/app/models ghcr.io/dakera-ai/dakera:0.12.1 models pull --dir /app/models <model>`,
then mount it as the PVC), and use only models the image or the volume holds.
See the server's [models-and-docker.md](https://github.com/Dakera-AI/dakera/blob/main/docs/models-and-docker.md).

## Observability

The server pod is annotated for Prometheus (`prometheus.io/scrape`, port 3000, path `/metrics`). The server's v0.12 alert
rules and dashboard are shipped as opt-in objects (off by default), both copied from the server repository, and every
expression and panel reads a metric the v0.12 server emits (checked against a running 0.12.0 server; many appear in
`/metrics` only once their event has happened: failures, cluster, encryption, Redis, media jobs):
expression and panel reads a metric the v0.12 server emits (checked against a running 0.12.0 server; the files and the
metrics are unchanged in 0.12.1; many appear in `/metrics` only once their event has happened: failures, cluster,
encryption, Redis, media jobs):

```yaml
monitoring:
Expand Down Expand Up @@ -392,13 +407,22 @@ dakera:
```

Cluster nodes must not share one S3 bucket as their store, and a server locks
its data root: one release of this chart is one node. When upgrading a running
v0.11 cluster leave the secret unset until every node runs v0.12.0 (server
UPGRADE.md, "Cluster").
its data root: one release of this chart is one node. Give every release the same
secret (the chart does not generate one: a value generated per release would differ
from node to node).

From 0.12.1 the secret is required on every cluster node, fresh or upgraded: a node
without it exits with code 78, and the chart refuses to render cluster mode without
it. Nodes with and without the secret do not see each other, so a cluster that runs
without one (0.12.0, or v0.11.108) switches in one window: scale every release's
server to 0, then `helm upgrade` each with the secret. The rolling alternative and
the checks: [charts/dakera/README.md, "Cluster mode"](charts/dakera/README.md#cluster-mode)
and the server's
[UPGRADE.md, "Cluster secret (required from 0.12.1)"](https://github.com/Dakera-AI/dakera/blob/main/docs/v0.12/UPGRADE.md#cluster-secret-required-from-0121).

## Rolling back to v0.11

Going back from v0.12.0 to v0.11.108 is supported (any v0.11 embedding model,
Going back from v0.12 to v0.11.108 is supported (any v0.11 embedding model,
encrypted or not). The v0.12 binary converts the data back with
`dakera downgrade`, which must run **after** the server has stopped (it refuses,
exit 78 with nothing changed, while a server runs on the data):
Expand Down
85 changes: 85 additions & 0 deletions charts/dakera/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,91 @@
The chart version equals the Dakera server version it deploys. v0.11 lives on the
`release/0.11` branch and in the 0.11.x chart versions (last published: 0.11.107).

## 0.12.1 (Dakera server v0.12.1)

### Security: the dashboard no longer receives the root API key

The chart deployed `dakera-dashboard:0.3.29` (enabled by default) and passed it
`DAKERA_ROOT_API_KEY` as `DAKERA_API_KEY`. Dashboard 0.3.x writes that key into
every page it serves, so anyone who could load the dashboard held the root
(`super_admin`) key (Dakera-AI/dakera-deploy#296). The chart now deploys
**Dashboard 0.5.0**: operators sign in at `/login` with their own API key, which
the dashboard keeps in server memory behind an `HttpOnly` cookie, and the chart
gives the dashboard no key. Probes use `/_session/healthz`; the new optional
`dashboard.sessionTtlHours` sets `DAKERA_SESSION_TTL_HOURS`.

**What to do:** if the dashboard was reachable by anyone who should not hold the
root key (an Ingress with `ingress.dashboardHost`, a `LoadBalancer`/`NodePort`
service, or a shared port-forward), **rotate `dakera.rootApiKey`**. Keep one
dashboard replica (sessions live in memory) and serve it behind TLS that
forwards `X-Forwarded-Proto` and `Host`.

### Upgrade notes (0.12.0 to 0.12.1)

Read the server's
[UPGRADE.md, "Upgrading from v0.12.0 to v0.12.1"](https://github.com/Dakera-AI/dakera/blob/main/docs/v0.12/UPGRADE.md#upgrading-from-v0120-to-v0121)
first. Nothing is migrated by hand; the data formats are those of 0.12.0.
From 0.11.x straight to 0.12.1: the 0.12.0 notes below apply too, except that
the cluster secret is set during the upgrade, not after it (item 2).

1. **Back up first** (`POST /admin/backups`).
2. **Clusters need `DAKERA_CLUSTER_SECRET` on every node.** A 0.12.1 node in
cluster mode without it exits with code 78, upgraded or not (0.12.0 let an
upgraded node start with its node-to-node routes open). Give every release
the same value of at least 16 characters (`dakera.cluster.existingSecret`
or `dakera.cluster.secret`). Nodes with and without it do not see each
other: switch them in one window (README, "Cluster mode"). A cluster that
already has the secret upgrades release by release as usual. Single-server
releases are not affected.
3. **Full-text indexes**: nothing to do. 0.12.1 re-analyses indexes still on
the v0.11 analyzer in the background at startup. The manual
`POST /admin/fulltext/reindex {"rebuild": true}` is only for a server still
on 0.12.0.
4. **Statistics totals change meaning.** Memory totals stop counting sentence
sub-memories and drop; session totals count ended sessions and rise
(`GET /v1/agents` `memory_count`, `GET /v1/agents/{id}/stats`,
`GET /admin/memory-type-stats`, `GET /v1/kpis` `session_count_weekly`).
The chart's alert rules and dashboard (identical to the server's v0.12.1
files) do not read these fields. Re-base your own dashboards, alerts and
reports.

### Changed
- Chart and `appVersion` 0.12.1; the server image tag follows `appVersion`
(`ghcr.io/dakera-ai/dakera:0.12.1`).
- **Cluster mode without a secret is refused at render time.** With
`DAKERA_CLUSTER_MODE` in `dakera.extraEnv` (`1`, `true`, `yes`, `on`, or set
with `valueFrom`) and no `dakera.cluster.secret`,
`dakera.cluster.existingSecret.name` or `DAKERA_CLUSTER_SECRET` entry in
`dakera.extraEnv`, helm fails with a message instead of deploying a pod that
exits. The chart does not generate a secret: one release is one node, and a
value generated per release would differ between nodes. A `<fullname>-secrets`
you created yourself with the key `DAKERA_CLUSTER_SECRET` is used with
`dakera.cluster.existingSecret.name=<fullname>-secrets`.
- `dakera.cluster.secret` must be empty or at least 16 characters in
`values.schema.json`; the template checks the length after trimming, as the
server does, and also checks a `DAKERA_CLUSTER_SECRET` value in
`dakera.extraEnv`. Setting the secret both in `dakera.cluster` and in
`dakera.extraEnv` is refused.
- Changing the embedding model re-embeds the store in the background (server
0.12.1): `features.multilingual` works on an existing store without
`DAKERA_ALLOW_MODEL_CHANGE`. That flag and
`POST /admin/namespaces/migrate-dimensions` remain the way for
`lateInteraction`, `vision` and `DAKERA_TIERED=1`. values.yaml, NOTES.txt
and the README say so.
- `features.multilingual.fulltextLanguage` applies to existing indexes too:
an index built with another analyzer is re-analysed at the next start.
- NOTES.txt: the v0.11 full-text rebuild reminder is gone (0.12.1 does it at
startup); after an upgrade in cluster mode it prints the secret reminder.
- README: "Upgrading from chart 0.12.0", the cluster upgrade procedure, and the
manual full-text rebuild kept for servers still on 0.12.0.

### Security
- Server 0.12.1 requires `DAKERA_CLUSTER_SECRET` on every cluster node: on
0.12.0 an upgraded node without it ran with open node-to-node routes, and
`POST /internal/sync/key/add` let anyone who reached the API port mint a
SuperAdmin key. It also carries the 2026-10-04 code audit fixes (server
CHANGELOG).

## 0.12.0 (Dakera server v0.12.0)

### Upgrade notes (0.11.x to 0.12.0)
Expand Down
Loading
Loading