node-red-contrib-telegrambot
Version:
Telegram bot nodes for Node-RED
112 lines (88 loc) • 4.92 kB
Markdown
# Automated / git-managed deployments
This document covers what you need to do when your Node-RED flows live in source
control (or are otherwise managed by automation) and your bot's token is supposed
to flow in from an environment variable or a secrets store rather than being typed
into the editor by hand.
It is also the answer to issue [#432](https://github.com/windkh/node-red-contrib-telegrambot/issues/432).
## Why this needs special handling
The bot's `token` field is declared to Node-RED as a **credential**, not a regular
config property. Node-RED treats credentials specially:
- It strips them out of `flows.json` on save.
- It stores their encrypted form in a separate `flows_cred.json` file.
- The encryption key is `credentialSecret` from `settings.js`. If
`credentialSecret` isn't pinned in `settings.js`, Node-RED generates one and
stores it in `.config.runtime.json`.
So if your automated deployment commits `flows.json` and uses a fresh
`credentialSecret` on every container start, the token will appear to "vanish" —
you'll see the bot abort with `Aborting: Token of <botname> is not set`.
The plugin's `{env.get("X")}` / `{flow.get("X")}` / `{global.get("X")}`
expressions are evaluated **at runtime, inside the credential resolution path**.
They work — but only once the encrypted credential has been re-read from
`flows_cred.json`. The expression has to survive the credential-serialisation
step first.
## Pattern 1 — pin `credentialSecret` and commit `flows_cred.json`
The most robust pattern. Pick a deterministic secret per environment and tell
Node-RED to use it via `settings.js`:
```js
// settings.js
module.exports = {
credentialSecret: process.env.NODE_RED_CREDENTIAL_SECRET || '<a-strong-fallback>',
// ...
};
```
Then commit both `flows.json` **and** `flows_cred.json` to your repo. The token
field can be a literal value or a `{env.get("...")}` expression — either
survives, because Node-RED re-decrypts `flows_cred.json` with the same secret on
every boot.
If you want to keep the token out of the encrypted file too, put the
`{env.get("TG_TOKEN")}` expression in the token field before committing. The
plugin will resolve it at runtime against whatever `TG_TOKEN` the container
inherits from its environment.
## Pattern 2 — write `flows_cred.json` at boot
If you don't want to commit any credential material, generate
`flows_cred.json` from your secrets store as the container starts. A small
entrypoint script that reads from Vault / SOPS / Doppler / AWS Secrets Manager /
... and writes the file to `$HOME/.node-red/flows_cred.json` before launching
Node-RED works fine. `credentialSecret` still needs to match what was used to
encrypt it.
## Pattern 3 — `userDir` and `flowFile` overrides
The `userDir` and `flowFile` settings let you point Node-RED at any directory.
A common pattern is to keep `flows.json` in `/etc/node-red/flows.json`
(read-only, baked into the image) and have Node-RED write `flows_cred.json` to
a writable volume.
## What `{env.get(...)}` does and doesn't fix
Once the token expression makes it past Node-RED's credential serialisation,
the plugin's expression evaluator (see
[ADR 0004](doc/architecture/adr/0004-safe-expression-evaluator.md)) handles
the supported forms:
```
{env.get("TG_TOKEN")}
{flow.get("token")}
{global.get("token")}
{context.get("token")}
{context.flow.get("token")}
{context.global.get("token")}
```
`usernames` and `chatids` use the same syntax. As of V17.3.1 a raw string
result from `env.get` / `flow.get` / `global.get` is split on commas — so
`CHATIDS=123,456` resolves to `[123, 456]`.
Anything outside that grammar (function calls, statement chains, prototype
walks, `require`, `eval`, etc.) evaluates to `undefined` — see
[ADR 0004](doc/architecture/adr/0004-safe-expression-evaluator.md) for the
exact whitelist.
## Common pitfalls
- **`credentialSecret` is not set in `settings.js`.** Node-RED generates a random
one on first start and writes it to `.config.runtime.json`. Subsequent
container restarts that lose that file will be unable to decrypt the existing
`flows_cred.json`. The bot will then abort with "token is not set".
- **`credentialSecret` is rotated.** Same effect — old `flows_cred.json` can no
longer be decrypted. Re-enter the token via the editor (or delete
`flows_cred.json` and let Node-RED re-create it on save).
- **`flows.json` is regenerated by a flow-export step.** Make sure your export
pipeline preserves the credential references (it normally just strips the
values, which is correct), and doesn't accidentally write back the bot config
node with `credentials: {}` cleared.
- **Multiple bot config nodes share the same token.** The plugin's
`botsByToken` registry aborts the second one with
"Token of <botname> is already in use by ...". Use one config node per token
and reference it from multiple runtime nodes.