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.

A working integration is not the same as a production-ready one. The checklist on this page covers the security requirements, operational hooks, and UX behaviors that separate a robust keychain-auth integration from one that introduces new vulnerabilities or leaves users stranded when something goes wrong. Work through each item before shipping your tool to end users.

Checklist

This is the most critical security requirement in the client contract. Skipping it allows untrusted child processes to inherit your authenticated socket connection and make requests on your tool’s behalf, bypassing all keychain-auth policy checks.
When your CLI forks and executes a child process — a build script, a subcommand, an untrusted plugin — that child inherits all open file descriptors by default. If the keychain-auth socket is among them, the child can write arbitrary requests to it. The daemon cannot distinguish between your tool and its child: the connection was already authenticated.The fix is to open the Unix domain socket with the SOCK_CLOEXEC flag. This tells the kernel to automatically close the file descriptor whenever exec() is called, cutting off any child process before it can use the connection.In Go:
# The daemon enforces this; your client must too
# Open with SOCK_CLOEXEC — see the Go integration guide for the full pattern
syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
On Windows, set handles as non-inheritable using the equivalent Win32 security attributes.
Your installer or package post-install hook must register the binary with the daemon before the user ever runs it. Without registration, the first invocation triggers a denial and queues the binary for manual user approval — a confusing and unnecessary friction point.Add this to your install script:
keychain-auth register $(which your-tool)
This writes the binary’s path and SHA-256 hash to ~/.config/keychain-auth/config.json with empty permission sets. The user then completes authorization by editing the config, but they never need to interact with the pending approval workflow.
Registration only records the binary — it does not grant any permissions. Follow up by configuring the allowed_read_services, allowed_write_services, and can_search fields as appropriate for your tool.
Every time your tool’s binary changes — on any version update — its SHA-256 hash changes. The daemon will reject the updated binary as unregistered until you update the recorded hash in config.json.Add this to your package post-upgrade hook:
keychain-auth upgrade $(which your-tool)
This recomputes the hash, finds the matching path entry in config.json, and updates the hash in place. Existing permission policies are preserved — the user does not need to re-authorize your tool after an upgrade.
Test this in your release pipeline by running keychain-auth upgrade immediately after building and installing the new binary, then verifying that a request succeeds.
The keychain-auth daemon may not be running when your tool executes — for example, after a system restart before the user has started it, or in a CI environment where it was never installed. A raw socket connection failure produces an unhelpful error for the user.Catch the connection error and surface a clear, actionable message:
Error: Cannot connect to keychain-auth daemon.
To resolve this, start the background agent:
  keychain-auth start
This applies to all platforms: Unix socket failures on macOS/Linux and named pipe failures on Windows should both produce the same user-facing guidance.
If your tool is the sole manager of its secrets and does not need other CLI tools to read them directly, isolate everything under a private service namespace — your tool’s own name is a natural choice.This means only your registered binary (with the matching hash and path) can read or write those secrets. No other tool can accidentally or intentionally access them, even if that tool is separately authorized for other namespaces.
{
  "path": "/usr/local/bin/your-tool",
  "hash": "sha256:...",
  "allowed_read_services": ["your-tool"],
  "allowed_write_services": ["your-tool"],
  "can_search": true
}
See Configure service namespace permissions for details on when to use shared namespaces instead.
Avoid reading every secret in a namespace into client memory when you only need to list or select from them. Instead, use search prefix filtering to retrieve just the target keys matching a prefix, then issue explicit reads for only the secrets your tool actually needs.Discovery via search (keys only, no values):
{
  "type": "REQUEST",
  "action": "search",
  "service": "your-tool",
  "targets": ["production/workspace_abc/proj_123/"]
}
For cases where you do need the values immediately, a single-roundtrip prefix read is more efficient than N individual reads:
{
  "type": "REQUEST",
  "action": "read",
  "service": "your-tool",
  "match": "prefix",
  "targets": ["production/workspace_abc/proj_123/"]
}
Both approaches require can_search: true in your binary’s policy. See Hierarchical namespace schemes for more on structuring your prefixes.