PST Query
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.
Metadata
Release files for pstq 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pstq-0.2.0.tar.gz | 145.8 kB | With provenance | Details for pstq-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pstq-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for pstq-0.2.0-py3-none-any.whl |
Total release size: 185.5 kB
Release files / pstq-0.2.0.tar.gz
| Download URL | pstq-0.2.0.tar.gz |
|---|---|
| Size | 145.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f22fe0e220e4fd438fe2773f22471514a51f7ea2e9804d6ebe8e9236b963fa84
|
|
BLAKE2b-256 checksum How to use checksums |
07828ac8c8d32953c2e9632e55eabfb128e6f9e266daf8f52770fd9f0dab3dc1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency logRelease files / pstq-0.2.0-py3-none-any.whl
| Download URL | pstq-0.2.0-py3-none-any.whl |
|---|---|
| Size | 39.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fa753594f3f07d948e91a5eebf4cda165e5507aa30777949712638c831d98f05
|
|
BLAKE2b-256 checksum How to use checksums |
232981cb9805d06f70baebac7fdc24db0b56257de31bdc3c720406ecc261e779
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency logSecurity for pstq 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for pstq-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.