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