CLI reference
One command does everything:
pinchpass request <secret-name>... [flags]. It starts a
one-time server, prints a link, blocks until the value arrives or the TTL
expires, writes .env, and exits.
7 flags -json for agents blocks until submit exits non-zero on timeout
Invocation forms
| Form | What happens |
|---|---|
pinchpass request API_KEY |
The explicit form. Use this in scripts and documentation. |
pinchpass API_KEY |
The subcommand is auto-detected, so a bare secret name is treated as
request API_KEY. Convenient interactively. |
pinchpass --help |
Prints usage, the flag list, and examples. --help exists
only at the top level, not on the subcommand. |
pinchpass version, -version, --version |
All three print the version, for example
pinchpass v1.2.0 (abc1234). |
At least one secret name is required. With no arguments, or an unknown command, PinchPass prints usage and exits.
Flags
| Flag | Default | Description |
|---|---|---|
-tunnel |
false |
Open a bore.pub tunnel and print a public URL instead of a
loopback one. Use it when the human is not on your machine. |
-note |
— | Human-readable description shown on the form, so the person knows what they are being asked for. |
-out |
.env |
Output .env path. Parent directories are created if
missing. |
-ttl |
30 |
Minutes until the link expires. When it lapses the server shuts down and nothing is written. |
-port |
0 (random) |
Local port to bind. Zero means pick any free port. |
-listen-addr |
127.0.0.1 |
Address to listen on. Changing it exposes the form beyond loopback,
which you almost never want without -tunnel. |
-json |
false |
Machine-readable output for agents and scripts. Two JSON objects are printed, one before blocking and one after. |
Write -out, not --out. There is also no
-gen-only flag: an unused field with that name exists inside the
code, but it is not exposed on the command line, so do not rely on it.
Examples
# local link, usable only from this machine
pinchpass request API_KEY
# public link through bore.pub
pinchpass request API_KEY -tunnel
# explain what is being asked for, and write somewhere else
pinchpass request API_KEY -note "Production API key" -out config/secrets.env
# machine-readable, for an agent to parse
pinchpass request API_KEY -json
# short-lived link
pinchpass request API_KEY -ttl 5
# collect several values into one .env
pinchpass request DB_HOST DB_PORT DB_NAME -out .env
# bare form, public link, JSON output
pinchpass WEBHOOK_SECRET -tunnel -json
Output
Human mode
Before blocking, the CLI prints the link, the names, and the expiry:
🔗 Secret request link (one-time, E2E encrypted via bore.pub)
http://bore.pub:38448/claim/gemini_api_key#k=…
Name: GEMINI_API_KEY
Note: Production API key
Expiry: 30 minutes
Waiting for user to submit...
When the value arrives:
✅ Saved to .env
GEMINI_API_KEY
If nobody submits before the TTL:
❌ Request timed out. No secret was saved.
The process exits non-zero, so a shell pipeline or an agent can tell the difference between "collected" and "gave up" without parsing output.
JSON mode
-json prints two objects. The first is emitted
immediately, before the command blocks, so a caller can read the URL and
present it to a human while the process keeps waiting:
{
"success": true,
"names": ["GEMINI_API_KEY"],
"message": "Secret request link (one-time, expires in 30 min)",
"url": "http://bore.pub:38448/claim/gemini_api_key#k=…",
"port": 38448
}
The second is emitted once the wait is over:
{
"success": true,
"names": ["GEMINI_API_KEY"],
"message": "Secrets provisioned successfully.",
"port": 38448
}
On timeout the second object reports the failure:
{
"success": false,
"names": ["GEMINI_API_KEY"],
"message": "Request timed out or failed.",
"port": 38448
}
The second object has no url field — take the URL from the first
one. Also note the first object's success field means "the request
started", not "a secret was collected": the only reliable signal of collection
is success in the second object, or the exit code.
What it writes to .env
Each collected value is written as KEY="value". The value is
always double-quoted and shell-escaped, so a key containing quotes, dollars,
backticks or backslashes survives being sourced.
| Character | Written as |
|---|---|
\ | \\ |
" | \" |
$ | \$ |
` | \` |
An existing key is replaced in place, and every other line in the file is left
alone. The file is written with 0600 permissions, parent
directories are created with 0700, and the result ends in a
newline.
Collecting DB_HOST twice, with a changed value:
# before
APP_ENV="staging"
DB_HOST="localhost"
# after
APP_ENV="staging"
DB_HOST="db.internal"
The tunnel
-tunnel dials bore.pub:7835 directly. The client is
written against the Go standard library and shells out to nothing, so there is
no bore binary to install. The server publishes a random remote
port and the CLI prints:
http://bore.pub:<remote-port>/claim/<path>#k=<key>
Everything after # is the decryption key. Browsers never transmit
a fragment, which is exactly why the key can travel this way — but it also
means the fragment is the one part of the URL that cannot be rebuilt. Paste the
URL verbatim; if a chat client or shortener drops the fragment, the form
disables submit and the human needs a new link.
Without -tunnel the link points at
http://127.0.0.1:<port>/claim/<path>#k=<key>, which
only works for someone sitting at that machine.
-listen-addr changes the bind address if you need to reach it over
a LAN, though -tunnel is usually the better answer.
The path segment is derived from the secret name — lowercased, with anything
outside a–z, 0–9 and
_ removed — so GEMINI_API_KEY becomes
/claim/gemini_api_key. Treat the name as public and the value as
protected; the security model explains why that is
still safe.
Lifetime of a request
-
The CLI generates a fresh 32-byte encryption key and starts an HTTP server on a random loopback port.
-
It prints the link and blocks. The link is now live, whether or not anyone has opened it.
-
The human opens the form, types the value, and submits. The browser encrypts it with the key from the fragment.
-
The ciphertext reaches the server, which stores it and unblocks the CLI. The CLI decrypts it locally, writes
.env, and exits. -
The request is spent. Every later request to that URL returns 404 with a link-expired page, so a leaked link is worthless once used.
-
Or the TTL lapses first: the server shuts itself down, the CLI exits non-zero, and nothing is written. The default is 30 minutes.
For the crypto and the threat model, see the security model. For the agent-facing view of the same flow, see agent integration.