Email trap (mail catcher) for the Stapel framework — capture outbound email in dev/staging
Project description
stapel-mailtrap
An email trap (mail catcher) for dev/staging: outbound mail is captured into a
TrappedEmailjournal instead of being sent, and browsed through a read-only "Mail" API. Plugs into stapel-notifications' email-provider seam by dotted path — no module-to-module import.
Part of the Stapel framework — composable Django apps that deploy as a monolith or as microservices without changing module code.
Install
pip install stapel-mailtrap
# settings.py
INSTALLED_APPS = [
# ...
"stapel_mailtrap",
]
# urls.py
path("mailtrap/", include("stapel_mailtrap.urls"))
python manage.py migrate
Trapping notifications email
Point stapel-notifications' email provider at the trap — one setting, no code:
STAPEL_NOTIFICATIONS = {
"EMAIL_PROVIDER": "stapel_mailtrap.provider.MailtrapEmailProvider",
}
Every email stapel-notifications would send is now persisted and inspectable
instead. The provider matches the notifications email duck type
(send(recipient, subject, html_body, headers)) — see MODULE.md §3.
Trapping any backend
For a host whose own mail path should trap, call the service directly:
from stapel_mailtrap import trap_email
trap_email(
to_email="alice@example.com",
from_email="bot@example.com",
subject="Your code",
body_html="<b>123456</b>",
body_text="123456",
attachments=[{"filename": "invoice.pdf", "content_type": "application/pdf", "size": 8123}],
scope_key="workspace-42", # opaque host scope
)
It persists the row and emits mailtrap.email.trapped transactionally.
Attachments are metadata only — raw bytes are never stored.
HTTP API ("Письма" / Mail)
Read-only (the trap is filled by the provider / trap_email, never via the API):
| Route | Meaning |
|---|---|
GET mailtrap/emails/ |
Trapped emails, newest first, paginated (anchor); ?scope_key= filters |
GET mailtrap/emails/<uuid>/ |
One trapped email — full HTML/text bodies + attachment metadata |
Default permission is staff/service only (trapped mail can contain OTPs and
magic links). Override permission_classes and set a SCOPE_PROVIDER for
per-tenant visibility — see MODULE.md.
Retention
STAPEL_MAILTRAP = {
"MAX_EMAILS": 1000, # hard cap on stored rows (0 disables)
"TTL_DAYS": 30, # delete rows older than N days (0 disables)
}
Run the sweep from any scheduler:
python manage.py purge_trapped_emails # --dry-run supported
Or schedule the Celery task (celery is an optional host dependency):
CELERY_BEAT_SCHEDULE = {
"purge-trapped-emails": {
"task": "stapel_mailtrap.tasks.purge_trapped_emails",
"schedule": crontab(hour=3, minute=0), # nightly
},
}
Settings
All configuration lives in the STAPEL_MAILTRAP namespace (dict setting, flat
setting, or env var — resolved lazily):
| Key | Default | Meaning |
|---|---|---|
SCOPE_PROVIDER |
"stapel_mailtrap.scope.DefaultScopeProvider" |
Dotted path to a ScopeProvider subclass — the multi-tenant seam. |
MAX_EMAILS |
1000 |
Retention count cap (0 disables). |
TTL_DAYS |
30 |
Retention age cap in days (0 disables). |
STORE_BODY |
True |
False stores the envelope only (bodies empty). |
comm surface
| Kind | Name | Contract |
|---|---|---|
| Event | mailtrap.email.trapped |
{email_id, to, from?, subject, scope_key, attachment_count, trapped_at} — schema |
Extension points
See MODULE.md — the agent-facing map of every fork-free seam
(settings, the ScopeProvider seam, the notifications email-provider adapter
with its duck-typed contract, serializer seams, permissions, comm surface,
system checks).
Development
pip install -e . && pip install pytest pytest-django pytest-cov ruff jsonschema
./setup-hooks.sh
pytest tests/
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stapel_mailtrap-0.1.2.tar.gz.
File metadata
- Download URL: stapel_mailtrap-0.1.2.tar.gz
- Upload date:
- Size: 21.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d99bf80e3dca1847a4320271091c2a29090fbaf1d09d456636a80233f1188a2b
|
|
| MD5 |
342f75fb519bb9713c6a9a335c298198
|
|
| BLAKE2b-256 |
ec562977018290df2f1ce33b525b0579c92dd79cd08e5c664f06b28b8c0b2fc6
|
Provenance
The following attestation bundles were made for stapel_mailtrap-0.1.2.tar.gz:
Publisher:
publish.yml on usestapel/stapel-mailtrap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_mailtrap-0.1.2.tar.gz -
Subject digest:
d99bf80e3dca1847a4320271091c2a29090fbaf1d09d456636a80233f1188a2b - Sigstore transparency entry: 2146082582
- Sigstore integration time:
-
Permalink:
usestapel/stapel-mailtrap@8c2bd291805a6bd42a4edda1cb3ccf593e8fb856 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c2bd291805a6bd42a4edda1cb3ccf593e8fb856 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stapel_mailtrap-0.1.2-py3-none-any.whl.
File metadata
- Download URL: stapel_mailtrap-0.1.2-py3-none-any.whl
- Upload date:
- Size: 22.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
abba88820550ac90ceafd4d896f996a8dcc01c9cd131aaeef1c8a18db77df783
|
|
| MD5 |
4dfb8fe5fe8142a9209d9d99d431a163
|
|
| BLAKE2b-256 |
74ad01c4b07a1c3f14fbbfe2ad99f3b297c837cc6be0595755201efd46ae18ac
|
Provenance
The following attestation bundles were made for stapel_mailtrap-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on usestapel/stapel-mailtrap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_mailtrap-0.1.2-py3-none-any.whl -
Subject digest:
abba88820550ac90ceafd4d896f996a8dcc01c9cd131aaeef1c8a18db77df783 - Sigstore transparency entry: 2146082697
- Sigstore integration time:
-
Permalink:
usestapel/stapel-mailtrap@8c2bd291805a6bd42a4edda1cb3ccf593e8fb856 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c2bd291805a6bd42a4edda1cb3ccf593e8fb856 -
Trigger Event:
push
-
Statement type: