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 configurationThis 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.
| What | Where to change it | |
|---|---|---|
| Application accounts | Accounts people log in with | Managed from each application's admin screen |
| Infrastructure and API keys | DB accounts, service-to-service authentication keys | This document — bin/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.
| Key | What | How it changes |
|---|---|---|
DOCKER_NEO4J_PASSWORD | Neo4j (graph storage) | The server-side account is authoritative — ALTER USER (cypher-shell) |
RAG_API_KEY | RAG API key — shared by copliot-web, lightrag, and rag-api | File |
VLLM_API_KEY | vLLM API key — shared by lightrag and insight-api | File |
LITELLM_MASTER_KEY | LiteLLM master key | File |
LITELLM_SALT_KEY | LiteLLM salt key | File |
PP_INBOUND_API_KEY | Backend inbound key — shared by rag, insight, parser, and vision | File |
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.
PP_INBOUND_API_KEY emptyThe 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 hereIt 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.
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.
| Order | Action |
|---|---|
| 1 | Server-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) |
| 3 | docker 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
| Where | State | What to do |
|---|---|---|
| Step 1 (Neo4j account change) | Aborts without touching the file | Verify neo4j is up with bin/status.sh, then run again |
| Step 3 (restart) | The file already holds the new value | Check 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.
Related documents
- Environment Variable Configuration — full list of configuration variables
- Service Operations — start, stop, status check
- Database Administration — Neo4j, PostgreSQL, and Qdrant operations