cd /news/developer-tools/show-hn-lokalite-local-secrets-for-c… · home topics developer-tools article
[ARTICLE · art-66684] src=github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Show HN: Lokalite – local secrets for CLIs and coding agents

RubenGlez released Lokalite, a local-first secrets manager for macOS that encrypts API keys, tokens, and credentials in an AES-256-GCM vault stored in Apple Keychain, delivering secrets to AI coding agents via MCP without exposing them to chat history or model context. The tool, which supports Claude Code, Cursor, and Windsurf, offers a menu bar app, CLI, and MCP server with Touch ID unlock, per-project environments, and logged access, requiring macOS 14 or later and no cloud accounts or telemetry.

read14 min views5 publishedJul 21, 2026
Show HN: Lokalite – local secrets for CLIs and coding agents
Image: source

Lokalite is a local-first secrets manager for macOS, built for how developers work with AI coding agents. Your API keys, tokens, and credentials live in an encrypted vault on your machine. When an agent needs one, the value is delivered into its shell runtime, never into the chat — so secrets stay out of the model's context and your conversation history. Reaches Claude Code, Cursor, and Windsurf through MCP, on your terms, with every access logged. Menu bar app, CLI, and MCP server in one. No cloud, no accounts, no telemetry.

Project page: rubenglez.dev/lokalite

vs.: values are encrypted at rest instead of sitting in plaintext, access is logged, and the same secrets are available to your shell, the CLI, and your AI agents without copy-pasting between files..env

filesvs. cloud secret managers(Doppler, Infisical, 1Password): nothing leaves your machine. No account to create, no per-seat billing, no vendor to trust with your credentials. Local-first is the point, not a limitation.Built for agents: an AI agent loads the secrets it needs through MCP — the value reaches its shell runtime via a one-time handoff, never the chat transcript or the model's context. Logged, read-only by default, scoped per project; no pasting keys into a conversation.

Encrypted vault: AES-256-GCM via CryptoKit, vault key stored in Apple Keychain** Touch ID unlock**: biometric authentication to unlock the vault — from the menu bar app, or via a prompt when a CLI/MCP request arrives while the vault is locked — plus per-read consent onapprove

/strict

secrets. While the vault is unlocked, reads don't re-prompt — seeSecurityMenu bar app: search, copy, and reveal secrets without leaving your workflow; recent secrets surfaced at the top** Global keyboard shortcut**: open the popover from anywhere, configurable in Settings (default⌘⇧Space

)Full CLI: read, write, and inject secrets from the terminal** Projects**: group secrets by project; link to a local directory for automatic resolution** Environments**: per-project environment profiles (dev, staging, production) with per-environment secret values; every project starts with a Default environment: pull from an existing.env

import/export.env

file or export back to oneEncrypted backup/restore:lokalite backup

writes a passphrase-encrypted file;lokalite restore

reads it backShell injection:eval $(lokalite shell)

exports secrets into the current sessionClipboard auto-clear: copied values are wiped after 30 seconds** Session timeout**: vault auto-locks after inactivity** In-app auto-updates**: the menu bar app keeps itself current via Sparkle — a manual*Check for Updates…item plus opt-in automatic background checksMCP integration: expose your vault as tools to Claude Code, Cursor, Windsurf, and any other MCP-compatible agent* Secret references**: commitlokalite://project/KEY

instead of a token in MCP configs;lokalite run --refs-only

resolves it at spawn timeZero runtime dependencies: no cloud, no telemetry, no vendor lock-in

  • macOS 14 or later

CLI, MCP server, and menu bar app via Homebrew:

brew install RubenGlez/lokalite/lokalite
brew install --cask RubenGlez/lokalite/lokalite-app
lokalite install   # registers the MCP server in ~/.claude.json

If you don't need the menu bar app:

brew install RubenGlez/lokalite/lokalite
lokalite install

If you already have the CLI installed or don't need it:

brew install --cask RubenGlez/lokalite/lokalite-app

Download from the Releases page:

CLI: run the.pkg

installer, thenlokalite install

Menu bar app: drag the.dmg

to Applications and launch it — no quarantine workaround needed.Releases are signed with an Apple Developer ID and notarized by Apple — the app, DMG, and CLI

.pkg

all pass Gatekeeper on first launch. The menu bar app then keeps itself up to date automatically (Sparkle). Every release also ships aSHA256SUMS

file you can verify against the artifacts.

lokalite init

lokalite status
lokalite status --json

lokalite log
lokalite log --limit 20
lokalite log --source cli       # filter by app, cli, or mcp
lokalite log --agent claude     # filter by the AI agent that accessed the secret

lokalite add OPENAI_API_KEY sk-...
lokalite add OPENAI_API_KEY          # no value: prompts for it, keeping it out of shell history
echo -n "sk-..." | lokalite add OPENAI_API_KEY -   # or pipe it via stdin

lokalite get OPENAI_API_KEY

lokalite copy OPENAI_API_KEY

lokalite list

lokalite list --search openai

lokalite set OPENAI_API_KEY sk-new-...

lokalite delete OPENAI_API_KEY

lokalite run -- npm start
lokalite run --keys OPENAI_API_KEY,ANTHROPIC_API_KEY -- claude
lokalite run --refs-only -- npx some-server   # resolve lokalite:// references only (see Secret references)

lokalite project add MyProject
lokalite project list
lokalite project use MyProject
lokalite project link [<name>] [--path <dir>]   # link to a directory; defaults to cwd
lokalite project link --unlink <name>            # remove path association
lokalite project delete MyProject

lokalite env add staging
lokalite env list
lokalite env use staging
lokalite env delete staging

lokalite import .env
lokalite import .env --overwrite        # overwrite existing secrets

lokalite export --output backup.lk          # encrypted (default), includes everything
lokalite export --plain --output secrets.json   # plaintext: approve-tier secrets are skipped (named on stderr)
lokalite export --format env                # .env format, stdout; approve-tier secrets are skipped
lokalite export --format env --output .env

lokalite backup                             # prompts for a passphrase, writes a timestamped .lokalite file (approve-tier secrets are skipped)
lokalite backup --output backup.lokalite
lokalite restore backup.lokalite            # prompts for the passphrase, skips existing secrets
lokalite restore backup.lokalite --overwrite

eval $(lokalite shell)
eval $(lokalite shell --keys OPENAI_API_KEY,ANTHROPIC_API_KEY)

Shell injection note:eval $(lokalite shell)

makes secrets visible to all child processes and shows up inenv

output for the duration of your session. Uselokalite run

to scope secrets to a single subprocess instead.

Click the armadillo icon in your menu bar (or press the global shortcut, default ⌘⇧Space

) to open the vault popover. The popover shows recently copied secrets at the top, then all secrets for the active project and environment. Use the project and environment menus in the header to switch context, and click Open Lokalite in the footer to open the full secrets manager window.

The secrets manager is a three-column layout:

Left sidebar: project list; create, rename, and delete projects; set icon and link to a local directory** Centre column**: environment switcher and searchable secrets list for the selected project** Right panel**: edit the selected secret's value; save or delete

On first launch, an onboarding screen guides you through creating your first project.

Right-click the menu bar icon (or open Settings → Updates) for Check for Updates…; once you opt in, the app also checks in the background and installs updates in place.

By default lokalite mcp

brokers vault access through the running Lokalite app over a local Unix socket, so the MCP server process never holds the vault key — it asks the app, which is the only process that can decrypt. The app is launched automatically if it isn't running. Pass --local

to open the vault in-process instead (for CI/headless use, where the value then passes through the MCP process).

lokalite install

registers the MCP server with Claude Code automatically (writing ~/.claude.json

). To register with a different client, pass --client

:

lokalite install --client claude          # ~/.claude.json (default)
lokalite install --client claude-desktop  # ~/Library/Application Support/Claude/claude_desktop_config.json
lokalite install --client cursor          # ~/.cursor/mcp.json
lokalite install --client windsurf        # ~/.codeium/windsurf/mcp_config.json

For any other MCP-compatible agent, add the same server block to its config manually:

{
  "mcpServers": {
    "lokalite": {
      "command": "lokalite",
      "args": ["mcp"]
    }
  }
}

By default the server is read-only and exposes these tools:

Tool Description
list_secrets
List secret names, categories, and descriptions (values never exposed)
list_projects
List projects and their linked directories (no values); use when no project resolves
list_environments
List a project's environments, marking the active one (no values)
use_environment
Switch the project's active environment — the one secrets resolve from by default
get_secret
Load a secret into the agent's shell environment via a one-time handoff — the value is never returned to the model

use_environment

switches the project's single active environment, the same one the menu bar app, the manager, and lokalite env use

use — switching from the agent updates all of them, and the app refreshes live. For a one-off read from a different environment without switching, pass an environment

argument to get_secret

instead.

get_secret

does not return the secret value. It writes the value to a single-use, owner-only shell script and returns a source '<path>'

command; the agent runs that in its own shell to load the variable, then runs its program in the same shell. The raw value never enters the conversation/model context, the script self-deletes on first source, and any unsourced script is swept after 30s. The server also sends instructions

on connect describing this flow and telling agents never to print a loaded variable or copy a secret into .env

/config/source.

When a tool call omits project

, the server auto-resolves it from the caller's working directory using the project's linked path — the same way the CLI does. Pass an absolute path

argument with the directory to resolve (preferred, since the server's own process may run elsewhere); otherwise it falls back to the server process's working directory. An explicit project

argument (or LOKALITE_PROJECT

in the server env

) always wins.

Pass --read-write

to also expose write tools:

{ "command": "lokalite", "args": ["mcp", "--read-write"] }
Tool Description
add_secret
Create a new secret
set_secret
Update an existing secret's value
delete_secret
Permanently delete a secret

Security note:the handoff keeps secret values out of the model's context, but an agent that can name a secret can still load it into a shell it controls (list_secrets

gives it the names), and a sourced value is then visible to processes in that shell. Set a per-secret agent-access tier withlokalite agent-access <name> allow|approve|block

(or from the app's secret editor):

block— off-limits:get_secret

refuses it,list_secrets

flags it[off-limits to agents]

.Best-effort:blocking an agent depends on detecting it (a process-tree name match), so a renamed or sandboxed agent can evade it. Treatblock

as a courtesy guard; useapprove

for a secret that must not be released without you in the loop.approve— consent-on-read: every read through the app broker — an agent'sget_secret

, alokalite://

reference, or the CLI'sget

/copy

— prompts for Touch ID before the value is released, whoever is asking; the approval then lasts for the rest of the unlock session.list_secrets

flags it[approval required]

. With--local

(no app to prompt) it fails closed, like block. Unlikeblock

, this doesnotrely on detection — the prompt fires for every caller.allow— the default.The CLI

get

/copy

won't print any secret to stdout or the clipboard when an AI agent is detected in the calling process tree — the same transcript-safety boundary as the MCP handoff, so an agent can't sidestep it by shelling out to read a value directly. Pass--allow-agent

to override for your own use; ablock

secret is refused regardless (no override). Anapprove

/strict

secret is additionally routed through the Lokalite app for the Touch ID prompt, for every caller (and refused, with no override, when the app isn't reachable); bulk reveals (shell

, plaintextexport

, bulkrun

injection,backup

) skipapprove

secrets and name them on stderr. This detection is best-effort (see theblock

note above) — it protects the transcript, not a determined agent's own shell; lean onapprove

/strict

consent for anything that must not be released without you in the loop.Writes are governed too:an agent'sset_secret

/delete_secret

(or CLIset

/delete

) on ablock

secret is refused, and on anapprove

/strict

secret it prompts for Touch ID — so an agent can't overwrite or delete a protected secret without consent. Also keep the server read-only (the default), scope it to a single project by settingLOKALITE_PROJECT

in the server'senv

config, and prefer clients that ask for approval before tool calls. Every MCP access is recorded in the activity log.

There are two independent gates on an agent's get_secret

, and they belong to different systems:

The client gate (above Lokalite). MCP hosts like Claude Code, Cursor, and Windsurf run their own permission check on every tool callbeforethe request ever reaches Lokalite. Depending on the client's mode,mcp__lokalite__get_secret

may be auto-blocked or surfaced for your approval — the agent stops and asks. Lokalite has no say here; this gate is entirely the client's.The Lokalite gate (below the client). Once a call gets through, the per-secret tier (allow

/approve

/block

) and vault-lock state apply, with Touch ID onapprove

/strict

. This is the gate described above.

The two are complementary: the client decides whether the tool may run at all; Lokalite decides whether this secret may be released. A secret can clear the client gate and still hit a Touch ID prompt, or be allowed by the client and refused by a block

tier.

If an agent reports that a get_secret

call was blocked by the permission classifier (not a "vault is locked" error), that is the client gate, and you resolve it in the client — not in Lokalite. In Claude Code, allow the tool by adding it to permissions.allow

in .claude/settings.local.json

(project-scoped, uncommitted):

{ "permissions": { "allow": ["mcp__lokalite__get_secret"] } }

Use mcp__lokalite

to allow the whole server. Weigh this against what you're releasing: allowlisting removes the client's per-call prompt for every secret in the project, so an agent can auto-load production credentials into a shell without you in the loop. Because the client gate can't filter by argument, it can't distinguish prod from dev — if you want a human gate that only fires on sensitive secrets, leave get_secret

out of the allowlist and mark those secrets approve

so Lokalite's Touch ID prompt fires instead.

A lokalite://

reference stands in for a secret's value anywhere an environment variable is configured. References carry no secret material, so they are safe to commit. Three forms:

lokalite://KEY

— project resolved like the CLI does (linked directory, then active project); the project's active environmentlokalite://myproject/KEY

— explicit project, its active environmentlokalite://myproject/staging/KEY

— fully qualified

lokalite run

scans its inherited environment for lokalite://

values and swaps each for the real secret value in the child process's environment before spawning. Pass --refs-only

to skip the bulk injection of project secrets and resolve only the references. Any other environment variable is inherited unchanged.

The primary use case is MCP server configs (~/.claude.json

, a project's .mcp.json

, claude_desktop_config.json

), which otherwise carry tokens in plaintext env

blocks:

{
  "mcpServers": {
    "github": {
      "command": "lokalite",
      "args": ["run", "--refs-only", "--", "npx", "-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "lokalite://myproject/GITHUB_TOKEN" }
    }
  }
}

The MCP host puts the reference in the child environment; lokalite run

replaces it with the real value before the server starts. The value never appears in the config file, the host's context, or the repo.

References are resolved through the Lokalite app (the same broker the MCP server uses), so the per-secret agent-access tiers apply at spawn time — a block

-tier reference is refused, an approve

-tier reference prompts for Touch ID whoever spawned the command — and every read lands in the activity log with agent attribution. Pass --local

to resolve in-process for CI/headless use; approval-tier and blocked secrets are then unavailable when an AI agent is detected.

Resolution fails closed: if any reference is malformed, unknown, or denied, lokalite run

prints the environment variable and the reference (never a value), exits non-zero, and the command is not run.

Values: every secret value is encrypted with AES-256-GCM before being written to disk. The 256-bit vault key is generated on first use and stored in your macOS login keychain — it is never written to the vault file.Metadata: secret names, descriptions, project names, linked paths, and the access activity log are stored unencrypted in~/Library/Application Support/Lokalite/vault.db

. They reveal which services you use, not the credentials themselves; the file relies on home-directory permissions and FileVault.App unlock: the menu bar app requires Touch ID or your device password before showing secrets, and auto-locks after the session timeout. A CLI or MCP request that arrives while the vault is locked triggers the same Touch ID prompt (naming the caller) instead of unlocking silently; if it's declined, the request fails closed.CLI and MCP: while the vault is unlocked, brokered reads don't re-prompt (per-secretapprove

/strict

tiers still do). The CLI's in-process paths (--local

, direct commands) read the vault key from the login keychain without a prompt — there the trust boundary is your unlocked macOS user session, just like with~/.aws/credentials

or.env

files (which Lokalite improves on by encrypting values at rest and logging access).Clipboard: copies are marked withorg.nspasteboard.ConcealedType

so well-behaved clipboard managers skip them, and are auto-cleared after 30 seconds.- Nothing leaves your machine: no cloud, no telemetry.

MIT

── more in #developer-tools 4 stories · sorted by recency
── more on @rubenglez 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-lokalite-loc…] indexed:0 read:14min 2026-07-21 ·