TILLSECRETS · SECRET REQUESTS

Ask a person for a value.

Beta

Agents and scripts often need a credential that only a person has: a cloud API token, a vendor key. Pasting it into a chat puts it in the transcript, the model's context and the logs. A secret request sends the person a link instead. They paste the value into their browser, and it goes straight into the vault. The workflow that asked gets back a secretref:// handle and never sees the value.

01From a terminal

Request a value

tilldev secrets request prints a link and a code on stderr, then waits. When the person has stored the value, it prints the handle on stdout, so $(…) captures the handle and nothing else.

request → handle
# An agent (or you) needs a value that only a person has.
REF=$(tilldev secrets request DIGITALOCEAN_TOKEN --env prod \
  --note "Read+write token for the job-runner droplets" \
  --allow-host api.digitalocean.com --ttl 30)

# stderr, meanwhile:
#   Waiting for a person to fill DIGITALOCEAN_TOKEN
#     link  https://tilldev.dev/acme/secrets/requests/8c1e…
#     code  K7QM-3XWD

echo "$REF"    # secretref://infra/prod/DIGITALOCEAN_TOKEN — never the value
without waiting
# Don't block: print the link and code, check back later.
tilldev secrets request STRIPE_SECRET_KEY --env prod --sensitive --no-wait
tilldev secrets requests get <id>      # state: pending | fulfilled | cancelled | expired
tilldev secrets requests ls [--all]
tilldev secrets requests cancel <id>
FlagMeaning
--envThe environment the value is stored in (slug or id; add --project if the slug is ambiguous).
--noteShown to the person: what the value is for and what access it needs.
--ttlMinutes until the link stops working. 5 to 10080 (7 days), default 60.
--sensitivity / --sensitiveTier for the stored secret (0–10; --sensitive is 7). Omit to inherit the environment’s.
--allow-hostUp to 10 hosts added to the secret’s broker allowlist when it is stored.
--agentWhich agent asked, shown to the person and recorded in the audit log.
02In the browser

What the person sees

The link opens the request in the Secrets console. Before they paste anything, the person sees who asked and why, and the value's exposure:

  • its effective sensitivity tier;
  • the service tokens that can pull it, and whether each is pinned to the environment;
  • the sync targets it is pushed to;
  • the TillForge repos whose CI can read it;
  • the hosts the broker may send it to, with the ones this request adds marked.

They enter the code from your terminal and paste the value. It is encrypted on arrival. If the key already exists, the value becomes its next version and the old one stays in history. The value is never sent back to the page.

03Why a code

The link alone is not enough

The code links the browser to the terminal that asked. Someone who only sees a forwarded link or a screenshot of it cannot fill the request.

  • Only a signed-in admin of the workspace can open or fill the request.
  • The code is stored only as a hash. A request takes at most five attempts every ten minutes.
  • A request can be filled once. After that, or once it expires or is cancelled, the link is dead.
  • Allow-listing hosts for a secret at tier 7 or above needs the workspace owner, the same rule as broker allow.
Send both
Send the link and the code by different routes if you can: the link in chat, the code read out loud.
04From outside

Drop links

When the value belongs to someone outside the workspace, make a drop link. They need no TillDev account. They open the link, enter the code and send the value. It waits for you, encrypted, and reaches the vault only when you accept it. In the console: Secrets → Requests → New drop link.

make a drop link
# Someone outside the workspace has the value: a vendor, a contractor.
tilldev secrets request VENDOR_API_KEY --env prod --external \
  --note "Production API key for our account" --ttl 1440

# ✓ Drop link for VENDOR_API_KEY — works once, expires in 24h
#   link  https://tilldev.dev/drop#tdr_…
#   code  K7QM-3XWD-P9RT
review what arrived
# You get an email when a value arrives. Look before you accept:
tilldev secrets requests get <id>      # when, from which IP and browser; same as the stored value or different
tilldev secrets requests accept <id>   # stores it as the next version
tilldev secrets requests reject <id>   # erases it
  • Only a signed-in workspace admin can make one. Agent tokens and API keys cannot.
  • The code is 12 characters. Send it by a different channel than the link, for example the link by email and the code by phone. The part of the link after # is not sent when a browser loads the page.
  • The link works once and lasts at most 72 hours. Five wrong codes in total lock it, and you get an email.
  • They see the workspace name, your name and email, the key and your note. Nothing about the stored value or who can read it.
  • Before you accept, you see when the value arrived, from which IP address and browser, and whether it is the first value for the key, the same as the stored one or different. Neither value is shown.
  • Accepting stores it as the next version, under the same rules as any write. Rejecting erases it. If nobody does either within 7 days, it is erased.
  • A drop link cannot allow broker destinations or name an agent. Allow destinations after you accept.
  • The link stops working if you leave the workspace or cancel it.
What it cannot tell you
Anyone holding both the link and the code can send a value, which is why nothing is stored until you accept it. If the time, address or browser is not what you expected, reject it and ask again by a route you trust. A drop link also cannot undo a key that leaked before it was sent: if in doubt, ask the vendor for a new one.

The audit log records request.drop, request.drop_refused for each wrong code, request.accept, request.reject and request.discard.

05Next

Use the handle

The handle works anywhere a reference does: secrets exec, the broker SDK, catalog actions and presigned calls.

request → presign
# Then use it without holding it:
tilldev secrets presign DELETE "https://api.digitalocean.com/v2/droplets?tag_name=job-42" \
  --bearer "$REF" --quiet

Every request is in the audit log as request.create and request.fulfill, with who asked, who filled it and the hosts it allowed. Pending requests are listed under Secrets → Requests in the console.