Skip to main content

PST Query

PyPI CI

PST Query is an offline command-line tool for searching an Outlook PST archive. It keeps a local SQLite cache with an FTS5 index so agents can locate concise message candidates before retrieving a complete message.

The PST is opened read-only and remains authoritative. The SQLite index is a disposable cache: delete it to force a complete rebuild. A working pypff / libpff binding is required to create or synchronize the cache.

Installation

pypff, the Python binding for libpff, is required to read PST archives. It is a native extension and upstream does not provide a Linux wheel for Python 3.13, so pip install pstq alone cannot create or synchronize a cache.

The supported installation is the included devcontainer. It builds the pinned, checksum-verified libpff-python source release for Python 3.13, then installs the project dependencies:

$ git clone https://github.com/JNevrly/pstq.git
$ cd pstq

Open the clone in VS Code and choose Dev Containers: Reopen in Container. After the container is ready, verify the installation with:

$ uv run pstq --help

For a manual installation, first build and install a compatible pypff wheel for the target Python and platform, then install pstq. The devcontainer Dockerfile is the reference build recipe.

Configuration

Configure one PST and one SQLite cache path in YAML. The directory containing index_path must already exist because cache builds use a sibling temporary file before atomically replacing the index.

archive:
  pst_path: /archives/mail.pst
  index_path: /var/cache/pstq/mail.sqlite
history:
  owner_emails:
    - user@example.com
  owner_names:
    - Example User
  timezone: Europe/Prague

Pass the file to every command:

$ pstq --config pstq.yaml status

Generate a commented configuration template with:

$ pstq --get-config-template pstq.yaml

Onacol environment overrides are also supported:

$ export PSTQ_ARCHIVE__PST_PATH=/archives/mail.pst
$ export PSTQ_ARCHIVE__INDEX_PATH=/var/cache/pstq/mail.sqlite
$ pstq status --json

Workflow

Start by checking the cache and inspecting its folder paths:

$ pstq --config pstq.yaml status --json
$ pstq --config pstq.yaml folders --json

search checks the configured PST's path, size, and modification time before querying. If the source changed, it synchronizes the cache first; otherwise it queries immediately. Results are capped at 20 by default and at 100 maximum. They deliberately omit message bodies.

$ pstq --config pstq.yaml search 'Capon calibration' --json
$ pstq --config pstq.yaml search --from 'Sender' --after 2025-01-01 --json
$ pstq --config pstq.yaml search 'Capon calibration' --limit 20 --offset 20 --json
$ pstq --config pstq.yaml search invoice \
    --from-owner \
    --to accounting@example.com \
    --after 2025-01-01 \
    --before 2026-01-01 \
    --folder 'Top of Outlook data file/Projects' \
    --has-attachment \
    --limit 10 \
    --json

Search supports FTS5 query syntax. Quote punctuation-heavy or exact phrases, and prefer the --to filter for recipient email addresses. QUERY may be omitted when one or more filters are supplied. Filter-only results are ordered by date descending and then stable message ID, with undated messages last; their score is 0.0 and snippet is empty.

Use --offset to request later bounded pages. It is zero-based and defaults to 0; retain the same query and filters, then increase it by the page --limit (for example, --limit 20 --offset 20 requests the second page). Each JSON response remains an array of at most 100 records; a response shorter than the requested limit is the final page. The source and cache must remain unchanged while traversing pages to preserve a stable result set.

Each result has a stable id. Search JSON includes id, date, from, to, subject, folder, snippet, and score. Pass the selected ID to show to retrieve the persisted message with cleaned body content by default:

$ pstq --config pstq.yaml show edc4f1c4c743ad49a590c83842fd889f:2128196 --json
$ pstq --config pstq.yaml show edc4f1c4c743ad49a590c83842fd889f:2128196 --full --json

Default show reads only SQLite and returns persisted cleaned content. show --full synchronizes when needed, then reads the current preferred body from the configured PST, including quoted history. Recovered quoted-history records keep their persisted derived body because they have no independent source item. The same selection applies to human-readable and JSON output. The returned record includes the selected body representation, body format, participants, folder, message headers, relationship metadata, and attachment count.

If the manually curated PST omits Sent Items, configure history.owner_emails and, where needed, exact display-name history.owner_names aliases. During synchronization PSTQ conservatively recovers complete Outlook reply and forwarded-message blocks in English, Czech, German, and Japanese. Those messages participate in normal search, show, and thread results. Use --from-owner to match any configured owner alias. Ambiguous, edited, or incomplete quotation blocks are intentionally omitted. history.timezone must be an IANA timezone and is applied to quoted timestamps that omit an offset.

Command Reference

Command Purpose
status [--json] Report source and cache metadata plus freshness.
folders [--json] List indexed folder paths and stable IDs.
search [QUERY] [filters] [--json] Synchronize when needed, then run a bounded FTS5 or filter-only search.
show MESSAGE_ID [--full] [--json] Retrieve cleaned content from SQLite, or use --full to read the current source body.
thread MESSAGE_ID [--json] Reconstruct a related-message view from persisted cache data.
attachments MESSAGE_ID [--json] List persisted attachment metadata from SQLite.
attachment ATTACHMENT_ID --output FILE Synchronize if needed, then extract one original attachment through its cached PST locator. --output must name a path that does not exist; PSTQ never overwrites an existing file.

search filters are --from, --to, --after, --before, --folder, --has-attachment, --from-owner, --limit, and --offset. The full agent contract, including JSON schemas, stable ID formats, error envelopes, cache access, limits, and known libpff limitations, is maintained in the CLI itself. Read it before invoking a command:

$ pstq --help
$ pstq search --help

Development

Run all formatting, linting, type checking, and tests with:

$ uv run tox

This project uses Backlog.md for task tracking. The workflow is documented in AGENTS.md.

Free software: MIT license.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pstq-0.2.0.tar.gz (145.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pstq-0.2.0-py3-none-any.whl (39.7 kB view details)

Uploaded Python 3

File details

Details for the file pstq-0.2.0.tar.gz.

File metadata

  • Download URL: pstq-0.2.0.tar.gz
  • Upload date:
  • Size: 145.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pstq-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f22fe0e220e4fd438fe2773f22471514a51f7ea2e9804d6ebe8e9236b963fa84
MD5 80c22bdb658c3674bf8a6e38fb12b39d
BLAKE2b-256 07828ac8c8d32953c2e9632e55eabfb128e6f9e266daf8f52770fd9f0dab3dc1

See more details on using hashes here.

Provenance

The following attestation bundles were made for pstq-0.2.0.tar.gz:

Publisher: release.yml on JNevrly/pstq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pstq-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pstq-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 39.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pstq-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fa753594f3f07d948e91a5eebf4cda165e5507aa30777949712638c831d98f05
MD5 b8a8dcef4bc10ee62b6dc4f2379fb894
BLAKE2b-256 232981cb9805d06f70baebac7fdc24db0b56257de31bdc3c720406ecc261e779

See more details on using hashes here.

Provenance

The following attestation bundles were made for pstq-0.2.0-py3-none-any.whl:

Publisher: release.yml on JNevrly/pstq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page