lockkit

module
v0.18.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 13, 2026 License: Apache-2.0

README

CI Latest release Go version

The shared Go core of every Lock: write the operations,
get the console, the approvals, the masking and the audit for free.

Quick start · How it works · Docs · Lock Family


lockkit is the shared foundation of the Lock Family, harness security for AI agents. It holds everything that keeps a secret-holding console separate from an AI agent, asks the human, and records what happened.

A product supplies its names and its operations, through a Backend; lockkit supplies the rest.

[!NOTE] No stability promise before v1. Products pin an exact version. GUIDE.md is the contract between lockkit and the products built on it.

Why lockkit

  • One security model for every Lock. The console, its isolated account and socket, and the policy review behave the same in locksql, lockmq, lockfile, lockhttp, locklog and lockssh.
  • The AI tightens, only the human loosens. Policy files, their review and the approved store are shared code, not re-implemented per product.
  • Masking and audit you do not write twice. Personal-data detectors, masks, capped output, error scrubbing and the append-only audit log.
  • One contract with the hub. Signed tickets and decisions, the client for lockhub, and the launcher verbs lox add calls.

Install

lockkit is a Go library (Go 1.26 or later). Pin an exact version:

go get github.com/DavidGodefroid/lockkit@v0.14.0

To work on a Lock and lockkit together, use an uncommitted go.work (see GUIDE.md § 15).

Quick start

A new Lock builds on lockkit in a few steps, each detailed in GUIDE.md:

  1. Declare your product once, a kit.Product with its names (binary, console account, clients group, keychain service, env prefix), and pass it everywhere.
  2. Register your refusal codes with refusal.Register: they are part of your client protocol.
  3. Implement console.Backend: your IPC methods, Connect, Checks, LoadPolicy and Status.
  4. Start the console with one call, console.Run(ctx, P, backend, opts), launched by lockhub (lox run) with the hub options.
  5. Expose MCP tools with mcpkit (each a thin adapter over client.Call) and wire the agents with agentwire.
  6. Implement the launcher verbs (init, profile add, doctor) with verbs, so lox add can set your Lock up.
var P = kit.Product{
	Name:            "lockmq",                                     // binary, .lockmq/, <config>/lockmq, sockets
	Tagline:         "Your AI reads the queue. You hold the key.", // banner
	Account:         "lockmq",                                     // console account
	ClientsGroup:    "lockmq-clients",                             // may reach the console
	KeychainService: "lockmq",                                     // OS keychain service
	EnvPrefix:       "LOCKMQ_",                                    // LOCKMQ_RUNTIME_DIR, ...
}

Highlights

🖥️ The console, done
console.Run does the isolation checks, the policy review, the secret (ask, OS keychain or Vault), the socket and the request loop. Your Backend only runs operations.

🎭 Masking built in
pii detects emails, phones, IBANs, cards, national IDs, public IPs and secrets; render caps and escapes output; sanitize scrubs errors.

✍️ Signed tickets
ticket is the lock ↔ lockhub contract: RFC 8785 canonical JSON, Ed25519 signatures, X25519 sealed boxes. hubclient submits tickets and waits for signed decisions.

🔌 Any MCP client
mcpkit gives the MCP plumbing; agentwire wires Claude Code, Codex, Cursor and Gemini CLI to your server, in a project or for the account, merging and never clobbering.

How it works

lockkit splits a Lock in two sides that never share an account:

  • The human side, console: it holds the credentials and the only connection, shows the policy for approval, and serves a socket. In hub mode it sends masked, signed tickets to lockhub and acts only on a decision signed by the hub key it pinned.
  • The agent side, client + mcpkit: secret-free. It dials the console's socket (peer credentials checked by the kernel) and calls it over newline-delimited JSON-RPC 2.0.

The product plugs its own operations in between, through console.Backend.

Packages

Area Package Owns
Product kit kit.Product: a product's names and directories
refusal refusal codes registry, refusal.Error, structured Info
Human side console terminal, approvers (typed, desktop, fingerprint), isolation checks, request loop, the generic Server and Run that host a Backend
ui palette, banner, status marks of human-facing output
fingerprint fprintd verification (desktop security level 2) and a fake for tests
sysconf the machine-wide /etc/<name>/system.toml of the separated setup; process, tty and relay probes
Config and secrets config config lookup, secret-safe TOML decoding, profiles, Policy, diff helpers, approved store
managed the company-managed managed.toml of the family: root-owned, optionally signed, bounds users may tighten but never loosen
secrets credential sources: terminal prompt, OS keychain, Vault CLI; Sanitize, Wipe
plans a lock's persisted plans awaiting a decision: bound, single use, 0600
Data protection pii value detectors, mask modes, name heuristics, the redacted-reference store
render capped, escaped output for agents: Value and Table
sanitize error text scrubbing and terminal-safe text
audit the console's append-only JSONL audit log
Transport ipc console socket, peer credentials, 1 MiB JSON-RPC 2.0 framing, lockkit's methods and error codes
client the secret-free side: dial, hello, Call
tunnel SSH bastion: known_hosts, key/agent/password auth, Dial and NewSession through it
lazyconn backend connections opened on a connect ticket's approval, closed when idle
Hub ticket signed tickets and decisions, risk classes and TTLs, ticket kinds, canonical JSON, sealed boxes
hubclient a lock's client for lockhub: Submit, long-poll of decisions, Begin/Finish, Ask; hubclient/hubtest fake hub
Agent side mcpkit MCP server and stdio, profile resolution, progress while the human decides, untrusted framing, structured refusals
agentwire wiring Claude Code, Codex, Cursor and Gemini CLI to your MCP server
verbs the launcher verbs every lock shares (init, profile add, doctor) and the JSON lox add reads
Release update cosign keyless signature check of a release artifact, checksums

The full map, with what not to reimplement in your product, is in GUIDE.md § 1 and § 11.

Who uses it

Every Lock is built on lockkit: locksql, lockmq, lockfile, lockhttp, locklog and lockssh. lockhub uses its ticket package to verify what the locks sign, and lea builds on it too.

Safety model

  • The agent side holds no secret: credentials, the connection and the approved policy stay in the console's separated account.
  • The boundary is the operating system's: separate accounts, the kernel's peer credentials on the socket.
  • A policy change the agent proposes can only tighten; loosening is asked of the human, and on production takes the typed profile name.
  • In hub mode, a lock executes only on a decision signed by the hub key it pinned, bound to the exact plan that was approved.
  • Masking is best effort. PII detection quality: precision and recall per detector and language on a synthetic bench, held to a CI baseline, in pii/testdata/bench/RESULTS.md; its Known gaps section lists what the detectors miss most.

Details: GUIDE.md § 6, § 10 and § 13.

Documentation

Doc What it covers
GUIDE.md The contract with products: every package, the console and its Backend, MCP and agent wiring, hub tickets, launcher verbs
CHANGELOG.md Release notes, with what each version changes for products

The Lock Family

Every Lock is built on the same console, separation and approval model, and one hub approves for all of them. Start at lockfamily.dev.

Repo What it does
lockfamily The family site and install.sh: everything in one install
locksql SQL databases: the agent queries, PII comes back masked; connections and writes wait for you
lockmq RabbitMQ: masked queue inspection; connections, peeks and unmasked reads wait for you
lockfile Files (S3, SFTP, disk): the agent asks yes/no questions about files it never reads
lockhttp Outbound HTTP(S): reads of trusted hosts pass; anything that sends data or reaches an unknown host waits for you
locklog Logs: masked, bounded log search; the agent reads the logs, not the people in them
lockssh Commands over SSH: allow-listed commands run at once, the rest waits for you; the SSH key never leaves the console
lockhub The approval hub and its lox CLI: lox run approves outside the agent's harness
lockkit The shared Go core of every Lock: console, policies, masking, signed tickets, audit, MCP wiring
lea Runs many agents in containers, wired to the Locks

Contributing · Security · License

  • Contributing: lockkit has one owner. Do not copy a package into your product or work around it: ask for the change, as GUIDE.md § 15 describes. Tests: go vet ./... && go test ./....
  • Security: report vulnerabilities privately through GitHub security advisories.
  • License: Apache License 2.0. See LICENSE and NOTICE.

Directories

Path Synopsis
User scope: wires agents through files under the home directory (and `claude mcp add --scope user`).
User scope: wires agents through files under the home directory (and `claude mcp add --scope user`).
Package audit writes the console's JSONL audit trail: one record per event, never secrets and never row data.
Package audit writes the console's JSONL audit trail: one record per event, never secrets and never row data.
Package client is the secret-free side of a product: it dials the console socket of a project and profile and calls the console's JSON-RPC methods.
Package client is the secret-free side of a product: it dials the console socket of a project and profile and calls the console's JSON-RPC methods.
Package config is the product-neutral half of a product's configuration: where the project and user config files are, how a TOML file is decoded without ever reading a secret, the profile base every product shares (credentials, Vault, TLS, SSH bastion, production, session limits), the policy fingerprint, the diff helpers that mark loosenings, and the store of the policies the human approved.
Package config is the product-neutral half of a product's configuration: where the project and user config files are, how a TOML file is decoded without ever reading a secret, the profile base every product shares (credentials, Vault, TLS, SSH bastion, production, session limits), the policy fingerprint, the diff helpers that mark loosenings, and the store of the policies the human approved.
Package console is the human side of a lockkit product: it holds the credentials and the only backend connection, enforces the approved policy, shows every request that needs a human decision on an approval screen, and writes the audit trail.
Package console is the human side of a lockkit product: it holds the credentials and the only backend connection, enforces the approved policy, shows every request that needs a human decision on an approval screen, and writes the audit trail.
Package fingerprint verifies the human's finger on the machine's reader through fprintd, for the console's approvals in desktop mode.
Package fingerprint verifies the human's finger on the machine's reader through fprintd, for the console's approvals in desktop mode.
Package hubclient is a lock's client for lockhub, the approval hub: it pushes signed tickets, long-polls the hub's decisions and turns an approval into a plan that runs once.
Package hubclient is a lock's client for lockhub, the approval hub: it pushes signed tickets, long-polls the hub's decisions and turns an approval into a plan that runs once.
hubtest
Package hubtest is a fake lockhub for the tests of locks: it speaks the lock API v1 (signed requests, tickets, decisions with a cursor) on an httptest server, and lets a test decide tickets.
Package hubtest is a fake lockhub for the tests of locks: it speaks the lock API v1 (signed requests, tickets, decisions with a cursor) on an httptest server, and lets a test decide tickets.
Package ipc is the console's local transport: a Unix domain socket in a directory private to the user (on Linux and macOS), newline-delimited JSON-RPC 2.0 framing capped at 1 MiB, and a peer check.
Package ipc is the console's local transport: a Unix domain socket in a directory private to the user (on Linux and macOS), newline-delimited JSON-RPC 2.0 framing capped at 1 MiB, and a peer check.
Package kit names a product built on lockkit.
Package kit names a product built on lockkit.
Package lazyconn opens a lock's backend connections only when an agent's request needs one, on a decision: a lock starts disconnected; the first request for a profile sends a connect ticket (ticket.KindConnect) to the hub, the connection opens on a signed approval, is shared by the requests that follow, and closes once it has been idle for Config.Idle.
Package lazyconn opens a lock's backend connections only when an agent's request needs one, on a decision: a lock starts disconnected; the first request for a profile sends a connect ticket (ticket.KindConnect) to the hub, the connection opens on a signed approval, is shared by the requests that follow, and closes once it has been idle for Config.Idle.
Package managed reads the company-managed configuration of the lock family: one file, set by the company's admin (MDM, Ansible, a package), that bounds the policy of every lock, lox and lea on the machine.
Package managed reads the company-managed configuration of the lock family: one file, set by the company's admin (MDM, Ansible, a package), that bounds the policy of every lock, lox and lea on the machine.
Package mcpkit is the product-neutral half of a product's MCP server: the server and its stdio transport, the profile a call is for, progress notifications while the human decides, the untrusted-data framing of results, the "no console" answer naming the start command, refusals as structured tool errors and exact large integers.
Package mcpkit is the product-neutral half of a product's MCP server: the server and its stdio transport, the profile a call is for, progress notifications while the human decides, the untrusted-data framing of results, the "no console" answer naming the start command, refusals as structured tool errors and exact large integers.
pii
Package pii is the product-neutral half of personal-data masking: value detectors (email, phone, IBAN, card, national ids), mask modes, the multilingual name heuristics that propose mask rules, and the store of the clear values behind redacted references.
Package pii is the product-neutral half of personal-data masking: value detectors (email, phone, IBAN, card, national ids), mask modes, the multilingual name heuristics that propose mask rules, and the store of the clear values behind redacted references.
internal/benchgen command
Command benchgen writes the PII detection bench corpus (see package gen):
Command benchgen writes the PII detection bench corpus (see package gen):
internal/benchgen/gen
Package gen builds the PII detection bench corpus: synthetic text cases in English, French, Dutch and German with the byte span and kind of every personal value they hold, hard negatives that look like personal data without being any, and field names labelled personal or quasi-identifying.
Package gen builds the PII detection bench corpus: synthetic text cases in English, French, Dutch and German with the byte span and kind of every personal value they hold, hard negatives that look like personal data without being any, and field names labelled personal or quasi-identifying.
Package plans is a lock's store of plans awaiting a decision: one file per plan in the lock's volume (0600 in a 0700 directory), surviving restarts.
Package plans is a lock's store of plans awaiting a decision: one file per plan in the lock's volume (0600 in a 0700 directory), surviving restarts.
Package refusal names why a console refused a request.
Package refusal names why a console refused a request.
Package render turns a (masked) product result into the text an agent sees, within the caps of a profile: a table (TSV or JSON) or any typed JSON value.
Package render turns a (masked) product result into the text an agent sees, within the caps of a profile: a table (TSV or JSON) or any typed JSON value.
Package sanitize cleans text before it leaves the console: error messages for a client or the audit log (known secrets in every encoded form, then the product's registered patterns), and any text printed for the human (control and bidi characters escaped, so that client-supplied text cannot drive or spoof the terminal).
Package sanitize cleans text before it leaves the console: error messages for a client or the audit log (known secrets in every encoded form, then the product's registered patterns), and any text printed for the human (control and bidi characters escaped, so that client-supplied text cannot drive or spoof the terminal).
Package secrets reads database secrets from the human (no-echo prompt) or the OS keychain, and scrubs them from error strings.
Package secrets reads database secrets from the human (no-echo prompt) or the OS keychain, and scrubs them from error strings.
Package sysconf reads a product's machine-wide setup that separates the console from the agent: the console runs as a dedicated account (the product's, used in its own login session) that alone holds the credentials, and the agent's account reaches it through a socket that a group grants access to.
Package sysconf reads a product's machine-wide setup that separates the console from the agent: the console runs as a dedicated account (the product's, used in its own login session) that alone holds the credentials, and the agent's account reaches it through a socket that a group grants access to.
Package ticket is the contract between a lock and lockhub: the ticket a lock pushes when a request needs a human, the decision the hub signs back, their canonical JSON (RFC 8785), Ed25519 signatures and the X25519 sealed boxes that carry what the hub must not read.
Package ticket is the contract between a lock and lockhub: the ticket a lock pushes when a request needs a human, the decision the hub signs back, their canonical JSON (RFC 8785), Ed25519 signatures and the X25519 sealed boxes that carry what the hub must not read.
devicetest
Package devicetest is a virtual WebAuthn authenticator for tests: it signs device assertions as a phone's platform authenticator would, so locks and the hub can test device-signed decisions without a browser.
Package devicetest is a virtual WebAuthn authenticator for tests: it signs device assertions as a phone's platform authenticator would, so locks and the hub can test device-signed decisions without a browser.
Package tunnel opens the SSH connection to a profile's bastion and dials the backend through it, to any address the bastion reaches (one tunnel serves several ports), or opens sessions on it (NewSession).
Package tunnel opens the SSH connection to a profile's bastion and dials the backend through it, to any address the bastion reaches (one tunnel serves several ports), or opens sessions on it (NewSession).
Package ui holds the look of a product's human-facing output: the palette of the logo, the banner and the status marks.
Package ui holds the look of a product's human-facing output: the palette of the logo, the banner and the status marks.
Package update checks a release's signature before a product installs anything from it: the cosign signature of an artifact (typically checksums.txt), keyless against the identity of the release workflow or key-pair against a public key, then the checksums of the files.
Package update checks a release's signature before a product installs anything from it: the cosign signature of an artifact (typically checksums.txt), keyless against the identity of the release workflow or key-pair against a public key, then the checksums of the files.
Package verbs is the shared half of the launcher verbs every lock accepts, so that `lox add` drives every lock the same way (GUIDE.md §14):
Package verbs is the shared half of the launcher verbs every lock accepts, so that `lox add` drives every lock the same way (GUIDE.md §14):

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL