Skip to content

Keys and credentials ​

What exists ​

SecretWhereProtectsIf lost
Master key (32 bytes, hex or base64)Your vault → a compose secret → PULSE_MASTER_KEY_FILESeals keys.enc and the stored webhook secrets; derives the key that names backup chunkskeys.enc cannot be opened. Every client needs new credentials and every hook a new secret.
keys.enc/data/pulsed/keys.enc (and every backup)The NATS operator, the SYS and APP accounts and their signing keys, internal users, the internal CA, the ACME account keyRestore it from a backup. It is useless without the master key.
Client .credsEach tool's hostOne client's user JWT and seedRotate the client; the old creds are revoked.
HTTP tokens, inbound hook secrets, outbound signing secretsShown once in the dashboardOne token or hookRotate it.
age backup identityOffline onlyEvery backupOld backups cannot be restored.
Cloudflare, S3, Telegram and Discord tokensCompose secrets (*_FILE)Their own serviceIssue new ones at the provider.

pulsed never shows a seed or a token twice. It keeps users' public keys and revocations, and hashes of HTTP tokens.

The master key ​

Generate it once and keep it in your vault:

sh
openssl rand -hex 32

In the prod profile it must come from a file (PULSE_MASTER_KEY_FILE); the plain PULSE_MASTER_KEY variable is refused, as are plain variables for every other secret. It is never written to a volume and it is not in the backups. Without it, pulsed init and pulsed serve refuse to start; with the wrong one, they fail closed instead of generating new keys.

Keep an offline copy next to the age identity (a password manager entry and a printed or offline medium). For disaster recovery you need both: the master key to open keys.enc, and the age identity to decrypt the backup.

The key store ​

pulsed init creates keys.enc on first run and completes it on later runs (it is idempotent): the operator and its signing key, the SYS and APP accounts with their signing keys, pulsed's internal users, the internal CA and the ACME account key. The operator JWT is used in strict signing-key mode: accounts and users are signed by signing keys, never by the identity keys themselves.

Each tool client gets its own scoped signing key in the APP account, which carries that client's permissions. When you revoke a client, its user key is added to the APP account's revocation list and pulsed pushes the updated account to NATS.

Client credentials ​

  • Created on the Clients page or with pulse clients create. A new client has no permissions until you add grants (clients and grants).
  • They expire after 365 days by default; the client_expiry alert fires 30 days before.
  • Rotate issues new creds and revokes the old user key. Revoke is permanent.
  • Store .creds files with mode 0600, readable only by the tool's user.

Inspecting or moving the key set ​

sh
pulsed keys export --yes-i-understand

prints the decrypted key set as JSON. pulsed must be stopped (the command takes the database lock to write an audit record first). Use it only to move to other tooling. To move Pulse to a new server, restore keys.enc from a backup and use the same master key (backup and restore).

Released under the Apache-2.0 license.