Armazene e recupere credenciais sensíveis de forma segura usando as APIs nativas de armazenamento de credenciais do sistema operacional.
import { secrets } from "bun";
let githubToken: string | null = await secrets.get({
service: "my-cli-tool",
name: "github-token",
});
if (!githubToken) {
githubToken = prompt("Por favor, insira seu token do GitHub");
await secrets.set({
service: "my-cli-tool",
name: "github-token",
value: githubToken,
});
console.log("Token do GitHub armazenado");
}
const response = await fetch("https://api.github.com/user", {
headers: { Authorization: `token ${githubToken}` },
});
console.log(`Logado como ${(await response.json()).login}`);Visão Geral
Bun.secrets fornece uma API multiplataforma para gerenciar credenciais sensíveis que ferramentas CLI e aplicações de desenvolvimento tipicamente armazenam em arquivos de texto simples como ~/.npmrc, ~/.aws/credentials ou arquivos .env. Ele usa:
- macOS: Keychain Services
- Linux: libsecret (GNOME Keyring, KWallet, etc.)
- Windows: Windows Credential Manager
Todas as operações são assíncronas e não bloqueantes, executando no threadpool do Bun.
NOTE
No futuro, podemos adicionar uma opção `provider` adicional para tornar isso melhor para segredos de implantação em produção, mas hoje esta API é principalmente útil para ferramentas de desenvolvimento local.API
Bun.secrets.get(options)
Recupera uma credencial armazenada.
import { secrets } from "bun";
const password = await Bun.secrets.get({
service: "my-app",
name: "alice@example.com",
});
// Retorna: string | null
// Ou se preferir sem um objeto
const password = await Bun.secrets.get("my-app", "alice@example.com");Parâmetros:
options.service(string, obrigatório) - O serviço ou nome da aplicaçãooptions.name(string, obrigatório) - O nome de usuário ou identificador da conta
Retorna:
Promise<string | null>- A senha armazenada, ounullse não encontrada
Bun.secrets.set(options, value)
Armazena ou atualiza uma credencial.
import { secrets } from "bun";
await secrets.set({
service: "my-app",
name: "alice@example.com",
value: "super-secret-password",
});Parâmetros:
options.service(string, obrigatório) - O serviço ou nome da aplicaçãooptions.name(string, obrigatório) - O nome de usuário ou identificador da contavalue(string, obrigatório) - A senha ou segredo a armazenar
Notas:
- Se uma credencial já existir para a combinação service/name dada, ela será substituída
- O valor armazenado é criptografado pelo sistema operacional
Bun.secrets.delete(options)
Exclui uma credencial armazenada.
const deleted = await Bun.secrets.delete({
service: "my-app",
name: "alice@example.com",
value: "super-secret-password",
});
// Retorna: booleanParâmetros:
options.service(string, obrigatório) - O serviço ou nome da aplicaçãooptions.name(string, obrigatório) - O nome de usuário ou identificador da conta
Retorna:
Promise<boolean>-truese uma credencial foi excluída,falsese não encontrada
Exemplos
Armazenando Credenciais de Ferramenta CLI
// Armazena token do GitHub CLI (em vez de ~/.config/gh/hosts.yml)
await Bun.secrets.set({
service: "my-app.com",
name: "github-token",
value: "ghp_xxxxxxxxxxxxxxxxxxxx",
});
// Ou se preferir sem um objeto
await Bun.secrets.set("my-app.com", "github-token", "ghp_xxxxxxxxxxxxxxxxxxxx");
// Armazena token do registro npm (em vez de ~/.npmrc)
await Bun.secrets.set({
service: "npm-registry",
name: "https://registry.npmjs.org",
value: "npm_xxxxxxxxxxxxxxxxxxxx",
});
// Recupera para chamadas de 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}`,
},
});
}Migrando de Arquivos de Configuração em Texto Simples
// Em vez de armazenar em ~/.aws/credentials
await Bun.secrets.set({
service: "aws-cli",
name: "AWS_SECRET_ACCESS_KEY",
value: process.env.AWS_SECRET_ACCESS_KEY,
});
// Em vez de arquivos .env com dados sensíveis
await Bun.secrets.set({
service: "my-app",
name: "api-key",
value: "sk_live_xxxxxxxxxxxxxxxxxxxx",
});
// Carrega em tempo de execução
const apiKey =
(await Bun.secrets.get({
service: "my-app",
name: "api-key",
})) || process.env.API_KEY; // Fallback para CI/produçãoTratamento de Erros
try {
await Bun.secrets.set({
service: "my-app",
name: "alice",
value: "password123",
});
} catch (error) {
console.error("Falha ao armazenar credencial:", error.message);
}
// Verifica se uma credencial existe
const password = await Bun.secrets.get({
service: "my-app",
name: "alice",
});
if (password === null) {
console.log("Nenhuma credencial encontrada");
}Atualizando Credenciais
// Senha inicial
await Bun.secrets.set({
service: "email-server",
name: "admin@example.com",
value: "old-password",
});
// Atualiza para nova senha
await Bun.secrets.set({
service: "email-server",
name: "admin@example.com",
value: "new-password",
});
// A senha antiga é substituídaComportamento por Plataforma
macOS (Keychain)
- Credenciais são armazenadas no keychain de login do usuário
- O keychain pode solicitar permissão de acesso no primeiro uso
- Credenciais persistem através de reinicializações do sistema
- Acessível pelo usuário que as armazenou
Linux (libsecret)
- Requer um daemon de serviço de segredos (GNOME Keyring, KWallet, etc.)
- Credenciais são armazenadas na coleção padrão
- Pode solicitar desbloqueio se o keyring estiver bloqueado
- O serviço de segredos deve estar em execução
Windows (Credential Manager)
- Credenciais são armazenadas no Windows Credential Manager
- Visível em Painel de Controle → Credential Manager → Windows Credentials
- Persiste com flag
CRED_PERSIST_ENTERPRISEentão é escopo por usuário - Criptografado usando Windows Data Protection API
Considerações de Segurança
- Criptografia: Credenciais são criptografadas pelo gerenciador de credenciais do sistema operacional
- Controle de Acesso: Apenas o usuário que armazenou a credencial pode recuperá-la
- Sem Texto Simples: Senhas nunca são armazenadas em texto simples
- Segurança de Memória: Bun limpa a memória da senha após o uso
- Isolamento de Processo: Credenciais são isoladas por conta de usuário
Limitações
- Comprimento máximo de senha varia por plataforma (tipicamente 2048-4096 bytes)
- Nomes de service e name devem ter comprimentos razoáveis (< 256 caracteres)
- Alguns caracteres especiais podem precisar de escape dependendo da plataforma
- Requer serviços apropriados do sistema:
- Linux: Daemon de serviço de segredos deve estar em execução
- macOS: Keychain Access deve estar disponível
- Windows: Serviço Credential Manager deve estar habilitado
Comparação com Variáveis de Ambiente
Ao contrário de variáveis de ambiente, Bun.secrets:
- ✅ Criptografa credenciais em repouso (graças ao sistema operacional)
- ✅ Evita expor segredos em dumps de memória de processo (memória é zerada após não ser mais necessária)
- ✅ Sobrevive a reinicializações de aplicação
- ✅ Pode ser atualizado sem reiniciar a aplicação
- ✅ Fornece controle de acesso em nível de usuário
- ❌ Requer serviço de credenciais do SO
- ❌ Não é muito útil para segredos de implantação (use variáveis de ambiente em produção)
Melhores Práticas
Use nomes de serviço descritivos: Correspondam ao nome da ferramenta ou aplicação Se você estiver construindo uma CLI para uso externo, provavelmente deve usar um UTI (Uniform Type Identifier) para o nome do serviço.
javascript// Bom - corresponde à ferramenta real { service: "com.docker.hub", name: "username" } { service: "com.vercel.cli", name: "team-name" } // Evite - muito genérico { service: "api", name: "key" }Apenas credenciais: Não armazene configuração de aplicação nesta API Esta API é lenta, você provavelmente ainda precisa usar um arquivo de configuração para algumas coisas.
Use para ferramentas de desenvolvimento local:
- ✅ Ferramentas CLI (gh, npm, docker, kubectl)
- ✅ Servidores de desenvolvimento local
- ✅ Chaves de API pessoais para teste
- ❌ Servidores de produção (use gerenciamento de segredos adequado)
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;
}