Skip to main content

Email trap (mail catcher) for the Stapel framework — capture outbound email in dev/staging

Project description

stapel-mailtrap

CI codecov PyPI

An email trap (mail catcher) for dev/staging: outbound mail is captured into a TrappedEmail journal 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

stapel_mailtrap-0.1.4.tar.gz (21.7 kB view details)

Uploaded Source

Built Distribution

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

stapel_mailtrap-0.1.4-py3-none-any.whl (23.2 kB view details)

Uploaded Python 3

File details

Details for the file stapel_mailtrap-0.1.4.tar.gz.

File metadata

  • Download URL: stapel_mailtrap-0.1.4.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stapel_mailtrap-0.1.4.tar.gz
Algorithm Hash digest
SHA256 713f64f72f83fbd195b1e414b4740305ce4beb3b51046e2bdc1b5b1ebe40f564
MD5 bffedb12a15acd3c7062e311abd62542
BLAKE2b-256 766d9a79e8c9aa4b5a6c5fbff18fd28f01dd8465b42052d7ffee102049ce609c

See more details on using hashes here.

Provenance

The following attestation bundles were made for stapel_mailtrap-0.1.4.tar.gz:

Publisher: publish.yml on usestapel/stapel-mailtrap

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

File details

Details for the file stapel_mailtrap-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: stapel_mailtrap-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 23.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stapel_mailtrap-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 1f360559e53e1ad83b56970948d235e9f2f69bea3be752017c331c92ea562d5e
MD5 f82ab7020640f5701067ad2a2368a658
BLAKE2b-256 a06789e9a293678b9a15fd999cf50775eab40e9f863e945569f0a4513e3fb6a7

See more details on using hashes here.

Provenance

The following attestation bundles were made for stapel_mailtrap-0.1.4-py3-none-any.whl:

Publisher: publish.yml on usestapel/stapel-mailtrap

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page