PromptCapsule
Pass the prompt, not the payload. Lossless prompt capsules for agent-to-agent handoff: inline when small, vault-backed when large, fail-closed integrity by default.
pip install promptcapsule # core: zero dependencies
pip install "promptcapsule[vault]" # adds S3 and GitHub Gist backends
Python 3.8–3.13 · MIT · 130+ tests on Linux, macOS and Windows · Changelog
What it does
A capsule is a short string one agent hands to another. The receiver gets the exact original prompt back, or an exception — never silently corrupted text.
| 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 a shared backend |
Inline capsules are about packaging, not shrinking: very short prompts get longer (a 42-byte prompt becomes a 78-character capsule). The size win is vault mode, where a 2,480-byte prompt becomes a 41-character capsule.
Quick start
from promptcapsule import PromptCapsule, IntegrityError
from promptcapsule.backends import SQLiteBackend
pc = PromptCapsule()
capsule = pc.compress("You are a helpful Python coding assistant.")
# cap_i_45b72418_c-o81FI7k^N>xZy$Vkm8NGr`z2&gQ{$j?(q&QHnAOIJuNF3v12Nz5zJ0{}*34}|
vault = SQLiteBackend("prompts.db")
long_prompt = "You are a senior software architect reviewing a pull request. " * 40
long_capsule = pc.compress(long_prompt, vault_backend=vault)
# cap_v_66152d9b_sql_JME_tuuntLjuEVRztHpH4A (the key part is random)
try:
text = pc.decompress(long_capsule, vault_backend=vault).text
except IntegrityError:
... # tampered or corrupted: do not use
decompress is strict by default: a checksum mismatch raises IntegrityError.
Signed capsules (authenticity)
The 8-hex checksum detects corruption, but anyone can compute it. To know a capsule came from someone holding a shared secret, sign it with HMAC-SHA256:
from promptcapsule import SignatureError
capsule = pc.compress("Summarise the Q3 report", sign="shared-secret")
try:
text = pc.decompress(capsule, verify_signature="shared-secret").text
except SignatureError:
... # wrong key, tampered, or unsigned capsule: do not use
- Receivers must pass the key. When
verify_signatureis a key (orTrue), unsigned capsules — including ones with the_sig_…suffix stripped — are rejected before anything is decompressed or fetched from a vault. With the defaultverify_signature=None, unsigned capsules are accepted. sign=True/verify_signature=Trueread the key fromPROMPT_CAPSULE_HMAC_KEY. Empty keys are rejected.- The signature is a 128-bit truncated HMAC-SHA256 over the prompt text, compared in constant time.
- Signing proves who created the prompt, not when: a valid capsule can be replayed.
Command line
echo "You are a helpful assistant" | promptcapsule pack --file -
promptcapsule pack --file long_prompt.txt --vault prompts.db
promptcapsule unpack --file capsule.txt --vault prompts.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 |
| 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 capsule without a backend, 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; bounded decompression.
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.
How it compares
| Tool | Approach | Trade-off |
|---|---|---|
| LLMLingua | Lossy semantic compression | Smaller prompts, but not exact |
| LangChain Hub | Hosted prompt registry | Tied to the LangChain ecosystem |
| zlib directly | General compression | No integrity, no vault, no signing |
| PromptCapsule | Lossless capsule + pluggable vault | Needs a shared backend for long prompts |
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.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| promptcapsule-0.2.1.tar.gz | 31.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| promptcapsule-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.9 kB
Release files / promptcapsule-0.2.1.tar.gz
| Download URL | promptcapsule-0.2.1.tar.gz |
|---|---|
| Size | 31.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a47cfb7b3e256e263c116048b41739cea7d96b6b19c04244051ee8ce134c45e1
|
|
BLAKE2b-256 checksum How to use checksums |
4f397241a77e6a0c52350e90cac1a31bd5cff89985f6a25f3114845021586a60
|
| 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.2.1-py3-none-any.whl
| Download URL | promptcapsule-0.2.1-py3-none-any.whl |
|---|---|
| Size | 18.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
da21c5cb29ee46eadd8128e347ccd73648c056eb6aa3e59c714666bc9c54b4a9
|
|
BLAKE2b-256 checksum How to use checksums |
e61f323b7b1f7e60c63a3e9d64ff0936a114d1f541c447a67611198b07183951
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|