Archivia e recupera credenziali sensibili in modo sicuro usando le API native di archiviazione credenziali del sistema operativo.
import { secrets } from "bun";
let githubToken: string | null = await secrets.get({
service: "my-cli-tool",
name: "github-token",
});
if (!githubToken) {
githubToken = prompt("Inserisci il tuo token GitHub");
await secrets.set({
service: "my-cli-tool",
name: "github-token",
value: githubToken,
});
console.log("Token GitHub archiviato");
}
const response = await fetch("https://api.github.com/user", {
headers: { Authorization: `token ${githubToken}` },
});
console.log(`Accesso effettuato come ${(await response.json()).login}`);Panoramica
Bun.secrets fornisce un'API cross-platform per gestire credenziali sensibili che gli strumenti CLI e le applicazioni di sviluppo tipicamente archiviano in file di testo in chiaro come ~/.npmrc, ~/.aws/credentials, o file .env. Usa:
- macOS: Keychain Services
- Linux: libsecret (GNOME Keyring, KWallet, ecc.)
- Windows: Windows Credential Manager
Tutte le operazioni sono asincrone e non bloccanti, eseguite sul threadpool di Bun.
NOTE
In futuro, potremmo aggiungere un'opzione `provider` aggiuntiva per renderlo migliore per i segreti di distribuzione in produzione, ma oggi questa API è per lo più utile per gli strumenti di sviluppo locale.API
Bun.secrets.get(options)
Recupera una credenziale archiviata.
import { secrets } from "bun";
const password = await Bun.secrets.get({
service: "my-app",
name: "alice@example.com",
});
// Restituisce: string | null
// Oppure se preferisci senza un oggetto
const password = await Bun.secrets.get("my-app", "alice@example.com");Parametri:
options.service(string, richiesto) - Il nome del servizio o dell'applicazioneoptions.name(string, richiesto) - Il nome utente o identificativo account
Restituisce:
Promise<string | null>- La password archiviata, onullse non trovata
Bun.secrets.set(options, value)
Archivia o aggiorna una credenziale.
import { secrets } from "bun";
await secrets.set({
service: "my-app",
name: "alice@example.com",
value: "super-secret-password",
});Parametri:
options.service(string, richiesto) - Il nome del servizio o dell'applicazioneoptions.name(string, richiesto) - Il nome utente o identificativo accountvalue(string, richiesto) - La password o segreto da archiviare
Note:
- Se una credenziale esiste già per la combinazione service/name data, verrà sostituita
- Il valore archiviato è crittografato dal sistema operativo
Bun.secrets.delete(options)
Elimina una credenziale archiviata.
const deleted = await Bun.secrets.delete({
service: "my-app",
name: "alice@example.com",
value: "super-secret-password",
});
// Restituisce: booleanParametri:
options.service(string, richiesto) - Il nome del servizio o dell'applicazioneoptions.name(string, richiesto) - Il nome utente o identificativo account
Restituisce:
Promise<boolean>-truese una credenziale è stata eliminata,falsese non trovata
Esempi
Archiviazione Credenziali Strumenti CLI
// Archivia token GitHub CLI (invece di ~/.config/gh/hosts.yml)
await Bun.secrets.set({
service: "my-app.com",
name: "github-token",
value: "ghp_xxxxxxxxxxxxxxxxxxxx",
});
// Oppure se preferisci senza un oggetto
await Bun.secrets.set("my-app.com", "github-token", "ghp_xxxxxxxxxxxxxxxxxxxx");
// Archivia token registry npm (invece di ~/.npmrc)
await Bun.secrets.set({
service: "npm-registry",
name: "https://registry.npmjs.org",
value: "npm_xxxxxxxxxxxxxxxxxxxx",
});
// Recupera per chiamate 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}`,
},
});
}Migrazione da File di Configurazione in Testo Chiaro
// Invece di archiviare in ~/.aws/credentials
await Bun.secrets.set({
service: "aws-cli",
name: "AWS_SECRET_ACCESS_KEY",
value: process.env.AWS_SECRET_ACCESS_KEY,
});
// Invece di file .env con dati sensibili
await Bun.secrets.set({
service: "my-app",
name: "api-key",
value: "sk_live_xxxxxxxxxxxxxxxxxxxx",
});
// Carica a runtime
const apiKey =
(await Bun.secrets.get({
service: "my-app",
name: "api-key",
})) || process.env.API_KEY; // Fallback per CI/produzioneGestione Errori
try {
await Bun.secrets.set({
service: "my-app",
name: "alice",
value: "password123",
});
} catch (error) {
console.error("Archiviazione credenziale fallita:", error.message);
}
// Controlla se una credenziale esiste
const password = await Bun.secrets.get({
service: "my-app",
name: "alice",
});
if (password === null) {
console.log("Nessuna credenziale trovata");
}Aggiornamento Credenziali
// Password iniziale
await Bun.secrets.set({
service: "email-server",
name: "admin@example.com",
value: "old-password",
});
// Aggiorna a nuova password
await Bun.secrets.set({
service: "email-server",
name: "admin@example.com",
value: "new-password",
});
// La vecchia password è sostituitaComportamento per Piattaforma
macOS (Keychain)
- Le credenziali sono archiviate nel portachiavi di accesso dell'utente
- Il portachiavi può richiedere il permesso di accesso al primo utilizzo
- Le credenziali persistono attraverso i riavvii del sistema
- Accessibile dall'utente che le ha archiviate
Linux (libsecret)
- Richiede un demone servizio segreti (GNOME Keyring, KWallet, ecc.)
- Le credenziali sono archiviate nella raccolta predefinita
- Può richiedere lo sblocco se il portachiavi è bloccato
- Il servizio segreti deve essere in esecuzione
Windows (Credential Manager)
- Le credenziali sono archiviate in Windows Credential Manager
- Visibili in Pannello di controllo → Credential Manager → Credenziali Windows
- Persistono con il flag
CRED_PERSIST_ENTERPRISEquindi sono limitate per utente - Crittografate usando Windows Data Protection API
Considerazioni sulla Sicurezza
- Crittografia: Le credenziali sono crittografate dal gestore credenziali del sistema operativo
- Controllo Accessi: Solo l'utente che ha archiviato la credenziale può recuperarla
- Nessun Testo Chiaro: Le password non sono mai archiviate in testo chiaro
- Sicurezza Memoria: Bun azzera la memoria della password dopo l'uso
- Isolamento Processo: Le credenziali sono isolate per account utente
Limitazioni
- La lunghezza massima della password varia per piattaforma (tipicamente 2048-4096 byte)
- I nomi service e name dovrebbero avere lunghezze ragionevoli (< 256 caratteri)
- Alcuni caratteri speciali potrebbero richiedere escaping a seconda della piattaforma
- Richiede servizi di sistema appropriati:
- Linux: Il demone servizio segreti deve essere in esecuzione
- macOS: Keychain Access deve essere disponibile
- Windows: Il servizio Credential Manager deve essere abilitato
Confronto con Variabili d'Ambiente
A differenza delle variabili d'ambiente, Bun.secrets:
- ✅ Crittografa le credenziali a riposo (grazie al sistema operativo)
- ✅ Evita di esporre segreti nei dump di memoria del processo (la memoria è azzerata dopo che non è più necessaria)
- ✅ Sopravvive ai riavvii dell'applicazione
- ✅ Può essere aggiornato senza riavviare l'applicazione
- ✅ Fornisce controllo accesso a livello di utente
- ❌ Richiede il servizio credenziali del sistema operativo
- ❌ Non molto utile per segreti di distribuzione (usa variabili d'ambiente in produzione)
Best Practices
Usa nomi servizio descrittivi: Corrispondi al nome dello strumento o dell'applicazione Se stai costruendo una CLI per uso esterno, probabilmente dovresti usare un UTI (Uniform Type Identifier) per il nome del servizio.
javascript// Buono - corrisponde allo strumento effettivo { service: "com.docker.hub", name: "username" } { service: "com.vercel.cli", name: "team-name" } // Da evitare - troppo generico { service: "api", name: "key" }Solo credenziali: Non archiviare configurazione dell'applicazione in questa API Questa API è lenta, probabilmente hai ancora bisogno di usare un file di configurazione per alcune cose.
Usa per strumenti di sviluppo locale:
- ✅ Strumenti CLI (gh, npm, docker, kubectl)
- ✅ Server di sviluppo locale
- ✅ Chiavi API personali per testing
- ❌ Server di produzione (usa gestione segreti appropriata)
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;
}