Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Mailer Smtp

ASecurity

How to use @owlmeans/mailer-smtp — SMTP transport (nodemailer) for the MailerService contract. Use when configuring real outbound email, an SMTP relay such as Mailgun, or the email-OTP mailer. Applies to files matching **/context.ts, **/config.ts, **/setup.ts.

3 stars
0 votes
0 copies
0 views
Added 9/22/2026
securitynodeapidocumentation

Works with

api

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add owlmeans/common --skill mailer-smtp --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mailer Smtp?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Mailer Smtp
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/owlmeans-mailer-smtp/badge)](https://www.skillsdirectory.com/skills/owlmeans-mailer-smtp)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: mailer-smtp
description: "How to use @owlmeans/mailer-smtp — SMTP transport (nodemailer) for the MailerService contract. Use when configuring real outbound email, an SMTP relay such as Mailgun, or the email-OTP mailer. Applies to files matching **/context.ts, **/config.ts, **/setup.ts."
metadata:
  applyTo: "**/context.ts, **/config.ts, **/setup.ts"
---

# Using `@owlmeans/mailer-smtp`

**Install:** `"@owlmeans/mailer-smtp": "^0.1.18-rc.29"` in `dependencies` — it depends on
`nodemailer` itself, so a consumer declares nothing extra

SMTP transport implementing `@owlmeans/mailer`'s `MailerService`, built on `nodemailer` (the
zero-dependency de-facto standard for Node SMTP). Reads `ctx.cfg.smtp`. Works against any relay —
Mailgun, SES, Postmark all expose SMTP.

## Public API surface

| Symbol | Kind | Purpose |
|--------|------|---------|
| `makeSmtpMailerService(alias?)` | fn | Service factory; default alias `SMTP_MAILER` |
| `SmtpMailerService` | interface | `MailerService` + `verify()` + `close()` |
| `SmtpSettings` / `SmtpConfig` | interface | `cfg.smtp` block |
| `SMTP_MAILER` | const | `'smtp-mailer'` |
| `SMTP_DEFAULT_PORT` | const | `465` |
| `toTransportOptions` / `toMailOptions` | fn | Pure translations into nodemailer's shapes; test these, not the socket |

## Config shape

```ts
cfg.smtp = {
  host: 'smtp.eu.mailgun.org',
  port: 465,          // default
  secure: true,       // default — implicit TLS
  user: 'no-reply@example.com',
  pass: process.env.SMTP_PASSWORD,
  from: 'Example <no-reply@example.com>',
  // optional: replyTo, headers, rejectUnauthorized, timeout
}
```

Every numeric/boolean field also accepts its **string** form. That is not sloppiness: in a server
context the values usually come from mounted files, and `fileConfigReader` yields text.

```ts
cfg.smtp = {
  host: '/etc/app-config/smtp-host',
  user: '/etc/app-config/smtp-user',
  from: '/etc/app-config/smtp-from',
  pass: '/etc/master-secret/smtp-secret',   // secret volume, never a ConfigMap
}
```

## Registration

```ts
import { MAILER_SERVICE } from '@owlmeans/mailer'
import { makeSmtpMailerService } from '@owlmeans/mailer-smtp'

context.registerService(makeSmtpMailerService(MAILER_SERVICE))
```

Register under `MAILER_SERVICE` so platform code — `OtpService` above all — resolves the mailer
without knowing the transport. A local-only console transport is an explicit mode; it is never a
production fallback:

```ts
if (cfg.iamMailer.mode !== 'smtp') throw new SyntaxError('production requires smtp mode')
context.registerService(makeSmtpMailerService(MAILER_SERVICE, {
  authenticated: true,
  verifyOnInit: true,
}))
```

Authenticated SMTP requires a host, from address, user and password; `verifyOnInit` makes an
unusable relay fail context startup rather than exposing a later login code through a console log.

## Ports and TLS

| Port | Mode | `secure` |
|---|---|---|
| 465 | implicit TLS from the first byte | `true` (default) |
| 587, 2525 | STARTTLS upgrade | `false` |
| 25 | plain / STARTTLS, often blocked | `false` |

## Rules

- **The relay's SMTP credential is not the account login.** Mailgun issues a per-sending-domain
  user (`postmaster@domain` or a custom one) under the domain's SMTP settings.
- The `From` domain must be verified with the relay; the local part is free-form.
- Deliberately **unpooled** — a pooled transport holds its socket and keeps a short-lived process
  alive. Add pooling only with a matching lifecycle.
- Relay errors are rethrown prefixed with the service alias and the server's own reply
  (`code`/`responseCode`/`response`). The password never reaches a message or a log. A missing
  `cfg.smtp.host` is the other failure and it is a `SyntaxError`, raised before any socket.
- `verify()` authenticates without submitting a message — right for health checks; not proof that
  the relay accepts *a message* from your sender.
- Production mail selection must be explicit and use `authenticated: true, verifyOnInit: true`.
  Console mail is for local fixtures/development and must never be selected because SMTP settings
  were absent or malformed.
- Never register this in unit tests — use `makeConsoleMailerService`. `toMailOptions` plus
  nodemailer's own `jsonTransport` cover envelope assertions without a socket.
- **Never authenticate with a deliberately wrong password against a live relay.** Repeated failed
  logins trip the provider's brute-force protection and lock the shared credential for every
  environment using it — Mailgun then answers `535 Authentication failed` to the correct password
  too, and the credential has to be reset in its dashboard. Provoke transport errors with an
  unreachable socket (`host: '127.0.0.1', port: '1'`) instead.
- The live spec (`tests/send.spec.ts`) is gated by `smtpGate()` from `@owlmeans/test-integration` on
  `SMTP_HOST` / `SMTP_USER` / `SMTP_PASSWORD` / `SMTP_FROM` / `SMTP_TEST_TO` (with `SMTP_PORT` and
  `SMTP_SECURE` optional), and **delivers real mail** when the gate is open. Empty variables = skip
  with a printed reason, never a failure.
- Rollup-bundling for a container image works with the default `preferBuiltins: true` node-resolve
  setup; nodemailer's dynamic requires do not need to be externalized.

## External references

- [nodemailer](https://www.npmjs.com/package/nodemailer) — v9.x, MIT-0, **zero dependencies**, the
  de-facto standard (~10.8k dependents). Ships no types; `@types/nodemailer` (v8.x) supplies them.
  CJS — import the default (`import nodemailer from 'nodemailer'`), not named members, so both Bun
  and Rollup's commonjs interop resolve it; it works under Bun and inside a Rollup CJS bundle.
- [Mailgun — Send via SMTP](https://documentation.mailgun.com/docs/mailgun/user-manual/sending-messages/send-smtp)
  — hosts `smtp.mailgun.org` / `smtp.eu.mailgun.org`; port 465 requires TLS, ports 25/587/2525 start
  plain and upgrade via STARTTLS. Credentials are **per sending domain**, managed under that domain's
  SMTP settings — not the account login. `X-Mailgun-Drop-Message: yes` submits in test mode (accepted,
  never delivered); other `X-Mailgun-*` headers cover tagging, DKIM, tracking and required TLS.

## Related

- [[mailer]] — the `MailerService` contract and the console transport
- [[server-mailer-mailgun]] — the same contract over Mailgun's HTTP API instead of SMTP

Attribution

owlmeansowlmeans
View sourceMore from owlmeans →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Springboot Security

Java Spring Boot 服务中关于身份验证/授权、验证、CSRF、密钥、标头、速率限制和依赖安全的 Spring Security 最佳实践。

2456590 votes

Security Review

Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.

2456590 votes

Paperclip Task Bridge

Create, comment on, update, and list Paperclip tasks from Hermes using scoped Paperclip API credentials.

805540 votes

Summarize Status

Write a short, colloquial summary for a Paperclip summary slot: open with the 1–3 specific, concrete actions the reader needs to take right now to unblock the work, then a brief plain-language status, streaming progress as it works.

805540 votes

Paperclip Evals

Choose, inspect, validate, and report Paperclip Runner or Product E2E evaluations while preserving evidence, provenance, cost, and failure classification.

805540 votes
View all in security →