Upgrading JAVV¶
This page tells you how to move a running JAVV to a newer release:
- What to check before you start.
- The sequence of the upgrade, with docker compose or with Helm.
- How to check the result, and how to go back to the previous release.
JAVV has three parts. You upgrade each part separately:
| Part | What changes its version | Where to find the version |
|---|---|---|
| Backend (FastAPI) | The JAVV release | The release tag. GET /api/v1/meta → version |
| Frontend (Vue) | The JAVV release, together with the backend | The same release |
| Scanner images (Trivy, Grype) | You change the published image tag in your deployment | versions.yaml lists the supported scanner versions |
JAVV never changes a version in a cluster that you scan. You set each version as a tag in your
own deployment. To install JAVV, see DEPLOYING.md.
For AI agents
Obey these rules when you upgrade JAVV for a user:
- Get the version that runs now from
GET /api/v1/meta→version. - Read each row of Version notes after that version, up to the new version. Do what each row tells, in the sequence of this page.
- Upgrade the backend first, then the frontend, then the scanner images.
- Before you start, read Go back to a previous release.
Before you upgrade¶
- Read the release notes. Each release has a GitHub Release and an entry in
CHANGELOG.md. Then read Version notes on this page. They tell you what a release needs from you. - Make a snapshot. The upgrade does not need it.
- Use Settings › Data & OpenSearch › Snapshot now, or
POST /api/v1/admin/snapshots. The request needscan_manage_retention. - The request returns 409 when OpenSearch has no snapshot repository. Snapshots tells you how to add the repository.
- The snapshot keeps the current state, the triage, the decisions, the users, the settings and the audit log. It does not keep the scan history that time travel reads. Thus a restore does not bring the history back.
- Use Settings › Data & OpenSearch › Snapshot now, or
- Check the OpenSearch version. CI tests JAVV with the version in
datastore.opensearchinversions.yaml.
Sequence of the upgrade¶
Upgrade the backend first, then the frontend, then the scanner images.
- Backend. Each new backend pod upgrades the indices when it starts.
- It compares each index and template with the schema version of the release, and it adds the missing parts.
- It opens its port only after this step. Thus it receives no requests before its indices are ready.
- You do not need to do a step manually.
- Frontend. Upgrade it together with the backend, or immediately after the backend. A newer frontend can send requests that an older backend does not have.
- Scanner images. Change the Trivy and Grype image tags after the backend runs the new
release. A newer scanner can send a report format (
schema_version) that only the newer backend accepts.GET /api/v1/meta→envelope_versionslists the formats that the backend accepts.
There is only one backend, and an upgrade replaces it. Thus two backend releases never run at the same time.
After you go back to an older release, an older backend reads indices that a newer backend changed. This is safe, because each schema change until now only added fields:
- An older backend ignores the fields that it does not know.
- A newer backend reads a missing field as empty.
With docker compose¶
Prerequisites:
- Your
.envfile. Keep it: it holds your settings. - The
compose.yamlof the new release. It names the backend and frontend images of that release. - A copy of your changes to
compose.yaml, for example the host port. Make the same changes in the new file.
Steps:
- In the folder of your installation, keep a copy of the old file, and get the file of the new
release.
VERSIONis the release that you upgrade to:VERSION=0.9.6 cp compose.yaml compose.yaml.old curl -fsSLO "https://raw.githubusercontent.com/Danube-Labs/javv/v$VERSION/deploy/compose/compose.yaml" - Make your changes to
compose.yamlagain, if you had changes. - Pull the images and start them:
docker compose pull docker compose up -d
JAVV is not available for some seconds, while the new backend starts and upgrades the indices. The
frontend waits until the backend reports healthy (depends_on).
To go back to the previous release, put the compose.yaml of that release back
(compose.yaml.old), and run the two commands of step 3. To go back to a release before 0.7.0,
first put back the old name of the secret key
(Go back to a previous release).
Upgrade from 0.5.1 to 0.6.1 or later¶
From 0.6.0, OpenSearch requires a sign-in. 0.6.0 published no images, so use 0.6.1 or later.
Before step 3, add these two values to your .env:
JAVV_OPENSEARCH_ADMIN_PASSWORD: the password of the OpenSearch admin.JAVV_OPENSEARCH_PASSWORD: the password ofjavv, the user that the backend signs in as.
The compose file does not start without these two values. OpenSearch refuses a weak admin password.
DEPLOYING.md gives the rules.
Your data stays:
- When OpenSearch first starts with its sign-in on, it keeps each index, and it adds its security configuration.
- You can also go back to a 0.5.x
compose.yamlwith the same data. Then OpenSearch runs without its sign-in again.
We did these steps on the real compose files, from a 0.5.1 installation with data.
Change an OpenSearch password¶
OpenSearch reads JAVV_OPENSEARCH_ADMIN_PASSWORD and JAVV_OPENSEARCH_PASSWORD only on its first
start with its sign-in on. It keeps the two passwords in its data. Thus OpenSearch ignores a new
value in .env:
- With a new admin password, OpenSearch never reports healthy, because its
healthcheckuses the new value. The backend waits for OpenSearch, anddocker compose up -dfails after some minutes. - With a new
javvpassword, the backend stops with "refused the credentials in JAVV_OPENSEARCH_USERNAME".
To change a password and keep the data:
- Put the new password in
.env. - Start only OpenSearch:
docker compose up -d opensearch. It still accepts only the old password. - Load the new password into the security index of OpenSearch. Use the demo admin certificate in
the image:
The command ends with
docker compose exec opensearch plugins/opensearch-security/tools/securityadmin.sh \ -f config/opensearch-security/internal_users.yml -t internalusers -icl -nhnv \ -cacert config/root-ca.pem -cert config/kirk.pem -key config/kirk-key.pemDone with success. The compose file writesinternal_users.ymlfrom.envat each start. Thus the file holds the two new passwords. - Run
docker compose up -d.
Other methods do not work, or delete data:
docker compose down -valso makes OpenSearch use a new password. But it deletes all JAVV data.- The account API of OpenSearch cannot change the admin password:
adminis a reserved user (403).
Only for OpenSearch started from a main checkout: load the missing users¶
Do these steps only when the security index of OpenSearch holds only admin. No release made this
state. A main checkout before 0.6.0 can make it, because OpenSearch reads the files for users,
roles and role mappings only on its first start with its sign-in on.
- In
.env, move the value ofJAVV_OPENSEARCH_PASSWORDtoJAVV_OPENSEARCH_ADMIN_PASSWORD. In this state,JAVV_OPENSEARCH_PASSWORDwas the admin password. - Give
JAVV_OPENSEARCH_PASSWORDa new value. This is the password ofjavv. - Start only OpenSearch:
docker compose up -d opensearch. - Load the three files one time, with the same certificate:
Each file ends with
for f in internal_users:internalusers roles:roles roles_mapping:rolesmapping; do docker compose exec opensearch plugins/opensearch-security/tools/securityadmin.sh \ -f "config/opensearch-security/${f%%:*}.yml" -t "${f#*:}" -icl -nhnv \ -cacert config/root-ca.pem -cert config/kirk.pem -key config/kirk-key.pem doneDone with success. Sometimes the tool then shows ajava.nio.channels.ClosedSelectorException. This message comes from the HTTP client of the tool when it closes, after the load. You can ignore it. - Run
docker compose up -d.
The same loop loads a changed javv role. If a release changes the role, its notes tell you. We did
these steps on the real compose files, from an OpenSearch that a main checkout started.
On Kubernetes (Helm)¶
Each release publishes the JAVV chart and the OpenSearch chart
(DEPLOYING.md) at oci://ghcr.io/danube-labs/charts,
with the version of the release.
Prerequisites:
- Your settings in your own values files. An install with
--set, as inDEPLOYING.md, writes no values file. Thus, before the first upgrade, write the values of each release to a file:helm get values store -o yaml > my-store-values.yaml helm get values javv -o yaml > my-javv-values.yaml helm get values scanner -n javv-scanner -o yaml > my-scanner-values.yaml # in each scanned cluster - These files hold each value that you set, the passwords included. Keep them as safe as your Secrets.
Steps:
- Upgrade with the chart of the new release and the same files.
VERSIONis the release that you upgrade to:VERSION=0.9.6 helm upgrade store oci://ghcr.io/danube-labs/charts/javv-opensearch --version "$VERSION" \ -f my-store-values.yaml helm upgrade javv oci://ghcr.io/danube-labs/charts/javv --version "$VERSION" -f my-javv-values.yaml helm test javv- Do the
storeupgrade when the release changed the OpenSearch chart. Version notes tells you which releases did. - It is safe to do it when the chart did not change. Then the upgrade changes only labels, and OpenSearch does not restart.
- Do the
- Upgrade the scanners last, in each cluster that you scan, after JAVV runs the new release. Keep
your values file (
backendUrland the token Secrets):VERSION=0.9.6 helm upgrade scanner oci://ghcr.io/danube-labs/charts/javv-scanner --version "$VERSION" \ -n javv-scanner -f my-scanner-values.yaml- A newer scanner can send a report format that only the newer backend accepts.
- The
javv-scannerchart holds the scanner versions of its release. - When the refresh container changes, the upgrade starts a vuln-DB refresh. The refresh
container changes with the image, the DB source,
extraEnv,resourcesorpullPolicy. - The next cycle uses the new image.
While the backend restarts:
- The backend runs as one pod with
strategy: Recreate. The backend runs the background jobs, so two backends must never run at the same time. Kubernetes stops the old pod before it starts the new pod. - JAVV is not available for some seconds, while the new pod checks OpenSearch and upgrades the indices. The startup probe gives this step 300 seconds.
- During this time, the frontend replies 502, and the web app shows "backend not answering".
- Kubernetes replaces the frontend with no gap.
To go back to a previous revision, use one command. helm history javv lists the revisions:
helm rollback javv # to the revision before the current one
helm rollback javv 3 # or to a specific revision
- The settings and the images of that revision come back.
- The indices stay as the newer release left them, and an older backend can read them (Go back to a previous release).
- CI does an upgrade and a rollback for each change to the charts.
- For the scanners, run
helm rollback scannerin each scanned cluster. Do this before you go back to the previous JAVV release.
To change an OpenSearch password (of admin, or of javv, the user that the backend signs in
as), see the
javv-opensearch chart README.
- After you change the
javvpassword, restart the backend. Then it reads the new password from the same Secret:kubectl rollout restart deploy/javv-backend. - The same section of the README tells you how to load the
javvrole and its mapping into an OpenSearch that first started without them.
Check what runs¶
| Check | Where | Expected result |
|---|---|---|
| OpenSearch replies | GET /readyz (no sign-in) |
200 |
| The versions that run | GET /api/v1/meta (with a signed-in session) |
{"version": "<release>", "mapping_version": <n>, "envelope_versions": [...]} |
| The versions that run, in the web app | The bottom of the sidebar | Three lines: v<release>, store schema v<n>, scanner schema v<n> |
| What the upgrade did | The backend log, one line for each pod start | The event bootstrap complete, with app_version, mapping_version, and the indices in created, updated or unchanged |
| The scanners use the new version | The Scanner status page, after the next scan cycle | The new scanner version for each cluster |
On the first start of a new release, updated lists the indices whose schema changed. Later starts
list all indices as unchanged.
Go back to a previous release¶
- The indices stay safe when you go back to an older backend. The index setup changes indices only to a newer schema. An older release finds the indices at a newer schema version, and it does not change them. The fields that the newer release added stay, and the older release does not use them.
- The older release can read the settings that the newer release saved. The newer release can
add a field to a setting. If you saved that setting, the older release ignores the new field. The
backend logs
stored setting has unknown keysone time for each setting. It counts each of these reads injavv_stored_setting_unknown_fields_total(API.md§ Metrics). If you save that setting while the older release runs, the older release saves only the fields that it knows. After you upgrade again, the new field has its default value. This is true when you go back to 0.4.8 or later. - Go back with the scanners first when the report format changed between the two releases. Then the scanners do not send a format that the older backend refuses.
- A release before 0.7.0 needs the old name of the secret key. These releases read
JAVV_TOKEN_PEPPER(docker compose) or the keytoken-pepperofsecrets.existingSecret(Helm). If you deleted it after the upgrade to 0.7.0, put it back with the same value before you go back:- docker compose:
sed -n 's/^JAVV_SECRET_KEY=/JAVV_TOKEN_PEPPER=/p' .env >> .env. Without it,docker compose upstops withset JAVV_TOKEN_PEPPER in .env, and the newer release continues to run. - Helm with
secrets.existingSecret: copy the value ofsecret-keyintotoken-pepper. Without it,helm rollback javvsucceeds, but the older backend pod does not start (couldn't find key token-pepper in Secret). JAVV is not available until you copy the value.kubectl patch secret <your-secret> --type merge -p \ "{\"data\":{\"token-pepper\":\"$(kubectl get secret <your-secret> -o jsonpath='{.data.secret-key}')\"}}"
- docker compose:
- CI tests a rollback from each change to the last release. On a new docker compose store, CI starts the last release and writes data with it. Then CI starts the change, the last release again, and the change again. After each start, CI signs in and reads the same findings and the same SLA policy.
- A snapshot does not give a rollback in one step. A restore makes
restored-*copies next to the live indices. It never writes over them (POST /api/v1/admin/snapshots/{snapshot_name}/restore). To use a copy, you must move it into place manually.
The first run of the background jobs¶
From 0.5.0, the backend starts its own background jobs. On an installation that ran these jobs only manually, or never, the first scheduled runs do all the work at the same time:
- The lifecycle sweep (03:00 by default) deletes all scan history that is older than the retention of each cluster (90 days by default). After this, time travel cannot show that period. Before the jobs run for the first time, see what the sweep will do: Data inspector › Repair actions › Lifecycle sweep › Dry run. If it deletes more than you want, increase the retention in Settings › Data & OpenSearch.
- The staleness sweep (02:00) marks as stale each finding that no scan found again for 3 days. It also marks as stale all findings of a scanner that sent nothing for 7 days.
- The findings cleanup (04:00) deletes the findings that no scan reported for longer than its period (180 days).
A job does not run because the backend started. Each job waits for its next scheduled time.
- To stop all the jobs, start the backend with
JAVV_SCHEDULER_ENABLED=false. - To stop one job, set its
JAVV_JOB_<KIND>_CRONto an empty value. - Job schedules and the time zone gives the schedules, and the time zone that the backend uses for them.
If you ran these jobs from your own cron or from CronJobs, delete them. The scheduler of the backend replaces them. If both run, nothing breaks: the backend does not start a job that is already running. But the second run is work for no result.
Version notes¶
A release gets an entry here when it needs something from you. These are the conditions:
- The store schema changed (
mapping_version). - The report formats that the backend accepts changed (
envelope_versions). - The scanner images must be published again, or changed together with the release.
- The OpenSearch chart (
javv-opensearch) changed. Then do thestoreupgrade.
The entries start with 0.6. Before 0.6, each schema change only added fields, and the backend applied it automatically on the first start.
| Release | Store schema | Report formats accepted | What to do |
|---|---|---|---|
| 0.9.6 | 22 | 3 and 4 | Nothing. The backend makes the new index system-mcp-tokens on the first start. The javv role of an OpenSearch of your own already has access to it, because its name starts with system-. |
| 0.9.5 | 21 | 3 and 4 | Nothing. The backend adds a field to the store on the first start. From the first scan after the upgrade, the counts of a scan count findings, not the entries of the scanner report. The scans before the upgrade keep their counts. Until both scanners scan an image again, the count pair of the image can compare a findings count with an entries count. |
| 0.9.1 | 20 | 3 and 4 | Nothing. The backend adds a field to the store on the first start. |
| 0.8.9 | 19 | 3 and 4 | The notes A new permission for the export files and A new permission for dashboards below |
| 0.8.7 | 18 | 3 and 4 | The note JAVV refuses a username with a capital letter below |
| 0.8.0 | 18 | 3 and 4 | The note A bootstrap admin password longer than 256 characters stops the backend below |
| 0.7.0 | 18 | 3 and 4 | The note JAVV_TOKEN_PEPPER is now JAVV_SECRET_KEY below |
| 0.6.2 | 18 | 3 and 4 | The note JAVV retires clusters that send no scans below |
| 0.6.0 | 18 | 3 and 4 | The note How the backend connects to OpenSearch below |
No release from 0.4.8 to 0.8.0 changed the store schema (18) or the report formats that the backend accepts (3 and 4).
No release up to and including 0.9.0 changed the OpenSearch chart in a way that needs the store
upgrade. Each release changed its version number. Some releases also changed other parts of the
chart, but not OpenSearch:
- 0.7.1 and 0.8.6 changed the text of its README and of the comments in
values.yaml, and no value. 0.8.1 changed the text of its README. - 0.8.7 changed the test pod of the chart (
helm test store). The pod can now run in a namespace with therestrictedlevel of the pod security standards.
0.8.9: a new permission for the export files¶
A user now needs the permission can_export to download a file of the data:
- the CSV and VEX files of the findings
- the CSV files of the audit log, the contributors and the approvals queue
- an export report
Without the permission, these requests get 403. The viewer role does not get it. A viewer can
still read the data on the screens and through the API.
A new store gives the permission to the triager, security_lead and admin roles. But JAVV
writes the roles only on the first start of a store. Thus on a store that you have, only admin
gets the permission, because * holds each permission. Also, each user keeps a copy of the
permissions of its role.
After the upgrade, give the permission to the triager and security_lead roles and to their
users:
- Save this script as
can-export.sh, in the folder of your installation:# Adds can_export to the triager and security_lead roles, and to their users. login=$(printf %s "$OPENSEARCH_INITIAL_ADMIN_PASSWORD" | sed 's/[\\"]/\\&/g') curl -sSk -K - -X POST 'https://localhost:9200/system-roles,system-users/_update_by_query?refresh=true' \ -H 'content-type: application/json' -d '{ "query": {"terms": {"role": ["triager", "security_lead"]}}, "script": {"lang": "painless", "source": "def c = ctx._source.capabilities; if (c == null || c.contains(\"can_export\")) { ctx.op = \"noop\" } else { c.add(\"can_export\") }"} }' <<EOF user = "admin:$login" EOF echo - Run the script in the OpenSearch container:
The answer has
docker compose exec -T opensearch sh < can-export.sh"failures":[].updatedis the number of roles and users that the script changed. If you run the script again, it changes nothing.
The script reads the admin password in the container, as the health check of the compose file does. Thus the password is not in your shell history or in the arguments of a process. The users do not sign in again: JAVV reads the permissions of a user on each request.
On Kubernetes, or with an OpenSearch of your own, send the same request to your OpenSearch, with its admin.
We did these steps on the compose files of 0.8.8, with a triager and a security lead.
0.8.9: a new permission for dashboards¶
A user needs the permission can_edit_dashboards to make a dashboard. The viewer role does not
get it.
The store schema is now 19. At its first start, the backend makes the new index
system-dashboards. You do not do a step for it. The javv role of an OpenSearch of your own
already has access to it, because its name starts with system-.
A new store gives the permission to the triager, security_lead and admin roles. On a store
that you have, only admin gets it, because JAVV writes the roles only on the first start of a
store. Also, each user keeps a copy of the permissions of its role.
After the upgrade, give the permission to the triager and security_lead roles and to their
users:
- Save this script as
can-edit-dashboards.sh, in the folder of your installation:# Adds can_edit_dashboards to the triager and security_lead roles, and to their users. login=$(printf %s "$OPENSEARCH_INITIAL_ADMIN_PASSWORD" | sed 's/[\\"]/\\&/g') curl -sSk -K - -X POST 'https://localhost:9200/system-roles,system-users/_update_by_query?refresh=true' \ -H 'content-type: application/json' -d '{ "query": {"terms": {"role": ["triager", "security_lead"]}}, "script": {"lang": "painless", "source": "def c = ctx._source.capabilities; if (c == null || c.contains(\"can_edit_dashboards\")) { ctx.op = \"noop\" } else { c.add(\"can_edit_dashboards\") }"} }' <<EOF user = "admin:$login" EOF echo - Run the script in the OpenSearch container:
The answer has
docker compose exec -T opensearch sh < can-edit-dashboards.sh"failures":[].updatedis the number of roles and users that the script changed. If you run the script again, it changes nothing.
The users do not sign in again: JAVV reads the permissions of a user on each request.
On Kubernetes, or with an OpenSearch of your own, send the same request to your OpenSearch, with its admin.
We sent the request of the script two times to OpenSearch 3.9.0, with the roles and users of 0.8.8. The first time changed the two roles and their users. The second time changed nothing.
0.8.7: JAVV refuses a username with a capital letter¶
A new username must be in lower case. Before 0.8.7, Alice and alice could be two different
users. Users that exist keep their names.
- New users.
POST /api/v1/admin/usersgives 422 for a username with a capital letter, with the messagea username must be in lower case. - The first admin. A
JAVV_BOOTSTRAP_ADMIN_USERNAMEwith a capital letter stops the backend at each start, also when the first admin exists. The backend showsinvalid settings: JAVV_BOOTSTRAP_ADMIN_USERNAME: Value error, a username must be in lower case. Then set the name in lower case. If no user has that name, and you setJAVV_BOOTSTRAP_ADMIN_PASSWORD, the backend makes a new first admin. The old admin stays. - Sign-in. A user can type a lower-case username in any case:
ALICEsigns in asalice. A user whose name has a capital letter, for exampleAlice, must type the name exactly. The sign-in lock counts the failures of all cases of a name together.
0.8.0: a bootstrap admin password longer than 256 characters stops the backend¶
A sign-in accepts a password of 256 characters at most. A first admin with a longer
JAVV_BOOTSTRAP_ADMIN_PASSWORD could never sign in. The backend now refuses such a value at start,
and shows invalid settings: JAVV_BOOTSTRAP_ADMIN_PASSWORD: String should have at most 256
characters. It does this also when the first admin exists, because the backend reads the setting
at each start.
If you see this error, set a password of 256 characters or fewer. A new value does not change the password of an admin that exists.
0.7.0: JAVV_TOKEN_PEPPER is now JAVV_SECRET_KEY¶
Only the name changes. Keep the same value. A backend that finds the old name stops at start, and it shows the new name. If you change the value:
- Each scanner gets 401 and needs a new token.
- Each user must sign in again.
-
Each open download link stops working.
-
docker compose: in
.env, add the new name with the same value. Keep the line with the old name until you do not need to go back to a release before 0.7.0:Until you add the new name,sed -n 's/^JAVV_TOKEN_PEPPER=/JAVV_SECRET_KEY=/p' .env >> .envdocker compose upstops withset JAVV_SECRET_KEY in .env. Thecompose.yamlof 0.7.0 and later gives onlyJAVV_SECRET_KEYto the backend. Thus the old name in.envhas no effect on it. - Helm, with the value in your values file: change the name
secrets.tokenPeppertosecrets.secretKey, and keep its value. Until you do this, the upgrade stops withadditional properties 'tokenPepper' not allowed. If you set the value with--set, write your values to a file first (see On Kubernetes (Helm)). Change the name inmy-javv-values.yaml. Then upgrade with-f my-javv-values.yaml. - Helm, with
secrets.existingSecret: the chart now reads the keysecret-keyfrom that Secret. Before the upgrade, copy the value oftoken-pepperinto it:Keep the keykubectl patch secret <your-secret> --type merge -p \ "{\"data\":{\"secret-key\":\"$(kubectl get secret <your-secret> -o jsonpath='{.data.token-pepper}')\"}}"token-pepperuntil you do not need to go back to a release before 0.7.0. Those releases read onlytoken-pepper. After that, you can delete it.
0.6.2: JAVV retires clusters that send no scans¶
A new daily job retires a cluster that sent no accepted scan for 45 days
(JAVV_CLUSTER_RETIRE_AFTER_DAYS). A retired cluster is not in the cluster list, in the cluster
selector or in All clusters. JAVV keeps all of its data and its tokens. The cluster comes back with
its next accepted scan. You can also bring it back manually:
POST /api/v1/clusters/{cluster_id}/unretire.
The first run (04:15 by default) retires each cluster that sent no scan for that time. An example is a cluster that you made again: it comes back with a new ID. To keep a cluster, do one of these steps:
- Set its period to never:
PUT /api/v1/settings/retirementwith itscluster_id,retire_after_days: null, andwarn_daysas before. - Set
JAVV_CLUSTER_RETIRE_AFTER_DAYS=0to stop retirement for all clusters.
When no cluster had an accepted scan within its scanner-down period, the job retires nothing, and it logs a warning. This condition shows a problem in JAVV, not in the clusters.
0.6.0: how the backend connects to OpenSearch¶
There are two changes, both on purpose:
- A
JAVV_OPENSEARCH_URLthat holds a user or a password stops the backend at start. Earlier releases senthttps://user:pass@host(oruser:pass@host, with no scheme) to the client. Move the credentials intoJAVV_OPENSEARCH_USERNAMEandJAVV_OPENSEARCH_PASSWORD(Connection to OpenSearch). - A wrong setting stops the start, with one line for each variable:
invalid settings: JAVV_<NAME>: <reason>. The line holds no value from your environment. Earlier releases showed part of the environment, and secrets could be in it.
The token command (python -m backend.core.tokens) and the scan-scope command now also use
JAVV_REQUEST_TIMEOUT, as the rest of the backend did before (30 seconds by default).