Consent for command-line credentials

Nothing leaves
without asking.

secreq wraps the CLIs you use every day — gh, aws, kubectl, psql — so each invocation pulls its credentials from your own secret store. Every release is gated by a prompt that names the process asking, and every value is masked on the way back out.

It is a PATH shim, not a shell alias, so it catches the requests you did not type: an npm postinstall, a Makefile, an agent running in your editor.

zsh~/project

$gh auth login

GITHUB_TOKEN •••••••••••• released, masked

gh wants to use GITHUB_TOKEN

SecretGITHUB_TOKEN op://Personal/GitHub/credential
Asked byzsh7926ghgh auth login
In~/project
DenyApprove

The path a request takes

  1. 01

    The shim catches the call

    secreq installs itself ahead of the real binary on your PATH. Every execvp("gh", …) lands here first — including the ones inside npm, make, cargo and your editor, which a shell alias would never see.

  2. 02

    You see who is asking

    The prompt walks the process tree and shows it to you: the shell, the script it launched, the binary at the end of the chain. Approval is scoped to that direct parent, so answering yes for your shell does not answer yes for a postinstall hook.

  3. 03

    The value goes out masked

    The secret is fetched from your provider, handed to the binary, and redacted from its stdout and stderr. It never lands in your shell history or your scrollback.

What you get

Provenance

The prompt names the caller, not just the command

A credential request tells you nothing on its own — "something wants your GitHub token" is not a question you can answer. secreq reconstructs the process chain that led to the request and puts it in front of you, so the answer is obvious.

The consent window →
A deeper ancestry. Every process between your shell and the asking binary is listed with its argv and pid, so a request from a build script never looks like one you typed.

Rules

Answer once, not fifty times

Approving the same request all afternoon is how consent prompts become noise you click through. Save a rule and secreq answers for you — matched on the wrap, the arguments and the ancestor process, so it stays narrow.

Rules and suggestions →
Saved rules answer for you. Each row shows what it matches, whether it approves or denies, how often it has fired, and which secrets it was trained on.

Audit

A record of every decision

What asked, what it wanted, where from, and how it went — your answers, rule auto-fires, and the requests that were abandoned before you got to them. Searchable across every field at once.

Reading the audit log →
Every decision is written down: what asked, what it wanted, where from, and how it went. Your answers and rule auto-fires land in the same list.

SSH

The same gate in front of your keys

Point SSH_AUTH_SOCK at secreq and it becomes your agent. Each signature shows the key fingerprint and the reason before it happens; the private key is resolved from your provider, used in memory, and zeroized.

The SSH agent →
Point SSH_AUTH_SOCK at secreq and every signature is gated. You see the key fingerprint and the reason before git push signs, plus quiet grants covering a 30-minute session.

Sandboxes

Serve secrets to a VM without trusting it

A guest has no host process tree, so there is no caller chain to verify — the scope you declared when you opened the socket is the principal instead. If a guest volunteers a chain, it is shown as a claim and marked unverifiable.

The secret agent →
A request from a guest VM. The headline is the sandbox, because a guest has no host process tree — the scope you declared is the principal, and there is deliberately no caller chain to read. DECLARED BY is the local process that opened the socket, which here is the secreq agent open you started.

Ad hoc

secret:// for any command

secreq run is op run for every store: resolve ambient secret:// references from the environment or an --env-file, then exec your command with the values injected and masked. No wrap required.

CLI reference →
A secreq run carrying 42 variables leads with the count and groups the secrets by locator prefix. The body scrolls; the decision buttons never leave the window.

Setting one up

Two minutes

Answer four questions, get a wrap

secreq wrap gh asks what the wrap should do, which provider holds the secret, and where. It resolves the locator against your store before writing anything, so a mistyped path fails while you are still looking at it — not the first time you run gh next week.

Getting started →
secreq wrap gh
┌ Wrap `gh`│◆ What should this wrap do?│ ● Inject secrets (resolve secret:// references into env vars)│ ○ Gate only (no secrets)└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◆ Provider for the next env var│ ● keychain (supports store)│ ○ lastpass│ ○ op│ ○ pass└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◆ Provider for the next env var│ ○ keychain│ ● lastpass (retrieve-only)│ ○ op│ ○ pass└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◆ Provider for the next env var│ ○ keychain│ ○ lastpass│ ● op (retrieve-only)│ ○ pass└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◇ Provider for the next env var│ op│◆ Environment variable name│ e.g. GITHUB_TOKEN└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◇ Provider for the next env var│ op│◇ Environment variable name│ GITHUB_TOKEN│◆ Locator│ provider-specific address (e.g. Personal/GitHub Token/credential)└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◇ Provider for the next env var│ op│◇ Environment variable name│ GITHUB_TOKEN│◇ Locator│ op://Personal/GitHub/credential│● Reading that as locator `Personal/GitHub/credential`│◇ Locator resolves ✓│◆ Add another env var?│ ○ Yes / ● No└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◇ Provider for the next env var│ op│◇ Environment variable name│ GITHUB_TOKEN│◇ Locator│ op://Personal/GitHub/credential│● Reading that as locator `Personal/GitHub/credential`│◇ Locator resolves ✓│◆ Add another env var?│ ○ Yes / ● No└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◇ Provider for the next env var│ op│◇ Environment variable name│ GITHUB_TOKEN│◇ Locator│ op://Personal/GitHub/credential│● Reading that as locator `Personal/GitHub/credential`│◇ Locator resolves ✓│◇ Add another env var?│ No│◆ Reason (shown in consent prompt)│ (empty to skip)└
┌ Wrap `gh`│◇ What should this wrap do?│ Inject secrets│◇ Provider for the next env var│ op│◇ Environment variable name│ GITHUB_TOKEN│◇ Locator│ op://Personal/GitHub/credential│● Reading that as locator `Personal/GitHub/credential`│◇ Locator resolves ✓│◇ Add another env var?│ No│◇ Reason (shown in consent prompt)│ GitHub API access│└ Wrapped `gh`. config: ~/.secreq/config.toml shim: ~/.secreq/shims/gh
Authoring a wrap by answering questions. Every value is checked as you go — the locator is resolved against your store before the wrap is written, so a typo fails here rather than the first time you run gh.

Install

curl -fsSL https://craigory.dev/secreq/install.sh | sh
Platform
macOS, Linux
Requires
curl

Then run secreq init

Installing creates no wraps — nothing on your PATH changes until you ask for it. secreq init sets up the shim directory and walks you through your first wrap.

The full platform matrix and signature verification are in the install guide.