# ClauLock

> **Your AI can use your secrets. It can't see them.**

[![License: Apache 2.0 core](https://img.shields.io/badge/core-Apache--2.0-blue)](./LICENSE-APACHE)
[![License: BSL 1.1 product](https://img.shields.io/badge/product-BUSL--1.1-orange)](./LICENSE-BSL)
[![Never-reveal invariant](https://img.shields.io/badge/invariant-never--reveal-brightgreen)](./tests/leak_test.sh)

ClauLock is a local-first secrets manager built for AI agents. API keys, tokens, passwords, SSH keys, and certificates stay encrypted on your machine — and the Claude model never receives the plaintext value, even when it's the one using it.

**Status:** Fase 1 MVP + Fase 2 (rotation queue, usage timeline, metadata editor, deep-links) running locally. Releases ship signed macOS `.pkg`, Linux and Windows tarballs, `.deb`/`.rpm`, and cosign + minisign signatures, built on the project's own CI. See the [plan](./docs/product-vision.md) for what's next.

## Scope of this repo

This repository contains the **public-bound** code of ClauLock — the
crypto, vault, daemon, hooks, MCP server, scrubber, CLI, Tauri UI,
plugin, and site. Everything here is licensed under **Apache-2.0**
(crypto + hooks + leak-test) and **BUSL-1.1** (product code; converts
to Apache-2.0 on 2030-05-01). See [LICENSE-APACHE](./LICENSE-APACHE),
[LICENSE-BSL](./LICENSE-BSL), and [NOTICE](./NOTICE) for the
file-to-license mapping.

The **commercial Pro / Enterprise** components (license verification,
billing, sync server, policy engine, breach detection, SOC 2 / HIPAA
exports, PKCS#11 adapters, tamper-evident audit log, enterprise UI)
live in a **separate private repository** under a proprietary EULA.
They are **not** included here. That separation is intentional:
auditors can read every line that touches a secret in this repo
without needing access to commercial code.

Signed release binaries (built from this repo + the private Pro repo
together) are published at
[releases.claulock.com](https://releases.claulock.com/)
along with the public signing keys.

## Install

```bash
curl -fsSL https://claulock.com/install.sh | sh
```

This drops `clsec`, `clsecd`, `clsec-exec`, `clsec-mcp`, and `clsec-scrub` into `~/.claulock/bin`, verifies the tarball's minisign signature against the key bundled in the installer, and chains into an interactive `clsec install` that generates a vault, stashes the passphrase in the OS keystore, loads the daemon user agent, and wires Claude Code user-level hooks + MCP registration.

After installing, confirm the setup:

```bash
clsec doctor
# → ✓ binaries, ✓ vault, ✓ keystore, ✓ daemon.socket, ✓ daemon.status,
#   ✓ claude.hooks, ✓ claude.mcp, ✓ launchd
```

Env overrides the installer respects: `CLAULOCK_VERSION` (pin a release tag), `CLAULOCK_INSTALL_DIR` (change the target), `CLAULOCK_NO_SETUP=1` (download only, skip `clsec install`), `CLAULOCK_SKIP_MINISIGN=1` (opt out of signature verification — CI only, not recommended for humans).

Verifying a release by hand (minisign + sha256 + cosign) is documented in [packaging/RELEASE.md](./packaging/RELEASE.md).

### Staying current

`clsec install` schedules a daily update check. It is deliberately timid about
when it acts: **an update is applied only while the vault is locked.** The CLI,
the daemon and the desktop app speak a versioned IPC protocol and have to move
together, so applying means restarting the daemon — which would lock the vault
out from under whatever is using it. A locked vault means nobody is mid-session,
so the restart costs nothing, and a run that finds the vault in use simply waits
for the next one.

```bash
clsec autoupdate status     # installed vs published, schedule, last run
clsec autoupdate run --check # is there a new version? change nothing
clsec autoupdate run        # apply now (defers if the vault is in use)
clsec autoupdate disable    # stop checking
```

Updates go through the channel you installed from — Homebrew upgrades and
relinks through brew, everything else re-runs the official installer, which
verifies the minisign signature over `SHA256SUMS`. The macOS app bundle is only
replaced if it is notarized and not currently running. Opt out at install time
with `clsec install --no-autoupdate`.

## How it works

```
┌─────────────┐   model writes:                           ┌──────────────┐
│   Claude    │   curl -H "Bearer {{GITHUB_TOKEN}}" …     │  transcript  │
│   (model)   │──────────────────────────────────────────▶│ sees {{…}}   │
└──────┬──────┘                                           │ forever      │
       │                                                  └──────────────┘
       │ Bash tool call
       ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  Claude Code PreToolUse hook                                            │
│   ↳ wraps the command with clsec-exec                                   │
└──────┬──────────────────────────────────────────────────────────────────┘
       ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  clsec-exec  (child process, memory-only substitution)                  │
│    1. Ask clsecd over Unix socket: "resolve GITHUB_TOKEN for role=cli"  │
│    2. Substitute {{GITHUB_TOKEN}} → <plaintext> in argv                 │
│    3. exec the real command                                             │
│    4. Pipe stdout/stderr through clsec-scrub to redact any leak         │
└──────┬──────────────────────────────────────────────────────────────────┘
       ▼
┌─────────────┐                                           ┌──────────────┐
│  curl runs  │───── stdout (scrubbed) ──────────────────▶│  Claude sees │
│  normally   │───── stderr (scrubbed) ──────────────────▶│  output w/   │
└─────────────┘                                           │  placeholder │
                                                          └──────────────┘
```

The MCP server deliberately **has no tool that returns a raw secret value.** The shape of the API enforces the invariant. Verification lives in [tests/leak_test.sh](./tests/leak_test.sh) and runs on every push.

## What Claude sees vs what runs

| Layer | Content |
|---|---|
| Claude's transcript (what the model ever sees) | `curl -H "Authorization: Bearer {{GITHUB_TOKEN}}" https://api.github.com/user` |
| Claude Code Bash tool call | `curl -H "Authorization: Bearer {{GITHUB_TOKEN}}" https://api.github.com/user` |
| After PreToolUse hook wraps | `clsec-exec -- curl -H "Authorization: Bearer {{GITHUB_TOKEN}}" https://api.github.com/user` |
| Inside `clsec-exec` (child memory only) | `curl -H "Authorization: Bearer ghp_live_…" https://api.github.com/user` |
| stdout returned to Claude | `{"login":"octocat",…}` with any accidental echo of the plaintext scrubbed to `{{GITHUB_TOKEN}}` |

Only the second-to-last line ever contains the real value, only for the lifetime of that one child process, and only in its own argv — not visible to Claude's context window, not logged in the audit trail, not written to disk.

## Platforms

| Platform | MVP | Later |
|---|---|---|
| macOS (Keychain + Touch ID unlock) | ✅ | |
| Linux (secret-service / file vault) | ✅ | |
| Windows (DPAPI) | | Post-launch |
| iOS / Android (unlock approval, E2EE sync) | | Post-launch |

### Other install methods

If you prefer a package manager over the curl installer:

**macOS / Linux (Homebrew):**

```bash
brew tap claulock/tap https://releases.claulock.com/homebrew-tap.git && brew install claulock
clsec install
```

`clsec install` leaves the binaries to brew and only sets up the rest — vault,
keystore, background service and Claude wiring. Upgrade with
`brew upgrade claulock`; running the curl installer over a brew
install would replace brew's symlinks with real files and quietly freeze every
later upgrade.

**Linux (Debian/Ubuntu):**

```bash
curl -LO https://releases.claulock.com/latest/claulock_0.1.0_amd64.deb
sudo dpkg -i claulock_0.1.0_amd64.deb
clsec install
```

**Linux (RHEL/Fedora):**

```bash
sudo rpm -i https://releases.claulock.com/latest/claulock-0.1.0-1.x86_64.rpm
clsec install
```

**macOS (.pkg):** grab `claulock-v0.1.0-macos-universal2.pkg` from the
[latest release](https://releases.claulock.com/latest/),
double-click, then run `clsec install`.

**Manual tarball (any platform):** download the matching
`claulock-v0.1.0-<os>-<arch>.tar.gz`, extract, and either run the four binaries
directly or copy them to `~/.local/bin/`.

All release artifacts are signed — see [Verifying release artifacts](#verifying-release-artifacts) below.

### Building from source

The source repository is currently private and ships only signed binaries
to [releases.claulock.com](https://releases.claulock.com/).
Public source release tracks `v1.0` (timeline in [docs/product-vision.md](./docs/product-vision.md)).
Until then, `curl install.sh | sh` and `brew tap claulock/tap https://releases.claulock.com/homebrew-tap.git && brew install claulock`
are the supported install paths — both verify minisign + cosign signatures
end-to-end.

## Dogfooding with Claude Code

After `clsec install` (run automatically by the installer or by you), the daemon, MCP server, and Bash hooks are wired up. Add a secret:

```bash
# Interactive — prompts for the value without echoing to history.
clsec add GITHUB_TOKEN --kind token

# Or let Claude ask you: in a session, Claude calls the MCP tool
# secret_request_from_user, which pops an elicitation prompt in your
# Claude client. The value goes straight to clsecd. Your transcript
# never sees it.
```

To use a secret, reference it by name with `{{…}}` in any Bash tool call:

```
curl -H "Authorization: Bearer {{GITHUB_TOKEN}}" https://api.github.com/user
```

The PreToolUse hook wraps the command in `clsec-exec`; plaintext never reaches Claude's context.

To verify the invariant end-to-end:

```bash
bash tests/leak_test.sh
```

## Importing existing secrets

Already have a `.env` file or a 1Password / Doppler vault? Bring everything in with one command. Plaintext values never touch disk or your shell history — each value streams straight from the source into the daemon and is zeroized as soon as it lands.

```bash
clsec import dotenv ./.env                                  # local .env
clsec import 1password --vault Personal                     # 1Password (requires `op signin`)
clsec import doppler --project my-app --config prd          # Doppler
```

On name collisions the import aborts by default; pass `--on-conflict=update` to overwrite or `--on-conflict=skip` to keep the existing value.

## Trust model

ClauLock is built around four principles. Being explicit about what each one does — and doesn't — cover matters more than slogans.

- **Source-available.** Every line that touches a secret is licensed under **Apache-2.0** (crypto + hooks + leak_test) or **BUSL-1.1** (product code; converts to Apache-2.0 on 2030-05-01) — see [NOTICE](./NOTICE) for the file-to-license mapping. Commercial Pro / Enterprise components live in a separate private repo under a proprietary EULA and are NOT included here. No closed blobs in this repo, no pre-built binaries without a matching commit. Reproducible release builds are tracked in Fase 3.
- **Self-hosted by design.** There is no cloud, no account, no sign-up, no telemetry, no update server that phones home. The daemon is a local process, the vault is a local file, the socket is a local Unix socket. If your machine is offline, ClauLock works exactly the same.
- **End-to-end encrypted — within the host.** The "ends" are the vault on disk and the child process that legitimately needs the secret (e.g. `git`, `curl`). A secret is decrypted only inside the daemon's memory (mlock'd, zeroized on drop) and the child's address space; it is never written to disk, never sent over a network socket, never placed in an environment variable visible via `ps`, and never enters the model's context window. What happens *after* the child receives it — e.g. `curl` sending it to `api.github.com` over TLS — is between you and that remote service. ClauLock cannot and does not try to encrypt the value end-to-end to GitHub; the remote API is the legitimate receiver and must see cleartext to authenticate you.
- **Zero-knowledge of the vault contents — to anyone but you.** The KEK is derived from your passphrase via Argon2id (m=256 MiB, t=3, p=4). Nobody — not the daemon process, not Claude, not Anthropic, not a laptop thief with the vault file — can read a secret without your passphrase. The default install caches the passphrase in the OS keychain (Touch ID / polkit) so unlock feels seamless; if you want strict zero-knowledge of the host itself (nothing cached anywhere but your brain), run `clsec install --no-keychain` and you'll be prompted on every unlock.

What is **not** in scope: protecting you from a remote service that is compromised after you authenticate to it, from kernel-level malware running as your user, or from a physical attacker with root and a cold-boot rig. Those live in a different threat model — see [SECURITY.md](./SECURITY.md) for the full list.

## Documentation

- [About ClauLock](./docs/about.md) — what this project is, who built it, and why.
- [Architecture](./docs/architecture.md) — components, key hierarchy, invariants.
- [Threat model](./docs/threat-model.md) — what we defend against and how.
- [UI reveal / copy flow](./docs/ui-reveal.md) — how the Companion UI lets you see a value safely.
- [Product vision (12-month)](./docs/product-vision.md) — where this is going next.
- [MCP API](./docs/mcp-api.md) — the full tool surface Claude sees.

## License

ClauLock uses a **split licensing model** so the security primitives stay auditable while the product itself remains sustainable:

- **Apache-2.0** (see [LICENSE-APACHE](./LICENSE-APACHE)) — covers the cryptography, the on-disk vault format, the Claude Code hook protocol, and the CI never-reveal invariant check. Anyone can audit, fork, and re-use these components under a permissive license.

- **Business Source License 1.1** (see [LICENSE-BSL](./LICENSE-BSL)) — covers the CLI, daemon, exec shim, scrubber, keystore adapters, MCP server, IPC protocol, Tauri UI, site, and packaging. Non-production use is unrestricted. Production use inside your own organization is permitted. **You may not offer the BSL portions as a commercial hosted service that competes with ClauLock's paid offerings.** The Change Date is **2030-05-01**, after which the BSL portions automatically convert to Apache-2.0.

- **Proprietary** — ClauLock Pro and Enterprise components (licensing verification, billing, sync server, policy engine, breach detection, SOC 2 / HIPAA exports, PKCS#11 adapters, tamper-evident audit log, enterprise UI) live in a separate private repository and are distributed under a separate EULA. They are **not** included in this repository.

See [NOTICE](./NOTICE) for the authoritative file-to-license mapping and [BRAND.md](./BRAND.md) for the trademark policy. Contributions require a signed [CLA](./CLA.md).

## Security

See [SECURITY.md](./SECURITY.md). Bug bounty ships with `v0.1.0`. Report vulnerabilities privately via [GitHub Security Advisories](https://github.com/claulock/claulock/security/advisories/new) or `security@claulock.com`.

### Verifying release artifacts

Every release ships with two independent signatures:

- `SHA256SUMS` + `SHA256SUMS.minisig` — minisign signature over the checksum file. Pubkey in [minisign.pub](./minisign.pub):
  ```
  RWSyx2T3dBSwgtWFkCgsVburcch3gocYhrhJD428hxwExlUIXyMvR6gY
  ```
  Verify:
  ```bash
  minisign -Vm SHA256SUMS -p minisign.pub
  shasum -a 256 -c SHA256SUMS
  ```
- `<binary>-<os>-<arch>.sig` — cosign signature for each shipped binary. Pubkey in [cosign.pub](./cosign.pub). Verify:
  ```bash
  cosign verify-blob --key cosign.pub --signature clsec-macos-arm64.sig clsec
  ```

Releases v1.5.0–v1.5.2 also carry an SLSA Build L3 provenance file (`*.intoto.jsonl`) from the GitHub-hosted pipeline of that period; every other release is covered by minisign + cosign.

## Author

Built by **Jesús E. Viera** — [jesuseviera.com](https://jesuseviera.com) — in San Juan, Puerto Rico, for the world. *Hecho en Puerto Rico.*

## Project

- Website: [claulock.com](https://claulock.com)
- App / docs: [claulock.app](https://claulock.app)
