postgr.esq — How To Use Each Endpoint

Complete setup and usage guides for accessing PostgreSQL mailing lists and repositories.

For LLM clients: see /llms.txt for a structured index of all programmatic interfaces, suitable for autonomous agents that need to bootstrap context about postgr.esq quickly.

OVERVIEW

postgr.esq offers multiple protocols to access the same data. Choose the one that fits your workflow:

1. IMAP — Email Client Access

Purpose: Read mailing lists in your email client

IMAP treats each PostgreSQL mailing list as an IMAP folder. Once connected, you can browse, search, and read messages directly in Thunderbird, Apple Mail, Outlook, or any IMAP-compatible email client.

Setup

1. Get the server address:
imap.postgr.esq:993 (SSL/TLS) or imap.postgr.esq:143 (STARTTLS)
2. Open your email client and add a new account:
  • Thunderbird: File → New → Existing Mail Account → Enter any username/password → Configure manually
  • Apple Mail: Mail → Preferences → Accounts → + → Other Mail Account
  • Outlook: File → Account Settings → Account Settings → New → More Options
3. Configure account settings:
Server: imap.postgr.esq Port: 993 (SSL) or 143 (STARTTLS) Username: (any name, e.g., "postgres") Password: (any password, e.g., "readonly") Security: SSL/TLS or STARTTLS
4. Subscribe to mailing lists: Once connected, you'll see all 54 PostgreSQL mailing lists as folders:
pgsql-hackers pgsql-general pgsql-bugs pgsql-admin pgsql-performance ... (47 more)
Click "Subscribe" to add them to your folder list. Start with pgsql-general (most active, general discussion).

Usage

Example 1: Browse recent discussions

1. Click "pgsql-hackers" folder

2. Your email client shows the newest messages first

3. Click a message to read the full thread

Example 2: Search across a mailing list

1. In Thunderbird: right-click a folder → Search Messages

2. Search for: "VACUUM performance" across pgsql-performance

3. Results show all matching messages with context

Example 3: Get notifications

1. Set your client to check for new mail every 1 hour (Settings)

2. Enable notifications when new messages arrive

3. You'll get alerts for new pgsql-hackers discussions

Tips

2. POP3 — Download and Keep Locally

Purpose: Download mailing list messages to your computer permanently

POP3 downloads messages and removes them from the server. Use this if you want to keep a local archive of PostgreSQL discussions.

Setup

1. Server address:
pop.postgr.esq:995 (SSL) or pop.postgr.esq:110 (STARTTLS)
2. Email client configuration:
Server: pop.postgr.esq Port: 995 (SSL) or 110 (STARTTLS) Username: <UUID>@<newsgroup>[.<slice>][?initial_limit=N&limit=N] Password: anonymous Leave email on server: YES (to keep archival copy)

Username format is required and strict. The server replies -ERR no UUID@ in mailbox name if the <UUID>@ prefix is missing.

  • UUID: any 32-hex-digit identifier, dashes optional. It is an opaque per-client tracking ID, not a credential — pick one once and reuse it. Example: deadbeefdeadbeefdeadbeefdeadbeef or dead-beef-dead-beef-dead-beef-dead-beef.
  • newsgroup: the inbox to read, e.g. pgsql.hackers, pgsql.general, pgsql.bugs.
  • .slice (optional): integer slice number; omit for the latest slice.
  • ?initial_limit=N&limit=N (optional): cap the number of messages returned.
  • Password: must be the literal string anonymous.

Example username: deadbeefdeadbeefdeadbeefdeadbeef@pgsql.hackers

Usage

Set your client to check every 24 hours. All new messages download and stay on your computer. Good for backup and offline research.

3. NNTP — Newsreader Access

Purpose: Access mailing lists via a classic NNTP newsreader

If you use a newsreader (Thunderbird, Usenet clients), connect via NNTP. Each mailing list appears as a newsgroup.

Setup

1. Server address:
nntp.postgr.esq:119 (plain text, no TLS)
2. Add newsserver to your NNTP client:
  • Thunderbird: Edit → Preferences → Network & Storage → Newsgroup Servers → Add
  • Other newsreaders: Add server with the address above
3. Subscribe to newsgroups:
pgsql-hackers pgsql-general pgsql-bugs (etc.)

Note: Newsgroup names use hyphens, not dots.

Usage

Browse and post to discussions like you would in a classic NNTP newsgroup. Good for developers familiar with that workflow.

4. Git — Clone a Mailing List as Repository

Purpose: Access mailing list history as a git repository for scripting/analysis

Clone any PostgreSQL mailing list as a git repository. Each message is a commit. Use this for data analysis, historical research, or building tools.

What these repositories actually are. The git URLs under /m/<inbox>.git are public-inbox v2 archives of mailing-list traffic. They are not mirrors of the upstream PostgreSQL source tree. Each commit is one email; the working tree contains the message's metadata and body. To clone the actual PostgreSQL source code or any other upstream project repository, go to its canonical home (for PostgreSQL: https://git.postgresql.org/git/postgresql.git). postgr.esq does not re-publish project source repos.

Setup

1. Clone via HTTPS:
git clone https://postgr.esq/m/pgsql-hackers.git
2. Or via SSH (if you have SSH access):
git clone ssh://git@git.postgr.esq:22/pgsql-hackers.git

Usage

Example 1: Find all messages by a specific author
git log --author="Tom Lane" --oneline | head -20
Example 2: Find commits about query optimization
git log --all --grep="query.*optim" --oneline
Example 3: Analyze discussion trends over time
git log --format="%ai" | cut -d' ' -f1 | sort | uniq -c | sort -rn

(Shows how many messages per day)

Tips

  • Large repositories: pgsql-hackers is ~100K commits. Clone may take 5-10 minutes.
  • Shallow clone (faster): git clone --depth 100 https://postgr.esq/m/pgsql-hackers.git
  • Script automation: Perfect for CI/CD pipelines that analyze historical discussions.

5. HTTP/REST — Programmatic Access

Purpose: Search and fetch mailing-list content with plain HTTP, no client library needed

Every indexed inbox has a search endpoint at https://postgr.esq/m/<inbox>/?q=<term>. Add &format=json to get JSON instead of the HTML view. Each inbox is also exposed as a public-inbox v2 git repository at https://postgr.esq/m/<inbox>.git.

Note: indexes are populated from upstream archives over time. If you query an inbox that's still importing you'll see “No messages yet.” or “Search is not available for this inbox.” That's import lag, not breakage. Inboxes confirmed populated today include pgsql-announce, pgsql-committers, pgsql-performance.

Setup

None. curl, wget, fetch, or any HTTP client.

Usage

The endpoint https://postgr.esq/m/<inbox>/?...&format=json accepts these query parameters. You may combine them; results are filtered by the intersection of the supplied predicates. At least one of q, subject, from, mid, after, or before must be present.

ParamMeaning
qFull-text query against subject, from, and body. BM25 by default; treated as a TRE regex if regex=1.
subjectFilter on the Subject header (BM25).
fromFilter on the From header (BM25; matches name or address fragments).
midExact Message-ID match (angle brackets stripped).
afterYYYY-MM-DD — only messages with Date ≥ this day.
beforeYYYY-MM-DD — only messages with Date < this day.
regex=1Interpret q as a TRE regex (uses pg_tre). URL-encode regex metacharacters.
k=NEdit-distance for fuzzy regex (0 = exact, 1+ allows that many typos). Only meaningful with regex=1; capped at 5.
format=jsonReturn JSON instead of HTML.
limit, oPage size (default 50, max 1000) and offset.
Example 1: Full-text search, JSON output
curl -s 'https://postgr.esq/m/pgsql-announce/?q=release&format=json' | jq '.total, .results[0]'

Returns the total hit count and the first match for “release” in pgsql-announce. Each result has subject, from, date, message_id, thread_url, message_url, and a BM25 relevance score.

Example 2: Multi-term search with paging
curl -s 'https://postgr.esq/m/pgsql-committers/?q=patch&format=json&limit=10&o=0' | jq '.results[].subject'

Search a different inbox; limit and o (offset) page through results. Drop &format=json to render the same query as a browseable HTML page.

Example 3: Filter by subject only
curl -s 'https://postgr.esq/m/pgsql-announce/?subject=release&format=json&limit=2' | jq '.total'

Restrict the BM25 match to the Subject header. Useful when the body is noisy or you only care about announcement-style headlines.

Example 4: Filter by sender
curl -s 'https://postgr.esq/m/pgsql-announce/?from=announce-noreply&format=json&limit=2' | jq '.results[0].from'

Match a fragment of the From header (display name or address).

Example 5: Exact Message-ID lookup
curl -s 'https://postgr.esq/m/pgsql-announce/?mid=177913516055.803.10890499456720421868@wrigleys.postgresql.org&format=json' | jq '.results[0].subject'

Equivalent to fetching the message page directly; convenient when you have a Message-ID from another source and want a JSON record.

Example 6: Date range
curl -s 'https://postgr.esq/m/pgsql-announce/?after=2024-01-01&before=2024-04-01&format=json&limit=5' | jq '.total, .results[].subject'

Q1 2024 only. after is inclusive, before is exclusive. You can combine date filters with any of the other predicates.

Example 7: Regex search
curl -s 'https://postgr.esq/m/pgsql-announce/?q=relea%5Bsz%5De&regex=1&format=json&limit=2' | jq '.total'

TRE regex via pg_tre. The example matches both “release” and “releaze”. Remember to URL-encode [, ], +, etc.

Example 8: Fuzzy regex (edit distance)
curl -s 'https://postgr.esq/m/pgsql-announce/?q=releas&regex=1&k=1&format=json&limit=2' | jq '.total'

k=1 tolerates one typo against the regex pattern; useful when subjects vary in spelling. Capped at k=5.

Example 9: Subscribe to new messages via Atom
curl -s 'https://postgr.esq/m/pgsql-announce/new.atom'

Standard Atom feed of recent messages. Poll no more often than once every 5 minutes — the feed only changes when new mail arrives, and aggressive polling wastes bandwidth on both sides. Most feed readers default to 30–60 minutes, which is appropriate.

Example 10: Fetch one message as raw RFC822
curl -s 'https://postgr.esq/m/pgsql-announce/177913516055.803.10890499456720421868@wrigleys.postgresql.org/raw'

Returns the original message bytes, including all headers and the unmodified body. URL pattern is /m/<inbox>/<message-id>/raw. Pipe into your own MIME parser or save to disk for archival.

Example 11: Bulk export a range as gzipped mbox
curl -s 'https://postgr.esq/m/pgsql-committers/1-100.mbox.gz' > pgsql-committers-1-100.mbox.gz zcat pgsql-committers-1-100.mbox.gz | head -20

URL pattern is /m/<inbox>/<begin>-<end>.mbox.gz, where begin and end are public-inbox v2 article numbers. Range is capped at 10000 messages per request. Use this to seed a local mbox archive without cloning the full git repo.

Example 12: Clone an inbox as a git repo
git clone https://postgr.esq/m/pgsql-announce.git

Public-inbox v2 layout. Run your own indexer or mirror locally for offline analysis. (Reminder: this is the mailing-list archive, not upstream PostgreSQL source.)

Tips

  • JSON or HTML: the same URL serves both — toggle with &format=json.
  • Paging: add ?limit=N&offset=M. Default limit is 200, max enforced server-side.
  • Browse first: visit https://postgr.esq/ in a browser to see the full inbox list and pick one.
  • Structured tools: for symbol search, call graphs (get_callees works; get_callers under repair), commitfest queries, etc., use the MCP server (Section 1) — those are JSON-RPC tool calls, not REST endpoints.

6. GraphQL — Structured Queries

Purpose: Ask for exactly the fields you want, in one round trip

The GraphQL endpoint exposes the same data as the REST API but lets the caller specify which fields they want. Useful when you'd otherwise be making N requests in a loop and stitching JSON together.

Endpoint: POST https://postgr.esq/api/graphql (Content-Type: application/json). GET with a ?query=… parameter also works for read-only queries. There is no /<inbox>/graphql URL — every Query field takes an explicit inbox: String! argument instead.

Interactive explorer: open https://postgr.esq/api/graphql in a browser and you get the GraphiQL UI with schema introspection, autocomplete, and query history. Programmatic clients (curl, fetch with Accept: application/json) keep getting the JSON API as before.

Schema

Top-level Query fields:

  • inbox(inbox: String!) — metadata: name, description, addresses, message count, last-modified.
  • messages(inbox: String!, limit: Int = 25) — recent messages.
  • message(inbox: String!, mid: String!) — one message by Message-ID, including body and attachments.
  • thread(inbox: String!, mid: String!) — full thread rooted at a Message-ID.
  • search(inbox: String!, query: String!, limit: Int = 50, offset: Int = 0) — BM25 search; results carry a relevance score.

Usage

Example 1: Inbox metadata + recent search results in one request
curl -s -X POST -H 'Content-Type: application/json' \ -d '{"query":"{ inbox(inbox:\"pgsql-announce\") { name messageCount lastModified } search(inbox:\"pgsql-announce\", query:\"release\", limit:3) { total results { subject date relevance } } }"}' \ https://postgr.esq/api/graphql

Returns both the inbox header and the first three search hits in one round trip. Compare with the REST API, which would require two calls.

Example 2: Pull a full thread by its root Message-ID
curl -s -X POST -H 'Content-Type: application/json' \ -d '{"query":"{ thread(inbox:\"pgsql-hackers\", mid:\"CABwTF4Wfs+u_O...@mail.gmail.com\") { rootMessageId messageCount messages { subject from date } } }"}' \ https://postgr.esq/api/graphql

Replace the mid value with a real Message-ID from the inbox you care about (you can copy one from a REST search response or the HTML view). Add body to the messages selection set if you also want the message text.

Tips

  • Errors are inline. A query that hits a missing inbox or a bad Message-ID still returns HTTP 200 with the failure inside an errors array. Check both data and errors in your client.
  • The inbox argument is required on every field. The endpoint is not scoped to any inbox; the schema deliberately surfaces the inbox name in each query so a single GraphQL request can span multiple inboxes by aliasing.
  • 1 MiB request body cap. Plenty for normal queries; chunk large mutations (none are exposed today — this is a read-only API).

7. MCP — For AI Agents

Purpose: Integrate with AI agents for automated PostgreSQL research

If you're building an AI agent (Claude Code, Kiro, Pi, or any MCP-compatible tool), connect to our MCP server.

Setup

Add to your MCP client config:
{ "mcp_servers": { "postgr-esq": { "type": "sse", "url": "https://postgr.esq/mcp/" } } }

Usage

Your agent now has 70+ tools available:

  • Search email archives
  • Explore code symbols
  • Trace call graphs (get_callees works; get_callers under repair)
  • Find related discussions
  • Semantic code search (experimental, currently slow — use keyword search for time-sensitive queries)
  • Git blame/log/diff
  • And more...

Pre-built skills for Claude, Kiro, and Pi available at:

git clone -b claude https://codeberg.org/postgresq/skills.git

Tips

  • Authentication: None required. Public read-only access.
  • Rate limits: Light rate limiting at the edge (~20 concurrent requests recommended). Use exponential backoff on HTTP 503.
  • Response format: Streaming HTTP for real-time responses

WHICH SHOULD I USE?

Use Case Best Endpoint Why
Browse discussions like email IMAP Familiar email client, threading, search built-in
Download archive to computer POP3 Keep local copy, work offline
Old-school newsreader fan NNTP Classic protocol, powerful newsreaders available
Build tools / analyze history Git Version control, scripting, full history
Quick searches / dashboards HTTP/REST Simple API, JSON responses, easy integration
AI agent integration MCP Purpose-built for AI, structured queries, 70+ tools

FINDING THE BERKELEY POSTGRES HISTORICAL ARCHIVE

postgr.esq mirrors the University of California, Berkeley POSTGRES project (1986–1995) — the academic predecessor of PostgreSQL — at /legacy/. The upstream archive at dsf.berkeley.edu/postgres.html has been effectively static since 1999; this mirror exists so the bytes survive if the upstream ever disappears.

Mailing list (1991–1997)

The original UCB POSTGRES hackers list, including design-era discussions from Stonebraker and the original implementors:

Project documentation, papers, FAQ

The archive contains the CONCERT Trouble Ticket System (a contrib sample application), not bug tickets — UCB never used a bug tracker for the project. Patch discussions and design exchanges are in the mailing list.

Source code

  • Latest official release (POSTGRES 4.2, 1994): browse extracted tree at /legacy/extracted/ or grab the tarball /legacy/postgres-v4r2/postgres-v4r2.tar.Z.
  • Earlier releases (v3.1, v4.0, v4.0r1, v4.1 incl. Sequoia 2000 fork, v4.2 alpha/beta) and the Postgres95 0.01–1.02 transition releases that became modern PostgreSQL: /legacy/oldpost/
  • Unofficial community ports and the unofficial port list: /legacy/oldpost/unofficial-ports/, UNOFFICIAL-PORT-LIST.
  • Code search via MCP (when the synthetic git repos are indexed): search_symbols(repository="ucb-postgres-v4r2", query="parser") and git_log(repository="ucb-postgres-v4r2"). The expected repository names are ucb-postgres-{v3r1,v4r0,v4r1,v4r2} and ucb-postgres95-{0.01..1.02}; consult /data for current indexing status.

TROUBLESHOOTING

IMAP won't connect

  • Check port: 993 (SSL) is standard, 143 (STARTTLS) also works
  • Verify hostname: imap.postgr.esq (no www, no .com)
  • Username/password: Can be anything (we don't authenticate)
  • Firewall: Some firewalls block IMAP. Use 993 (SSL) instead of 143

No folders appear after connecting

  • Check "Show all folders" or "List all folders" in your client
  • Might need to refresh/re-subscribe
  • Try subscribing to specific list: pgsql-general

HTTP search returns no results

  • Try simpler query: ?q=query instead of complex syntax
  • Check URL encoding: spaces should be + or %20
  • Use jq . to see response structure

Git clone too slow

  • Use shallow clone: git clone --depth 10 ...
  • Or clone only recent commits with: git log -p --max-count=100

OPENAPI 3.1 SPECIFICATION

Purpose: machine-readable contract for the entire HTTP surface

Every public HTTP endpoint described on this page is also documented in a single OpenAPI 3.1 document — search, single-message JSON, raw RFC 822, threads, Atom/RSS feeds, inbox metadata, the help/color/mirror text pages, the git smart-HTTP transport, /healthz, and the MCP JSON-RPC envelope.

The spec is served by Agora itself at /openapi.yaml and rendered as an interactive Swagger UI page at /api.html. Both are kept in lock-step with the route table in pkg/protocol/http/router.go; if a route is broken (today: the git-upload-pack POST) the spec flags it with x-status: pending rather than pretending it works.

Usage

Browse the API in your browser
https://postgr.esq/api.html

Loads Swagger UI from a CDN and points it at the live spec. Each endpoint has a “Try it out” button that issues real requests against the production service.

Fetch the raw spec
curl -s https://postgr.esq/openapi.yaml | head -40

Useful for embedding in your own docs site, validating with spectral lint, or generating clients with openapi-generator.

Generate a typed client
openapi-generator-cli generate -i https://postgr.esq/openapi.yaml -g python -o ./pgesq-client

Replace python with any of the supported targets (typescript-fetch, go, rust, etc.). The generated client speaks JSON only — for the HTML, Atom, RSS, and git smart-HTTP routes you still need a regular HTTP client.

Tips

  • Same URL, two formats: the search endpoint defaults to HTML; add &format=json for the JSON envelope. The OpenAPI document only describes the JSON variant.
  • Pagination: follow Link headers (RFC 8288 rel="next" / rel="prev" / rel="first") instead of recomputing offsets. The body's has_more is also reliable.
  • MCP for agents: the POST /mcp/ entry in the spec describes the JSON-RPC envelope. AI agents should use a real MCP client library; the OpenAPI is there mostly for debugging.

QUESTIONS?

Something not working as described? Get in touch. We want these endpoints to work perfectly.