Skip to content

Almacena y recupera credenciales sensibles de forma segura usando las APIs nativas de almacenamiento de credenciales del sistema operativo.

typescript
import { secrets } from "bun";

let githubToken: string | null = await secrets.get({
  service: "my-cli-tool",
  name: "github-token",
});

if (!githubToken) {
  githubToken = prompt("Por favor ingresa tu token de GitHub");

  await secrets.set({
    service: "my-cli-tool",
    name: "github-token",
    value: githubToken,
  });

  console.log("Token de GitHub almacenado");
}

const response = await fetch("https://api.github.com/user", {
  headers: { Authorization: `token ${githubToken}` },
});

console.log(`Conectado como ${(await response.json()).login}`);

Visión General

Bun.secrets proporciona una API multiplataforma para gestionar credenciales sensibles que las herramientas CLI y aplicaciones de desarrollo típicamente almacenan en archivos de texto plano como ~/.npmrc, ~/.aws/credentials, o archivos .env. Usa:

  • macOS: Servicios de Keychain
  • Linux: libsecret (GNOME Keyring, KWallet, etc.)
  • Windows: Windows Credential Manager

Todas las operaciones son asíncronas y no bloqueantes, ejecutándose en el threadpool de Bun.

NOTE

En el futuro, podríamos agregar una opción `provider` adicional para hacer esto más útil para secretos de implementación en producción, pero hoy esta API es principalmente útil para herramientas de desarrollo local.

API

Bun.secrets.get(options)

Recupera una credencial almacenada.

typescript
import { secrets } from "bun";

const password = await Bun.secrets.get({
  service: "my-app",
  name: "alice@example.com",
});
// Devuelve: string | null

// O si prefieres sin un objeto
const password = await Bun.secrets.get("my-app", "alice@example.com");

Parámetros:

  • options.service (string, requerido) - El servicio o nombre de aplicación
  • options.name (string, requerido) - El nombre de usuario o identificador de cuenta

Devuelve:

  • Promise<string | null> - La contraseña almacenada, o null si no se encuentra

Bun.secrets.set(options, value)

Almacena o actualiza una credencial.

typescript
import { secrets } from "bun";

await secrets.set({
  service: "my-app",
  name: "alice@example.com",
  value: "super-secret-password",
});

Parámetros:

  • options.service (string, requerido) - El servicio o nombre de aplicación
  • options.name (string, requerido) - El nombre de usuario o identificador de cuenta
  • value (string, requerido) - La contraseña o secreto a almacenar

Notas:

  • Si ya existe una credencial para la combinación service/name dada, será reemplazada
  • El valor almacenado está encriptado por el sistema operativo

Bun.secrets.delete(options)

Elimina una credencial almacenada.

typescript
const deleted = await Bun.secrets.delete({
  service: "my-app",
  name: "alice@example.com",
  value: "super-secret-password",
});
// Devuelve: boolean

Parámetros:

  • options.service (string, requerido) - El servicio o nombre de aplicación
  • options.name (string, requerido) - El nombre de usuario o identificador de cuenta

Devuelve:

  • Promise<boolean> - true si se eliminó una credencial, false si no se encontró

Ejemplos

Almacenar Credenciales de Herramientas CLI

javascript
// Almacenar token de GitHub CLI (en lugar de ~/.config/gh/hosts.yml)
await Bun.secrets.set({
  service: "my-app.com",
  name: "github-token",
  value: "ghp_xxxxxxxxxxxxxxxxxxxx",
});

// O si prefieres sin un objeto
await Bun.secrets.set("my-app.com", "github-token", "ghp_xxxxxxxxxxxxxxxxxxxx");

// Almacenar token del registro npm (en lugar de ~/.npmrc)
await Bun.secrets.set({
  service: "npm-registry",
  name: "https://registry.npmjs.org",
  value: "npm_xxxxxxxxxxxxxxxxxxxx",
});

// Recuperar para llamadas a API
const token = await Bun.secrets.get({
  service: "gh-cli",
  name: "github.com",
});

if (token) {
  const response = await fetch("https://api.github.com/name", {
    headers: {
      Authorization: `token ${token}`,
    },
  });
}

Migrar desde Archivos de Configuración de Texto Plano

javascript
// En lugar de almacenar en ~/.aws/credentials
await Bun.secrets.set({
  service: "aws-cli",
  name: "AWS_SECRET_ACCESS_KEY",
  value: process.env.AWS_SECRET_ACCESS_KEY,
});

// En lugar de archivos .env con datos sensibles
await Bun.secrets.set({
  service: "my-app",
  name: "api-key",
  value: "sk_live_xxxxxxxxxxxxxxxxxxxx",
});

// Cargar en tiempo de ejecución
const apiKey =
  (await Bun.secrets.get({
    service: "my-app",
    name: "api-key",
  })) || process.env.API_KEY; // Fallback para CI/producción

Manejo de Errores

javascript
try {
  await Bun.secrets.set({
    service: "my-app",
    name: "alice",
    value: "password123",
  });
} catch (error) {
  console.error("Error al almacenar credencial:", error.message);
}

// Verificar si existe una credencial
const password = await Bun.secrets.get({
  service: "my-app",
  name: "alice",
});

if (password === null) {
  console.log("No se encontró credencial");
}

Actualizar Credenciales

javascript
// Contraseña inicial
await Bun.secrets.set({
  service: "email-server",
  name: "admin@example.com",
  value: "old-password",
});

// Actualizar a nueva contraseña
await Bun.secrets.set({
  service: "email-server",
  name: "admin@example.com",
  value: "new-password",
});

// La contraseña anterior es reemplazada

Comportamiento por Plataforma

macOS (Keychain)

  • Las credenciales se almacenan en el keychain de inicio de sesión del nombre
  • El keychain puede solicitar permiso de acceso en el primer uso
  • Las credenciales persisten a través de reinicios del sistema
  • Accesibles por el nombre que las almacenó

Linux (libsecret)

  • Requiere un daemon de servicio de secretos (GNOME Keyring, KWallet, etc.)
  • Las credenciales se almacenan en la colección predeterminada
  • Puede solicitar desbloqueo si el keyring está bloqueado
  • El servicio de secretos debe estar en ejecución

Windows (Credential Manager)

  • Las credenciales se almacenan en Windows Credential Manager
  • Visibles en Panel de Control → Administrador de Credenciales → Credenciales de Windows
  • Persisten con la bandera CRED_PERSIST_ENTERPRISE así que está limitado por usuario
  • Encriptadas usando Windows Data Protection API

Consideraciones de Seguridad

  1. Encriptación: Las credenciales están encriptadas por el administrador de credenciales del sistema operativo
  2. Control de Acceso: Solo el nombre que almacenó la credencial puede recuperarla
  3. Sin Texto Plano: Las contraseñas nunca se almacenan en texto plano
  4. Seguridad de Memoria: Bun borra la memoria de contraseña después del uso
  5. Aislamiento de Proceso: Las credenciales están aisladas por nombre de cuenta

Limitaciones

  • La longitud máxima de contraseña varía por plataforma (típicamente 2048-4096 bytes)
  • Los nombres de service y name deben tener longitudes razonables (< 256 caracteres)
  • Algunos caracteres especiales pueden necesitar escape dependiendo de la plataforma
  • Requiere servicios del sistema apropiados:
    • Linux: El daemon de servicio de secretos debe estar en ejecución
    • macOS: Keychain Access debe estar disponible
    • Windows: El servicio Credential Manager debe estar habilitado

Comparación con Variables de Entorno

A diferencia de las variables de entorno, Bun.secrets:

  • ✅ Encripta credenciales en reposo (gracias al sistema operativo)
  • ✅ Evita exponer secretos en volcados de memoria de proceso (la memoria se borra después de que ya no es necesaria)
  • ✅ Sobrevive a reinicios de aplicación
  • ✅ Se puede actualizar sin reiniciar la aplicación
  • ✅ Proporciona control de acceso a nivel de nombre
  • ❌ Requiere servicio de credenciales del SO
  • ❌ No es muy útil para secretos de implementación (usa variables de entorno en producción)

Mejores Prácticas

  1. Usa nombres de servicio descriptivos: Coincide con el nombre de la herramienta o aplicación Si estás construyendo un CLI para uso externo, probablemente deberías usar un UTI (Uniform Type Identifier) para el nombre de servicio.

    javascript
    // Bueno - coincide con la herramienta real
    { service: "com.docker.hub", name: "username" }
    { service: "com.vercel.cli", name: "team-name" }
    
    // Evitar - demasiado genérico
    { service: "api", name: "key" }
  2. Solo credenciales: No almacenes configuración de aplicación en esta API Esta API es lenta, probablemente aún necesites usar un archivo de configuración para algunas cosas.

  3. Usa para herramientas de desarrollo local:

    • ✅ Herramientas CLI (gh, npm, docker, kubectl)
    • ✅ Servidores de desarrollo local
    • ✅ Claves de API personales para testing
    • ❌ Servidores de producción (usa gestión de secretos apropiada)

TypeScript

typescript
namespace Bun {
  interface SecretsOptions {
    service: string;
    name: string;
  }

  interface Secrets {
    get(options: SecretsOptions): Promise<string | null>;
    set(options: SecretsOptions, value: string): Promise<void>;
    delete(options: SecretsOptions): Promise<boolean>;
  }

  const secrets: Secrets;
}

Bun por www.bunjs.com.cn editar