Skip to main content

Changing Passwords and API Keys

PlantPulse AI uses a graph storage (Neo4j) account plus a handful of API keys that tie the internal services together. The initial values are identical across every installation, so change them once during commissioning and be done. After that, there is nothing to touch.

There is exactly one tool for this: bin/passwd.sh. All three products — platform, ai, studio — ship it under the same name with the same usage.

/etc/kopens/plantpulse-ai.env is the single source of truth for configuration

This product's secrets live in /etc/kopens/plantpulse-ai.env. That is outside the repo tree, not inside the installation directory, and its permissions are 0600.

compose/.env is the old path. Nothing reads it anymore — editing it will not change the stack.

Before 2026-08-16, passwd.sh wrote to /etc/kopens/plantpulse-ai.env while the ten scripts that bring the stack up read compose/.env. Rotation was therefore silently ineffective: passwd.sh reported success while the services kept running on the old keys. The read path and the write path are now the same single file. Do not go back to the old path.


This is a separate matter from application accounts

Do not conflate the two.

WhatWhere to change it
Application accountsAccounts people log in withManaged from each application's admin screen
Infrastructure and API keysDB accounts, service-to-service authentication keysThis documentbin/passwd.sh

This document covers only the latter: infrastructure credentials.


What can be changed

Start with the list. Running the command with no arguments gives the same result.

cd /opt/kopens/plantpulse-ai-docker
bin/passwd.sh --list

You get the key, the ID, the current value (first 3 characters only), the target, and where that value lives. To see the full value, use --list --show.

KeyWhatHow it changes
DOCKER_NEO4J_PASSWORDNeo4j (graph storage)The server-side account is authoritative — ALTER USER (cypher-shell)
RAG_API_KEYRAG API key — shared by copliot-web, lightrag, and rag-apiFile
VLLM_API_KEYvLLM API key — shared by lightrag and insight-apiFile
LITELLM_MASTER_KEYLiteLLM master keyFile
LITELLM_SALT_KEYLiteLLM salt keyFile
PP_INBOUND_API_KEYBackend inbound key — shared by rag, insight, parser, and visionFile

Key names are exactly the environment variable names. There are no aliases such as neo4j or rag — an alias would require a separate table mapping names to variables, and that table is guaranteed to drift from reality.

Never leave PP_INBOUND_API_KEY empty

The four backend services (rag, insight, parser, vision) let requests through without authentication when this key is empty. Clearing the value is the same as unlocking the door. Always set a new value when changing it, and confirm with --list that the value took effect.

DOCKER_PLATFORM_* is not here

It is a copy of the platform repository credential, so it cannot be changed from this product (there is no permission to change the server side). It is not in the list, and passing it is rejected the same way a typo would be. Change it on the platform side with bin/passwd.sh, then align this product's value.


Making the change

The recommended path is to omit the value and enter it at the prompt. The password then never appears in ps output or in shell history. You are prompted twice and the two entries are checked against each other.

bin/passwd.sh PP_INBOUND_API_KEY

You can also pass the value directly as an argument.

bin/passwd.sh RAG_API_KEY=<new-value>

Passing several at once collapses the restart into a single one. Use this form during commissioning.

bin/passwd.sh VLLM_API_KEY=<new-value> LITELLM_MASTER_KEY=<new-value>

To see only what would change without touching anything, use --dry-run.

bin/passwd.sh --dry-run RAG_API_KEY=<new-value>

When run, it first shows the items to be changed and their impact, then asks for confirmation with [y/N]. To skip confirmation in automation, use --yes.

The value cannot be omitted in non-interactive mode

In environments without a terminal, such as scripts or cron, omitting the value means no prompt can be shown and the command fails. Pass it in KEY=<value> form instead.


What happens when it runs

The order is fixed.

OrderAction
1Server-authoritative items first — for DOCKER_NEO4J_PASSWORD, the account is changed inside Neo4j first
2/etc/kopens/plantpulse-ai.env is updated with the new value (permissions 0600 preserved)
3docker compose up -d --wait — services are recreated so they pick up the new value

The server side changes first, then the file is brought into line. Reversing the order leaves the server on the old value while the file holds the new one, so authentication stays broken until the restart.

The AI path is down during this window. Perform it inside an inspection window.

If it fails partway through

WhereStateWhat to do
Step 1 (Neo4j account change)Aborts without touching the fileVerify neo4j is up with bin/status.sh, then run again
Step 3 (restart)The file already holds the new valueCheck status with bin/status.sh and resolve the startup failure

Verification

bin/passwd.sh --list

If the first 3 characters of the current value have changed, it took effect. Those 3 characters are kept visible so you can tell at a glance whether a default is still in place.