Upgrading
When: a new version of the service, or of the chart.
Who: the platform operator.
What happens at start
- Migrations run at start, one replica at a time (a lock in the database). The Kubernetes store has none: its CRDs are the chart's.
- An older binary refuses a database migrated by a newer one, never writes it. Going back is a restore.
- A command run with a newer image than the service's migrates the database too: run the service's own version (Running a command).
Steps
-
Read the release notes (GitHub releases) of every version between the running one and the new one: a step of its own, a CRD changed, a setting renamed.
-
Back up the database, or the namespace's
tresorresources (Backup and restore). -
The CRDs (Kubernetes store): Helm installs
crds/on the first install only, and never upgrades them. An upgrade that changes them says so: apply them first.helm pull oci://ghcr.io/hugr-lab/charts/tresor-server --version <chart version> --untarkubectl apply --server-side -f tresor-server/crds/Variables (spec 004) brought
TresorVariable: an install from before needs it, or the service does not start. -
The image, in an upgrade of its own - not with a configuration change:
- Kubernetes:
helm upgradewith the new chart version (itsappVersion) orimage.tag. TherefsBeforeUpgradehook does not run when the image changes. - Container Apps: the recipe again with
-p image=ghcr.io/hugr-lab/tresor-server:<version>. A version tag, notedge. - ECS:
terraform apply -var image=ghcr.io/hugr-lab/tresor-server:v<version>(the recipe'simage). - Cloud Run:
terraform apply -var image_tag=v<version>(imageonly for an image of your own registry). - Docker, a VM: the new image or binary, one replica at a time.
- Kubernetes:
-
The version's own steps, once every replica runs it (below).
Steps a version asks for
Authenticated data keys (spec 003)
Data keys made before carry no tag, and every value sealed under them is refused (500) until they are tagged.
Once, with the new version:
-
Key Vault: give the KEK the
signoperation (az keyvault key set-attributes --vault-name <vault> --name <key> --ops wrapKey unwrapKey sign, or rotate it with those operations), and the service's identity the custom role (get, wrap, unwrap, sign) on the key (Encryption). -
rewrap -tag-untagged: it tags the data keys that have none - you vouch for the store as it is - and logs each one. No job runs it: on Kubernetes, a job rendered from<fullname>-rewrapwith its arguments changed (Running a command); on Container Apps, not supported by the recipe; a VM:tresor-server rewrap -tag-untagged -config server.yaml
The SQL stores' MAC (spec 014; on by default since 0.4)
From 0.4 the checks are on unless state.mac: false. A database whose rows were all written by 0.2 or later
needs nothing. One with rows from before 0.2 (no MAC) makes the start stop, naming how many:
mac(<fullname>-mac,<prefix>-mac; on SQLite with the service stopped) with the new version. Rows with no MAC, or a stale one, get one: you vouch for the database. A row that cannot be given one (its data key gone) is named, and the command exits1.- Start the service. To start first and fill later:
state.mac: falsefor now (Changing the configuration), then remove it aftermac.
- The refused start has already migrated the database (the MAC's columns come with it): going back to a version before 0.2 means restoring the backup.
rewrapandresealopen the store as the service does: on such a database they stop with the same message -macfirst, orTRESOR_STATE__MAC=falsefor that run.
Upgrading to 0.4 (spec 022)
state.macon by default on the SQL stores (above): a database with rows from before 0.2 needsmaconce, or the start stops and says so. See State.- A row whose MAC does not verify is refused (a read fails, a list leaves it out), where before, with the
setting unset, it was served and logged: before the upgrade, 0.3's log warns
a row's MAC does not verifyfor each; after,tresor.state.left_outcounts them.macgives them a MAC again, if you vouch for them. cors.allowed_origins, new: web pages running tresor in DuckDB-wasm, apart from the console'sui.allowed_origins(Configuration). Nothing changes until it is set.- Signed artifacts: the image and the chart can be verified before an upgrade (Security).
Nothing else: no migration of the Kubernetes store, no CRD changed, no setting renamed.
Upgrading to 0.5 (spec 023)
- Kubernetes store: apply the CRDs first (step 3: the 0.5 chart's
crds/):TresorSecretandTresorVariablehave a new field,dynamic. With the old CRDs the API server drops it, and a secret with generated parameters is refused (its MAC no longer verifies). - The SQL stores migrate at start (a column,
dynamic): nothing to do. - Roll out every replica before writing a generated parameter: a replica still on 0.4 serves
mint+…as the text it is. provider: token_exchangeis served asconfig, the consumer's provider: nothing reads the old name. Itsexpires_atis now the token's expiry lessdynamic.margin(at most half of what is left), not the expiry itself: a client fetches a new token sooner.- Secrets written before: not dynamic, so a
mint+…or${…}stored in one is served as it is, until the secret is written again. Only an escape changes:$${is served as${,mint++asmint+. - New, off until set: service logins call nothing until
dynamic.login_hostslists a host; rules andjwt_assertionuse nothing untildynamic.identitieslists an identity (Generated parameters). A secret of typetresor_service_loginis never served. - ZITADEL key files: a machine user's key (
userId, noclientId) is now read as that user's client - anexchange.key_fileof such a key names its client by it.
Upgrading to 0.5.1
- Nothing to do: tresor's extension at a newer commit (the docs); no migration, no setting.
Upgrading to 0.6 (spec 024)
- Nothing to migrate: no column, no CRD change (variables'
dynamiccame with 0.5). - Variables take generated values (
mint+exchange://,mint+claim://,mint+login://,${…}), servedsensitivewith anexpires_at(tresor spec 037). Roll out every replica before writing one: a replica on 0.5 serves it as the text it is. - A variable written before is served as written, but for its escapes (
mint++,$${); written again, a text that is not a valid generator is refused - write it escaped. - A login's scope begins with the base URL it calls, then base URLs or
variable://<name>only (a rule's: any scheme, within its identity's destinations). - A sandboxed DuckDB (tresor spec 036) stops tresor's own requests: name the service's and the issuers' URLs in
allowed_directories(Security).
Upgrading to 0.7 (spec 026)
- Nothing to migrate: no column, no CRD change.
- New, off until set:
namespaceOverrideandclusterIssuerin the chart; an issuer'sca_fileanddiscovery_token_file(Kubernetes).discovery_token_fileneedsca_file. - The chart's objects now name their namespace (
metadata.namespace): the same one as before unlessnamespaceOverrideis set.
Upgrading to 0.7.1
- Nothing to do: Go 1.27.3, a security release, in the binary and the image.
Upgrading to 0.7.2 (specs 027, 028)
- Nothing to migrate: no column, no CRD change; nothing changes until set.
state.auth: vault(spec 027): the state's database credentials from a Vault/OpenBao database secrets engine, a user per lease; on PostgreSQLstate.vault.set_rolenames the group role that owns the tables (OpenBao and Vault). A database already in use moves to it once its tables belong to that group role (ALTER TABLE … OWNER TO tresor_owner, orREASSIGN OWNED BY).configTemplate(spec 028, the chart):configrendered throughtpl- off by default, the render unchanged.
Upgrading to 0.7.3 (spec 029)
- Nothing to migrate; the chart and the configuration are unchanged.
- The TLS certificate is read again when it changes (every 30 s): a renewal in place needs no restart. A bad
new pair leaves the old one serving, and readiness's
tlscheck says so (degraded).
Upgrading to 0.7.4 (spec 030)
- Nothing to migrate; nothing changes until set.
whoaminames the person (name), for people to read; a delegation grant made before 0.7.4 has none (a new session carries it).- An issuer's
ignore_roles: roles never taken as principals - before adding one, check no grant orpolicy.adminsnames those roles.
Upgrading to 0.7.5 (spec 031)
- Nothing to migrate; nothing changes until set.
- An actor by role (
policy.actors,role:<name>with itsissuer): before adding one, check who can give the role in the identity provider - every client given it acts for users. - A delegation grant is presented by a service token of its own client: a grant used with another kind of token of the same identity stops working.
Upgrading to 0.8.0 (specs 032-034)
- Nothing to migrate; nothing new changes until set.
- A client secret without a client id is sent in the form: an
oauth2_passwordoroauth2_refreshlogin with aclient_secretand noclient_idsent HTTP Basic with an empty user before - check such a login still logs in, or give it itsclient_id. - New: Snowflake through Horizon (Generated parameters:
a PAT with no
client_id, thesnowflakeidentity kind, per user bymint+exchange); the Docker recipe (Docker). - The recipes' defaults (Terraform, Compose) are v0.8.0:
terraform applywith noimagemoves to it.
Upgrading to 0.8.1
- Nothing to migrate, no change in the service: the docs install tresor from DuckDB's community extensions; tresor's conformance suite runs at tresor v0.1.1; the recipes' defaults are v0.8.1.
Check
- Every replica ready, on the new version: the console's Service screen names it.
- A secret written before the upgrade reads.
refsexits0(<fullname>-refs,<prefix>-refs).- No
500in the log for a data key or a row.
Back
- Before a migration: the old image.
- After one: the old binary refuses the database. Roll forward, or restore the backup from step 2 with the old image - writes made since are lost.
- The MAC checks:
state.mac: falseand deploy; the MACs stay written.