Skip to content

Secure Enclave

Encrypted at-rest secret storage for API keys, OAuth tokens, and other sensitive credentials. Secrets are encrypted with AES-256-GCM and stored per-user in the Lumiverse secrets database — they are never written to disk as plaintext.

No permission is required (free tier).

For user-scoped extensions, the userId is inferred automatically. For operator-scoped extensions, you must pass userId explicitly.

Usage

// Store a secret
await spindle.enclave.put('spotify_token', accessToken, userId)

// Retrieve a secret
const token = await spindle.enclave.get('spotify_token', userId)
if (token) {
  // use the token
}

// Check if a secret exists (without retrieving it)
const hasToken = await spindle.enclave.has('spotify_token', userId)

// List all secret keys for this extension
const keys = await spindle.enclave.list(userId)
// -> ['spotify_token', 'refresh_token', 'client_secret']

// Delete a secret
await spindle.enclave.delete('spotify_token', userId)

Methods

Method Returns Description
put(key, value, userId?) Promise<void> Store or update an encrypted secret
get(key, userId?) Promise<string \| null> Retrieve a decrypted secret, or null if not found
delete(key, userId?) Promise<boolean> Delete a secret. Returns true if it existed
has(key, userId?) Promise<boolean> Check if a secret exists (without decrypting)
list(userId?) Promise<string[]> List all secret keys for this extension and user

Key Constraints

  • Pattern: ^[a-zA-Z0-9_\-.]{1,128}$ — alphanumeric, underscore, dash, and dot only
  • Max length: 128 characters

Value Constraints

  • Must be a string
  • Max size: 64 KB
  • Allowed characters: printable ASCII (0x20-0x7E) plus \t, \n, \r — no binary or control characters

Namespacing

Keys are automatically namespaced as spindle:{identifier}:{key} in the underlying secrets table. Extensions cannot read each other's secrets. The list() method only returns the bare key names (without the namespace prefix).

When to Use Enclave vs. Storage

Use case Use
OAuth tokens, API keys, client secrets spindle.enclave
User preferences, UI state, cached data spindle.userStorage or spindle.storage
Shared config for all users (operator-scoped) spindle.storage
Per-user config (operator-scoped) spindle.userStorage