Skip to main content

PromptCapsule

Pass the prompt, not the payload.

Lossless prompt capsules with fail-closed integrity for agent-to-agent handoff.

As simple as a string. pack(text) turns a prompt into a capsule string you can store, log, or hand to another agent. unpack(capsule) gives back the exact original, or raises. Never silently corrupted text.

pip install promptcapsule            # core: zero dependencies
pip install "promptcapsule[vault]"   # adds S3 and GitHub Gist backends

Python 3.8–3.13 · MIT · 180+ tests on Linux, macOS and Windows · Frozen format spec · Changelog

Quick start

from promptcapsule import pack, unpack

capsule = pack("You are a helpful Python coding assistant.")
# 'cap_i_45b72418_c-o81FI7k^N>xZy$Vkm8NGr`z2&gQ{$j?(q&QHnAOIJuNF3v12Nz5zJ0{}*34}|'

prompt = unpack(capsule)   # the exact original, or an exception

Nothing to configure. Prompts over 500 bytes are stored in a local SQLite vault, ~/.promptcapsule/vault.db, created on first use with owner-only permissions:

long_prompt = "You are a senior software architect reviewing a pull request. " * 40
capsule = pack(long_prompt)   # 'cap_v_66152d9b_sql_JME_tuuntLjuEVRztHpH4A' (key part is random)
unpack(capsule)               # works wherever that vault is available

A vault capsule is a verified reference to the stored text: the agent that unpacks it needs the same vault. To share one, point both sides at it with PROMPT_CAPSULE_VAULT=/shared/team.db, or pass vault= (a path or any backend below) to pack and unpack.

What's in a capsule

Mode When Format What travels
Inline ≤ 500 bytes (UTF-8) cap_i_<checksum8>_<zlib+base85> The whole prompt, self-contained
Vault > 500 bytes cap_v_<checksum8>_<key> A random key; the prompt stays in the vault

Capsules are a packaging format, not a way to make prompts smaller: an inline capsule is usually a little longer than the prompt it carries, and the model always sees the full, unchanged text.

The format is frozen. SPEC.md defines it byte for byte, and every capsule produced since 0.1.0 will decode in every future release. CI enforces this with fixed test vectors.

Signed capsules (authenticity)

The 8-hex checksum detects corruption, but anyone can compute it. To check that a capsule was made by someone holding a shared secret key, sign it with HMAC-SHA256:

from promptcapsule import SignatureError

capsule = pack("Summarise the Q3 report", sign="shared-secret")

try:
    text = unpack(capsule, verify_signature="shared-secret")
except SignatureError:
    ...  # wrong key, tampered, or unsigned capsule: do not use
  • Receivers must pass the key. When verify_signature is a key (or True), unsigned capsules — including ones with the _sig_… suffix stripped — are rejected before anything is decompressed or fetched from a vault. Without a key, unsigned capsules are accepted.
  • sign=True / verify_signature=True read the key from PROMPT_CAPSULE_HMAC_KEY. Empty keys are rejected.
  • The signature is a 128-bit truncated HMAC-SHA256 over the prompt text, compared in constant time.
  • A valid signature proves only that the signer holds the shared key. It does not identify which key holder signed, and it does not prove when: a signed capsule can be replayed.

For metadata, use the class API: PromptCapsule().decompress(capsule, ...) returns a result with .text, .verified (checksum only) and .signed (True only when a signature was checked against the key).

When to use it

  • Handing an exact prompt between agents, processes or services through a channel that should carry a short string: a queue message, a tool argument, a log line, a database column.
  • You need to know the text arrived unchanged, and optionally that it came from a holder of a shared key.
  • Referring to long prompts from tickets, configs or commits without pasting them in.

When not to use it

  • To cut tokens or cost. PromptCapsule never shortens or rewrites what the model reads; use a prompt-optimization tool for that.
  • To keep prompts secret. Capsules and vaults are not encrypted.
  • When the receiver can't reach your vault. Long-prompt capsules need a vault both sides can read; otherwise send the text itself.
  • For identity, access control or replay protection. Add those in your application.
  • For general file archiving. Use gzip or zstd.

Command line

echo "You are a helpful assistant" | promptcapsule pack --file -
promptcapsule pack --file long_prompt.txt        # long prompts use the default vault
promptcapsule unpack --file capsule.txt --vault team.db
promptcapsule verify --file capsule.txt           # check without printing the prompt
promptcapsule inspect --file capsule.txt --json   # mode, checksum, signed?

export PROMPT_CAPSULE_HMAC_KEY=...                 # or use --key-file PATH
promptcapsule pack --file prompt.txt --sign
promptcapsule unpack --file capsule.txt --require-signature

Keys are never accepted as command-line arguments, so they stay out of shell history and process listings.

Backends

Backend Import Notes
In-memory InMemoryBackend() Tests and prototypes
SQLite SQLiteBackend("prompts.db") Local file, no dependencies; the default vault
GitHub Gist GitHubGistBackend(token=...) Private gists; retrieval requires the gist to belong to the token's user
AWS S3 S3Backend(bucket=..., region=...) Keys confined to the configured prefix

All live in promptcapsule.backends. Both agents must reach the same backend. Vault keys are random (secrets.token_urlsafe), and the capsule checksum is bound to the stored content, so swapping keys between capsules fails verification.

Custom backend: subclass promptcapsule.core.VaultBackend and implement store(text, checksum) -> key, retrieve(key), and retrieve_with_checksum(key) -> (text, checksum).

Limits

Limit Value
Inline threshold 500 bytes
Max prompt / capsule / decompressed size 10 MiB each (blocks zip bombs)
Checksum in capsule First 8 hex chars of SHA-256

Oversized input raises SizeLimitError.

Errors

All exceptions derive from PromptCapsuleError:

Exception Raised when
IntegrityError Checksum mismatch, bad checksum prefix, vault content mismatch
SignatureError Wrong key, tampered or missing signature (subclass of IntegrityError)
FormatError Malformed capsule, trailing or truncated zlib data
SizeLimitError Input or output over 10 MiB
VaultError Vault not found or unreachable, or backend failure

IntegrityError, FormatError and SizeLimitError also subclass ValueError.

Security model

Provides: exact reconstruction; tamper and corruption detection; optional authenticity with a shared secret key; zip-bomb protection (decoding is size-capped).

Does not provide:

  • Encryption. Anyone holding an inline capsule, or with access to the vault, can read the prompt.
  • Identity or access control. Protect your vault with its own ACLs (IAM, private gists, file permissions).
  • Replay protection. Add your own nonce or expiry if you need it.

See TRUST.md for the full threat model.

Development

pip install -e ".[dev]"
pytest

See CONTRIBUTING.md. Report security issues privately via GitHub Security Advisories or data.pycap@gmail.com.

License

MIT — see LICENSE.

Release files for promptcapsule 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for promptcapsule 0.3.1
File Size Uploaded
promptcapsule-0.3.1.tar.gz 39.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for promptcapsule 0.3.1
File Interpreter ABI Platform
promptcapsule-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 60.4 kB

Release files / promptcapsule-0.3.1.tar.gz

Download URL promptcapsule-0.3.1.tar.gz
Size 39.2 kB
Tags Source
SHA-256 checksum
How to use checksums
50ed78dccd4e6366093a3d49262a13170f04ec0f15b5b5163958b5389e2dd349
BLAKE2b-256 checksum
How to use checksums
654bf28420eb4a418e137139c2a54a6f94e22472665b20fe23393dbed80b204b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / promptcapsule-0.3.1-py3-none-any.whl

Download URL promptcapsule-0.3.1-py3-none-any.whl
Size 21.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dda6ff7cab9aa938732fa1edd4f6638240e1e6e8121ee12ced1ceea149bac4d7
BLAKE2b-256 checksum
How to use checksums
c1f09b712aad34c2bff66b8243d56de0f7f5fb39295d8f635edc44090b176cc1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release 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