Secrets

In-memory credential store with AES-256-GCM encryption at rest.

Overview

@consensus-tools/secrets provides an in-memory encrypted credential store. Credentials are keyed by provider + keyName (e.g. slack / bot_token) and encrypted using a master secret derived via scrypt. Use it to store API tokens, webhook secrets, and other sensitive values without exposing plaintext.

Installation

pnpm add @consensus-tools/secrets

Quick start

import { CredentialManager } from "@consensus-tools/secrets";

const creds = new CredentialManager("my-master-secret");

// Store a credential (upsert semantics -- inserts or updates)
creds.upsert("slack", "bot_token", "xoxb-abc-123");

// Retrieve the decrypted value
const token = creds.get("slack", "bot_token");
// => "xoxb-abc-123"

API reference

CredentialManager

In-memory encrypted credential store. Constructor takes a secret: string which is derived internally via scrypt.

const creds = new CredentialManager(secret: string);

upsert(provider, keyName, value)

Insert or update a credential.

const result = creds.upsert("slack", "bot_token", "xoxb-abc-123");
// => { id: "cred_1", provider: "slack", keyName: "bot_token", updated: false }
  • provider string -- service name (e.g. "slack", "github")
  • keyName string -- credential identifier (e.g. "bot_token")
  • value string -- plaintext credential value
  • Returns { id, provider, keyName, updated } where updated is true on overwrite

get(provider, keyName)

Retrieve and decrypt a stored credential.

const token = creds.get("slack", "bot_token");
// => "xoxb-abc-123" or null
  • Returns string | null -- decrypted value, or null if missing or corrupt

list()

List all stored credentials without exposing values.

const all = creds.list();
// => [{ provider: "slack", keyName: "bot_token", createdAt: ..., updatedAt: ... }]

delete(provider, keyName)

Remove a credential. Returns true if removed.

creds.delete("slack", "bot_token");

getProviderStatus(provider)

Check which keys exist for a provider.

const status = creds.getProviderStatus("slack");
// => { bot_token: true }

encrypt(plaintext, key)

Low-level AES-256-GCM encryption. Returns a base64 string containing IV + tag + ciphertext. key must be a 32-byte Buffer.

import { encrypt, decrypt } from "@consensus-tools/secrets";
import crypto from "node:crypto";

const key = crypto.scryptSync("my-secret", "consensus-tools-salt", 32);
const ciphertext = encrypt("sensitive-value", key);

decrypt(encoded, key)

Low-level AES-256-GCM decryption. Takes a base64 string, returns plaintext.

const plaintext = decrypt(ciphertext, key);
// => "sensitive-value"

Master secret management

The master secret is used to derive the encryption key. If you lose it, all stored credentials become unrecoverable. Store the master secret securely outside the application.

Examples

Full credential lifecycle

import { CredentialManager } from "@consensus-tools/secrets";

const creds = new CredentialManager("my-master-secret");

// Store credentials for multiple providers
creds.upsert("slack", "bot_token", "xoxb-abc-123");
creds.upsert("github", "pat", "ghp_xxxxxxxxxxxx");

// List all (values never exposed)
const all = creds.list();
// => [{ provider: "slack", keyName: "bot_token", ... }, { provider: "github", keyName: "pat", ... }]

// Check provider status
const status = creds.getProviderStatus("slack");
// => { bot_token: true }

// Clean up
creds.delete("slack", "bot_token");