A grep-friendly CLI data jar for URLs, IDs, account numbers, snippets, and vault-backed secret references.
Project description
recall
A small CLI data jar1 for lookup data you fetch over and over: URLs, IDs, account numbers, an ORCID, reusable snippets, plus references to secrets kept in a real vault.
You stop digging through email, password managers, and old notes for the same account number or portal URL. You, or an agent working for you, just ask:
$ recall orcid.self
copied orcid.self (id) to clipboard
$ recall insurance.portal
copied insurance.portal (url) to clipboard
$ recall search card
library-card [account] — Public library card number
$ recall github.token
github.token is a secret → op://Private/GitHub/token
run: recall secret github.token (opens it in 1Password to copy by hand)
Contents
- Philosophy
- Install
- Quickstart
- The data file
- Multiline data
- Commands
- Secrets
- Configuration
- Agents
- Security model
- Why JSONL
- Troubleshooting
- Development
Philosophy
Most reusable lookup data is not secret: an ORCID, a health-insurance member ID, a portal URL, a library card number. A few items genuinely are secret: API keys, tokens, passwords.
recall splits data into two tiers:
| Tier | Examples | Where the value lives |
|---|---|---|
| non-secret | URLs, IDs, account numbers, ORCID, snippets | in a plain JSONL file, returned directly |
| secret | API keys, tokens, passwords | only a reference is stored; the value lives in 1Password and recall never reads it by default |
That split is the whole design. It lets you keep the data file in a private git repo, and lets an agent use recall search, recall list, and recall <key> without being able to exfiltrate credentials.
Install
uv tool install recaller # PyPI distribution is `recaller`; the command is `recall`
For the secret tier you also need the 1Password CLI with desktop-app integration turned on:
brew install 1password-cli- In 1Password: Settings -> Developer -> Integrate with 1Password CLI
recall is macOS-first today: it uses pbcopy for the clipboard and open for URLs, files, and 1Password deep links.
Quickstart
-
Run init and choose where the private data file should live:
$ recall init Where should recall store data.jsonl? [/Users/you/.config/recall/data.jsonl]: ~/git/dotfiles/recall/data.jsonl Seconds before copied secrets are cleared [45]: Default secret backend [1password]: Create a starter data file? [Y/n]:
For a scriptable setup, pass the choices directly:
$ recall init --data-file ~/git/dotfiles/recall/data.jsonl --yes
-
Add entries to the data file:
{"key": "orcid.self", "kind": "id", "value": "0000-0000-0000-0000", "note": "My ORCID iD", "tags": ["identity"]} -
Use it:
$ recall orcid.self copied orcid.self (id) to clipboard $ recall doctor
The data file
The data file is data.jsonl: one JSON object per physical line. Each object is a complete entry and must include a key field.
{"key": "orcid.self", "kind": "id", "value": "0000-0000-0000-0000", "note": "My ORCID iD", "tags": ["identity"]}
{"key": "orcid.coauthor", "kind": "id", "value": "0000-0000-0000-0001", "note": "A frequent collaborator", "tags": ["identity"]}
{"key": "insurance.member-id", "kind": "account", "value": "MEMBER-ID-EXAMPLE", "note": "Health insurance member ID", "tags": ["health"]}
{"key": "insurance.portal", "kind": "url", "value": "https://example.com/login", "note": "Claims portal; sign in with the member ID", "tags": ["health"]}
{"key": "library-card", "kind": "account", "value": "LIBRARY-CARD-EXAMPLE", "note": "Public library card number", "tags": ["library"]}
{"key": "github.token", "kind": "secret", "backend": "1password", "ref": "op://Private/GitHub/token", "note": "GitHub personal access token", "tags": ["dev"]}
This format is intentionally boring. The lookup key, kind, value/ref, note, and tags are on the same line, so rg output has enough context for a human or agent:
$ rg -n 'insurance|health|member-id' ~/git/dotfiles/recall/data.jsonl
Only the one-object-per-line rule matters; whitespace within a line is free. The canonical form (what recall format emits to stdout) is one space after each : and ,, exactly what json.dumps produces by default. Minified lines parse identically, but the spaced form is easier to read and hand-edit.
JSONL has no comments. Keep commentary in note, in a linked file, or in project docs such as README.md or ROADMAP.md. That constraint is useful here: the data file stays machine-parseable, line-oriented, and unambiguous.
Keys and namespaces. Dots create virtual namespaces. insurance.member-id and insurance.portal are entries; insurance is inferred from those prefixes. Use short lowercase keys with dots between namespaces and hyphens inside a segment: service.account-id, person.orcid, grant.nsf.institution-id.
Fields:
| Field | Applies to | Meaning |
|---|---|---|
key |
all entries | required dotted lookup key |
kind |
all entries | url, file, id, account, note, snippet, secret, or another free-form label |
value |
non-secret entries | the value returned/copied; for file, this is a path |
backend |
secret entries | 1password (default) or keychain |
ref |
secret entries | a vault reference, e.g. op://Private/GitHub/token |
note |
optional | shown in search / list; never affects the value |
tags |
optional | list of strings for recall tags |
Multiline Data
The data file is an index, not a content store. Keep each entry and each field value to one physical line.
For multiline or large reusable content, store the content in a separate file and point to it:
{"key": "email.reply-template", "kind": "file", "value": "~/git/dotfiles/recall/snippets/reply.md", "note": "Standard email reply template", "tags": ["email"]}
recall rejects multiline value, note, ref, backend, kind, and tag strings. Use a file entry when the payload does not fit on one line.
For file-backed entries:
$ recall email.reply-template --show
~/git/dotfiles/recall/snippets/reply.md
$ recall open email.reply-template
If a file entry uses a relative path, recall resolves it relative to the directory containing data.jsonl.
recall <key> copies the file path. recall open <key> opens the file with the system default app.
Commands
| Command | What it does |
|---|---|
recall init |
create config.json and the first data file |
recall <key> |
copy a non-secret value to the clipboard |
recall get <key> [--show] |
same; --show prints the value instead of copying |
recall secret <key> |
open the item in 1Password so you copy it by hand |
recall secret <key> --copy |
resolve via op and copy to the clipboard (auto-clears) |
recall open <key> |
open a url or file entry |
recall search <query> |
search keys, notes, tags, and non-secret values |
recall list [prefix] |
list entries, optionally under a namespace |
recall tags <tag> |
list entries carrying a tag |
recall json [key] |
dump a subtree as JSON |
recall format [--check] |
emit canonical JSONL, or verify the file is already canonical |
recall export |
alias for recall json |
recall doctor |
validate the data file and environment |
Asking for a namespace instead of an entry lists its children:
$ recall insurance
insurance/ (namespace)
insurance.member-id [account] — Health insurance member ID
insurance.portal [url] — Claims portal; sign in with the member ID
Secrets
Secrets store only a reference:
{"key": "github.token", "kind": "secret", "backend": "1password", "ref": "op://Private/GitHub/token"}
The op:// reference has this shape:
op://<vault>/<item>/<field>
There are three ways to interact with a secret, in increasing order of exposure:
recall github.tokenrefuses to resolve. It prints the reference and a hint.recall secret github.tokenopens the item in the 1Password app.recalldoes not read the secret value.recall secret github.token --copyrunsop read, copies the value, and clears the clipboard after the configured timeout.
There is no recall secret --show mode. Secret plaintext should not be printed to stdout, where agents, transcripts, shell logs, or terminal capture can read it. For human debugging, use op read directly so the unsafe action is explicit and outside recall's normal contract.
Because secret entries already store op:// references, they also work with 1Password's op run and op inject. An env file like this:
GITHUB_TOKEN=op://Private/GitHub/token
resolves at launch with op run --env-file=.env -- your-command. Keep op run to non-agent contexts, because resolved secrets live in the child process environment.
Configuration
recall reads ~/.config/recall/config.json. Override the directory with $XDG_CONFIG_HOME or $RECALL_DIR.
recall init writes this file for you:
{
"data_file": "~/git/dotfiles/recall/data.jsonl",
"clipboard_clear_seconds": 45,
"default_backend": "1password"
}
Data-file resolution order: $RECALL_DATA_FILE -> config.json's data_file -> ~/.config/recall/data.jsonl.
A natural setup: the tool is public while your data is a data.jsonl in a private repo, e.g. your dotfiles, pointed at by config.json. The public code hardcodes nothing personal.
Agents
Give your coding agent one instruction:
If you need a URL, account number, identifier, reusable snippet, or credential reference, use
recall. Find keys withrecall search <term>orrecall list. Never ask me to paste a secret; non-secret values go to the clipboard and secrets open in 1Password.
Then enforce the boundary in your harness. For Claude Code (~/.claude/settings.json):
{
"permissions": {
"allow": ["Bash(recall:*)"],
"deny": ["Bash(recall secret:*)"]
}
}
With this, an agent can look up non-secret data unattended, but secret resolution stays gated.
Security Model
| Actor | Sees a non-secret | Sees a secret value |
|---|---|---|
recall <key> |
yes | no, it prints the reference only |
recall secret <key> |
- | no, it opens the vault app |
recall secret <key> --copy |
- | transiently, via op, to clipboard |
an agent allowed recall but denied recall secret |
yes | never |
| 1Password | - | yes |
Other properties:
- The data file is plain text by design, so keep it in a private repo. Filenames and keys are visible to anyone with repo access; do not encode secrets in key names.
recalllogs access to~/.config/recall/audit.log.- The repo's
.gitignorerefuses to commit a realdata.jsonl,config.json, older local data/config filenames, oraudit.log.
Why JSONL
- One entry per line. Grep hits are self-contained.
- Strict parser. No aliases, implicit typing, or layout-sensitive nesting.
- Append and review friendly. A new entry is a one-line diff.
- Portable. Every language can parse JSON without an extra dependency.
- Clear escape hatch. Multiline content belongs in a linked file, not inside the index.
Compared with a plain text file plus grep, the CLI adds validation, clipboard ergonomics, namespace listing, JSON export, op:// indirection, and the agent-safe split between recall and recall secret.
Troubleshooting
no data file at ...: create the file and pointconfig.json'sdata_fileat it, or set$RECALL_DATA_FILE, then runrecall doctor.invalid JSONL ...: each nonblank line must be one complete JSON object. Runrecall doctorafter edits.op read failed/op not found: install the 1Password CLI and enable the desktop-app integration.recall secretopens 1Password but not the right item: check therefagainstop item get "<item>" --vault "<vault>".- Clipboard did not clear: clipboard-history managers can keep copies after
recallclears the system clipboard. Excluderecallor pause history when using--copy.
Development
git clone https://github.com/yy/recall && cd recall
uv tool install --editable . --force
uv run python recall.py doctor
uv run pytest
The core is one Python file (recall.py) and has no runtime package dependencies. The lookup layer is plain functions, so a future MCP server or other front-end can wrap it without going through the CLI.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file recaller-0.2.0.tar.gz.
File metadata
- Download URL: recaller-0.2.0.tar.gz
- Upload date:
- Size: 21.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
006d8fc0960991c86887c5514ddd541bf3792d94062fdfa015b96b1c43b5ca94
|
|
| MD5 |
e13428a2e092f2575ebfad73aa27a1bd
|
|
| BLAKE2b-256 |
8952446970e1f40ca31ba1bc247748dea47817347f1b62d459e7143be9174cb6
|
File details
Details for the file recaller-0.2.0-py3-none-any.whl.
File metadata
- Download URL: recaller-0.2.0-py3-none-any.whl
- Upload date:
- Size: 17.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f766ae3ecb3ac82588bdd6c6fefd5303af9a12a574516a7ac1821d0e7fdaf2a9
|
|
| MD5 |
5833ade0bfb9a058b5ba454253787e21
|
|
| BLAKE2b-256 |
4bda10a55461ec6704f73c2a7192c9cc9940af48a7f27ae3d5fb3e3e8b55e272
|