@neotales/darwin-keychain
v0.0.0
Published
macOS Keychain API for Node, Bun, and Deno.
Maintainers
Readme
@neotales/darwin-keychain
Overview
@neotales/darwin-keychain provides simple macOS keychain access for generic passwords backed by Security.framework FFI.

Documentation
Documentation is available on jsr.io.
A list of other modules can be found at github.com/neotales/js-os.
Installation
deno add jsr:@neotales/darwin-keychain
npx jsr add @neotales/darwin-keychain
npm install @neotales/darwin-keychainUsage
import {
getSecret,
getSecretString,
isAvailable,
listSecrets,
removeSecret,
saveSecret,
} from "@neotales/darwin-keychain";
if (!isAvailable())
throw new Error("Keychain FFI is unavailable");
saveSecret("my-service", "token", "my-secret");
saveSecret("my-service", "bytes", new Uint8Array([0, 255, 1]));
console.log(getSecretString("my-service", "token"));
console.log(getSecret("my-service", "bytes"));
console.log(listSecrets("my-service").map(({ account }) => account));
removeSecret("my-service", "token");
removeSecret("my-service", "bytes");Services and accounts must be non-empty. The root API stores generic-password entries in the default macOS keychain and returns opaque bytes from getSecret() and listSecrets().
Native API
import { isAvailable, Keychain } from "@neotales/darwin-keychain/ffi";
if (!isAvailable())
throw new Error("Keychain FFI is unavailable");
const item = Keychain.SecKeychainAddGenericPassword(
"my-service",
"native-token",
new TextEncoder().encode("secret"),
);
try {
Keychain.SecKeychainItemModifyAttributesAndData(item, new TextEncoder().encode("updated"));
const found = Keychain.SecKeychainFindGenericPassword("my-service", "native-token");
console.log(found?.secret);
if (found)
Keychain.CFRelease(found.item);
Keychain.SecKeychainItemDelete(item);
} finally {
Keychain.CFRelease(item);
}Keychain also exposes SecKeychainSearchCreateFromAttributes, SecKeychainSearchCopyNext, and SecKeychainItemCopyAttributesAndData for native enumeration. Returned KeychainHandle instances are runtime-specific opaque references; release every item and search handle with Keychain.CFRelease().
Exports
| Export | Subpath | Description |
| ------------------------------------------------------------------------------------------ | ------------------------------- | --------------------------------------------------- |
| getSecret, getSecretString, saveSecret, removeSecret, listSecrets, isAvailable | @neotales/darwin-keychain | Uniform secret-store facade. |
| DarwinKeychain, Keychain, isAvailable, KeychainHandle | @neotales/darwin-keychain/ffi | Native generic-password and Security.framework API. |
| SecretRecord | @neotales/darwin-keychain | Keychain list record type. |
Runtime Notes
This package is macOS-specific. On non-macOS systems isAvailable() returns false; root reads, removals, and lists return safe defaults, while native /ffi calls throw when invoked.
- Deno requires
--allow-ffi, for example:deno run --allow-ffi app.ts. - Node.js 26+ uses native FFI with
--experimental-ffi, for example:node --experimental-ffi app.ts. - Node.js without native FFI can use the koffi fallback:
npm install koffi. Koffi does not require a runtime flag. - Bun uses its built-in FFI and needs no additional flag.
