poke

module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: MIT

README

Poke your APIs. Pogo through your requests.

Website · Docs · Keys · Security

CI Go Reference


poke is curl with a memory. Type it where you would have typed curl — same flags, same output, same exit code, because it hands your arguments to the real curl binary. It also writes down what happened.

pogo is a terminal UI over everything poke has run: find a request, inspect it, replay it, change it and run it again, diff two responses.

Your shell remembers the command. It does not remember the response, the status, how long it took, or which of the six nearly identical curl lines in your history was the one that worked.

Quickstart

# 1. install (needs curl, which you already have)
curl -fsSL https://raw.githubusercontent.com/rmpato/poke/main/install.sh | sh

# 2. make a request, exactly as curl would
poke https://api.github.com/zen

# 3. open everything you have run
pogo
pogo showing recent requests with method, host, path, status, duration, size and age

Then press ctrl+k. Everything pogo can do is there, searchable by name, with each command's key beside it — so you learn the shortcuts by using them and stop needing the palette.

pogo's command palette, listing every action with its keyboard shortcut

Or go straight to it: ↑↓ move, ⏎ inspects, r replays, e edits and runs, / searches, d diffs two responses.

You never saved anything. You never named a collection. It was just there.

Other ways to install
go install github.com/rmpato/poke/cmd/poke@latest
go install github.com/rmpato/poke/cmd/pogo@latest
git clone https://github.com/rmpato/poke && cd poke && make install

Update later with poke --update. Both binaries work on macOS and Linux, and curl is the only runtime dependency.

What you get

Find it

The sidebar shows what is actually in your history — filters, your collections, the hosts you hit — with counts. tab focuses it, ⏎ filters by a row, and the search box then shows the query it ran, which is how the syntax gets learned without reading anything.

Or type it directly with /: free text, or method:POST, status:4xx, host:api.example.com, collection:auth, is:starred, is:failed. t cycles grouping — chronological, by host, by collection.

Read it

Request and response, headers, query parameters, bodies, with a JSON tree you can fold and secrets masked until you ask (S).

pogo inspecting a request and response, with a masked bearer token and a foldable JSON tree

Change it and run it

e opens the request as fields. ctrl+r runs it as a new entry; the original is never touched.

pogo editing a request as structured fields

Edits are applied to your original command rather than regenerating one, so a request carrying --cacert, --resolve or -k keeps every one of those options when you change a header.

See what changed

Replay something and press d: the request you replayed is already marked, so "what changed?" is one keystroke rather than four. JSON-aware — reordered keys and reformatted whitespace are not differences, changed values are — and response headers are diffed too.

pogo comparing two responses from the same endpoint, showing one changed field

See where the time went

Straight from curl's own instrumentation. Nothing is estimated; if your curl cannot report it, pogo says so.

pogo showing DNS, connect, wait and download phases

Keep secrets out of your history

Write the request with variables and it runs against real values while your history stores the braces:

poke -H "Authorization: Bearer {{token}}" '{{base}}/users/42'

curl gets the token. history.jsonl gets {{token}}. A replay months later resolves it again — against whatever the variable holds then. E switches environment, so you can replay the same request against staging and production and d the results.

→ Environments, variables, collections and HAR import

Why not just shell history?

history | grep curl

finds the command you typed. It does not tell you:

shell history pogo
the command ✅ ✅
what came back ❌ ✅
status code ❌ ✅
how long it took ❌ ✅
where the time went ❌ ✅
search by status, host or collection ❌ ✅
replay retype it r
edit and replay retype it e
compare two responses ❌ d

pogo is HTTP-aware history for your terminal. That is all it is trying to be.

Storage and security

History lives in ~/.local/share/poke as append-only JSONL plus payload files. Directory 0700, files 0600, nothing encrypted. Your requests never leave the machine.

[!IMPORTANT] Request headers routinely carry credentials, and by default poke stores them as sent. Your history file will contain bearer tokens, cookies and API keys in plain text. That is a trade-off, not an accident: stripping them would make replay silently fail to authenticate. pogo masks secrets on screen; the file on disk holds the real thing.

Three ways to change that, in increasing order of strength:

POKE_REDACT=store poke ...                                # strip secrets before writing
poke --poke-no-capture ...                                # do not record this one
poke -H "Authorization: Bearer {{token}}" '{{base}}/me'   # never capture it at all

→ What is stored, what it exposes, and how to change it

Documentation

docs/keybindings.md Every key, and the search syntax
docs/environments.md Variables, environments, collections, HAR import
docs/security.md What lands on disk and how to control it
docs/architecture.md How poke wraps curl without changing it
docs/runbooks/ Releasing, screenshots, triage, recovery
CONTRIBUTING.md Building, testing, and the two rules

How it works, briefly

   poke <curl args> ──▶ curlargs ──▶ runner ──▶ the real curl
                        (metadata)   (execute + capture)
                                        │
                                     capture ──▶ store  (JSONL + blobs, local)
                                        ▲          │
                                        │          ▼
                          pogo replays ─┘         tui

Both binaries share one execution path, so a replay is the same code running the same argv — not a reconstruction of it. Storage knows nothing about the UI, the UI never touches the filesystem, and the curl layer is testable without a network.

Go, Bubble Tea, Lip Gloss, Bubbles. No database, no cgo, no daemon. Everything but the two commands lives under internal/ deliberately: poke is a tool, not a library.

→ Architecture, including the two curl behaviors poke has to restore

Development

make          # build both binaries into ./bin
make check    # gofmt, vet, test, race — the same gates CI runs
make help     # everything else

Tests run the real curl against local httptest servers; nothing touches the public internet. Start with CONTRIBUTING.md.

Roadmap

The goal everything below serves:

You type poke where you type curl, and nothing changes. Then you open pogo and it teaches you itself — reading the docs is for advanced use, not for getting started.

Shipped in v0.2.0: structured request editor · collections and grouping · environments and {{variables}} · HAR import · response header diffing · per-host redaction rules.

Ideas, not promises, ordered by how soon a new user hits the gap.

Finish the drop-in promise

poke already matches curl on the things that matter to a script — exit codes, -w output, stdout and stderr byte for byte, every flag. Two exceptions remain, and they are exactly the two that break alias curl=poke:

curl poke
--version curl 8.6.0 … poke v0.2.0 …
--help curl's usage poke's usage

Harmless when you type poke on purpose, wrong when something in your toolchain shells out to curl --version to decide what it can do. An opt-in alias mode (POKE_AS_CURL=1) that forwards both to curl would make the substitution total, and make alias curl=poke a thing we can recommend rather than a thing that mostly works.

More of the TUI that teaches itself

Most of this shipped in v0.3.0 — the command palette, the sidebar, and arming the comparison after a replay. What is left:

  • Suggest the next step, not just the last result. After an edit-and-run, or a 401, or a first search that found nothing, there is usually one obvious thing to do next and nothing says so.
  • A first-run pass. Someone opening pogo with three requests in history should be shown the palette once rather than having to notice the footer.
  • The detail view still has to be learned — five numbered panes, and the body view mode is a key you have to know about.

Autocomplete that learns from your own history

You retype Content-Type: application/json a hundred times a week, and you cannot remember whether that internal header is X-Acme-Signature or X-ACME-Sig. poke already knows: it has watched you send both.

  • Header names from the ones you have actually used, ranked by recency.
  • Header values per name — application/json for Content-Type, the hosts you have hit for Host.
  • Never a value for a sensitive header. Suggesting a real bearer token would undo the point of keeping them out of history; the suggestion is Bearer {{token}} instead.
  • {{variables}} from the active environment, so a wrong name surfaces while typing rather than after a 401.
  • In the search bar: filter keys, then their values — your hosts, your collections, the status codes you have actually seen.

Mechanically an index over loaded history plus textinput's existing suggestion support, not a new widget.

Postman import and export

The people who would most like pogo are the ones with a Postman collection they inherited and do not enjoy opening.

  • Import a collection (v2.1): folders become collections, requests become history entries you can inspect, edit and replay like anything else.
  • Import a Postman environment export straight into environments.json — the syntaxes already agree, since Postman spells variables {{token}} too. That alignment was not planned, but it makes the mapping exact rather than approximate.
  • Export a poke collection back out, so handing work to a colleague who lives in Postman does not mean retyping it.

Same shape as the HAR importer, which is the proof the seam works.

Group by endpoint, not just by host

/users/42, /users/43 and /users/44 are one endpoint and three rows. At a few hundred requests they are thirty rows and the shape of your traffic is invisible.

  • Normalize path segments that look like identifiers — numeric, UUID, long hex — into /users/:id.
  • A grouping mode that collapses them, with the count and the mix of statuses.
  • Which turns "this endpoint started 500ing at 14:20" into something you see rather than something you search for.

Further out

  • export a collection as a runnable script
  • chained requests: use a value from one response in the next
  • a --diff flag on pogo, for comparing responses in CI
  • response body search, which needs an index
  • size and timing trends per endpoint
  • .pokerc per project, so a repo carries its own environment

An issue describing the moment you wanted something beats a feature request.

License

MIT — see LICENSE.

Directories

Path Synopsis
cmd
pogo command
Command pogo is a terminal UI over the request history poke records.
Command pogo is a terminal UI over the request history poke records.
poke command
Command poke runs curl and remembers what it ran.
Command poke runs curl and remembers what it ran.
internal
capture
Package capture turns a curl invocation into a history entry.
Package capture turns a curl invocation into a history entry.
clipboard
Package clipboard copies text to the system clipboard.
Package clipboard copies text to the system clipboard.
config
Package config resolves where poke keeps its data and how it behaves.
Package config resolves where poke keeps its data and how it behaves.
curlargs
Package curlargs extracts human-meaningful metadata from a curl command line.
Package curlargs extracts human-meaningful metadata from a curl command line.
curledit
Package curledit applies structured edits to a curl command line.
Package curledit applies structured edits to a curl command line.
environment
Package environment resolves {{variable}} references in a curl command.
Package environment resolves {{variable}} references in a curl command.
harimport
Package harimport turns a browser's HAR export into poke history.
Package harimport turns a browser's HAR export into poke history.
history
Package history defines the record poke writes and pogo reads.
Package history defines the record poke writes and pogo reads.
runner
Package runner executes curl and captures what happened.
Package runner executes curl and captures what happened.
selfupdate
Package selfupdate replaces the installed binaries with the latest release.
Package selfupdate replaces the installed binaries with the latest release.
store
Package store persists captured requests.
Package store persists captured requests.
tui
Package tui implements pogo, the terminal UI over poke's request history.
Package tui implements pogo, the terminal UI over poke's request history.
version
Package version carries build metadata, injected at link time by the Makefile and by goreleaser.
Package version carries build metadata, injected at link time by the Makefile and by goreleaser.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL