Skip to main content

Documentation Index

Fetch the complete documentation index at: https://theseventeen-2abbdf80.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

On Windows, keychain-auth listens on a Named Pipe and uses the GetNamedPipeClientProcessId Win32 API to retrieve the verified PID of every connecting process directly from the kernel. It then opens a process handle with OpenProcess and resolves the binary path using QueryFullProcessImageName. After computing a SHA-256 hash and checking it against your approved policy, the daemon fulfills the request through Windows Credential Manager.

The problem: DPAPI has no caller verification

Windows Credential Manager stores secrets encrypted with the Data Protection API (DPAPI). DPAPI decrypts data for any process running as the current user — no identity check, no prompt, no audit trail. A PowerShell script, a malicious npm post-install hook, or any compromised process running in your user session can call CredRead to dump every generic credential stored under your account. keychain-auth acts as a zero-trust broker in front of Credential Manager. Your applications talk to the daemon over a Named Pipe; the daemon verifies their identity at the kernel level before deciding whether to forward the request.

Named Pipe path

The daemon creates a Named Pipe at:
\\.\pipe\keychain-auth
Access to the pipe is restricted to your user account via the pipe’s security descriptor, equivalent to Unix 0600 socket permissions. To connect from a client:
# Python (pywin32)
import win32file
handle = win32file.CreateFile(
    r"\\.\pipe\keychain-auth",
    win32file.GENERIC_READ | win32file.GENERIC_WRITE,
    0, None,
    win32file.OPEN_EXISTING,
    0, None
)
// Node.js
const net = require('net');
const client = net.createConnection('\\\\.\\pipe\\keychain-auth');

Kernel-level process verification

When a client connects, the daemon calls GetNamedPipeClientProcessId on the active pipe handle. The kernel fills in the true PID of the connecting process — a value the client cannot forge. With the verified PID, the daemon calls OpenProcess with limited query rights, then uses QueryFullProcessImageName to get the absolute path of the running executable on disk. It reads the binary file and computes a SHA-256 hash, then checks it against your approved binary database. Only verified, hash-matching binaries receive a response.

Windows Credential Manager backend

The daemon reads and writes secrets through Windows Credential Manager using CredRead, CredWrite, and CredDelete. Credentials stored by the daemon are of type CRED_TYPE_GENERIC and are encrypted by DPAPI at rest, tied to your Windows user account. Because all access is mediated by the daemon’s policy engine, unregistered processes cannot reach Credential Manager through keychain-auth, even though they could still call CredRead directly on items stored elsewhere.
keychain-auth controls access to secrets stored under its own service namespaces. Credentials stored in Credential Manager by other applications (outside of keychain-auth) remain accessible to any process running as your user via CredRead. For full protection, store all credentials through keychain-auth.

Non-inheritable handles (the Windows equivalent of O_CLOEXEC)

On Unix, the O_CLOEXEC flag tells the kernel to close a file descriptor when a process calls exec(). Windows has an equivalent mechanism: handle inheritance. By default, child processes created with CreateProcess inherit all inheritable handles from their parent — including an open, already-authenticated pipe handle. If a child process inherits your pipe handle to the daemon, it can send requests as your trusted application without going through verification. Always create pipe handles as non-inheritable. In the Win32 API, pass FALSE as the bInheritHandle parameter of SECURITY_ATTRIBUTES, or use SetHandleInformation to clear HANDLE_FLAG_INHERIT after opening:
// Using SECURITY_ATTRIBUTES at creation time
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, FALSE }; // bInheritHandle = FALSE
HANDLE hPipe = CreateFile(pipePath, ..., &sa, ...);

// Or retroactively on an existing handle
SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, 0);
The official keychain-auth SDK sets handles as non-inheritable automatically.
If you write a custom Windows client and create an inheritable pipe handle, any subprocess your application spawns with CreateProcess (and bInheritHandles = TRUE) could silently read or write keychain secrets using your authorized session. Always use the official SDK or explicitly clear HANDLE_FLAG_INHERIT.

Approving binaries

The first time an unregistered binary connects, the daemon rejects it and queues it in %USERPROFILE%\.config\keychain-auth\pending.json. Use the CLI to inspect and approve:
# Inspect the pending queue
keychain-auth list-pending

# Approve by SHA-256 hash
keychain-auth approve sha256:<hash>

# Register a binary directly (e.g. during your tool's installer)
keychain-auth register (Get-Command your-tool).Source
Include keychain-auth register in your tool’s installer or setup script. When the tool is updated and its hash changes, run keychain-auth upgrade to refresh the approved hash without losing the existing access policy.

Quick-start

# Start the daemon
keychain-auth start

# Register your tool during setup
keychain-auth register (Get-Command your-cli-tool).Source

# List binaries waiting for approval
keychain-auth list-pending

# Approve a pending binary
keychain-auth approve sha256:<hash>
The audit log is written to %APPDATA%\keychain-auth\audit.log. Every access attempt — approved or denied — is recorded with the binary path, hash, PID, and target names. Plaintext secret values are never written to the log.