npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@composurecdk/ses

v0.11.2

Published

Compose-native builders for AWS SES — email identities, receipt rule sets, filters, and rule actions

Readme

@composurecdk/ses

Compose-native builders for AWS SES, part of ComposureCDK.

SES is Amazon's email service for sending and receiving mail. This package currently covers the receiving path — email identities, receipt rule sets, receipt filters, and rule actions. Sending-side builders (configuration sets, dedicated IP pools, VDM, reputation alarms) will follow.

Install

npm install @composurecdk/ses

Peer dependencies: @composurecdk/core, @composurecdk/logs (for the activation provider's log group), @composurecdk/route53 (for .publishDkim() / .publishInboundMx()), aws-cdk-lib, constructs.

Email identity

An SES email identity is a domain or email address you've verified ownership of, so SES will send or receive mail for it. This builder verifies the identity and sets up DKIM signing.

import { createEmailIdentityBuilder } from "@composurecdk/ses";
import { ref } from "@composurecdk/core";

const zone = ref<HostedZoneBuilderResult>("zone").get("hostedZone");

const { emailIdentity, dkim } = createEmailIdentityBuilder()
  .domain("ask.example.com") // .email(...) / .publicHostedZone(...) also
  .easyDkim() // default; .byoDkim({ selector, privateKey, publicKey }) for BYODKIM
  .publishDkim(zone)
  .publishInboundMx(zone) // route the domain's mail to SES
  .build(stack, "MailIdentity");
  • .domain() / .email() / .publicHostedZone() — the three identity variants. .publicHostedZone(zone) verifies the zone apex and lets CDK auto-publish DKIM into it — the concise "I own the whole zone" form.
  • .publishDkim(zone) — for the .domain() case (e.g. a subdomain whose apex lives elsewhere), publishes the DKIM DNS records into zone: three CNAMEs for Easy DKIM, one TXT for BYODKIM. Mutually exclusive with .publicHostedZone() (which already publishes); not valid for an email identity.
  • .publishInboundMx(zone, { priority? }) — publishes the MX record that routes the domain's mail to SES's inbound endpoint (inbound-smtp.<region>.amazonaws.com, derived from the stack's Region). Without it senders have nowhere to deliver, however the rule set is configured. Valid for .domain() and .publicHostedZone() — CDK's zone form publishes DKIM but never a receiving MX. Priority defaults to 10 (DEFAULT_INBOUND_MX_PRIORITY). Emits the @composurecdk/ses:receiving-region warning when the stack's Region can't receive mail.
  • .mailFromDomain(...) — a custom MAIL FROM domain; defaults to REJECT_MESSAGE on MX failure (no insecure fallback to amazonses.com, preserving SPF/DMARC alignment).
  • The result exposes dkim — the identity's DKIM DNS records as { name, value }[] (CDK's dkimRecords), for manual publication when a zone isn't available — and dkimRecords / inboundMxRecords (the Route 53 records) when .publishDkim() / .publishInboundMx() were used.

Receipt rule set

A receipt rule set is an ordered list of rules that tell SES how to handle inbound mail. Each rule matches recipients and runs an ordered list of actions (store to S3, invoke a Lambda, bounce, …).

import { createReceiptRuleSetBuilder, s3Action, lambdaAction } from "@composurecdk/ses";
import { ref } from "@composurecdk/core";

const { ruleSet, rules } = createReceiptRuleSetBuilder()
  .rule("inbound", (r) =>
    r
      .recipients(["[email protected]"])
      .addAction(
        "store",
        s3Action(ref<BucketBuilderResult>("mailBucket").get("bucket"), {
          objectKeyPrefix: "inbound/",
        }),
      )
      .addAction("process", lambdaAction(ref<FunctionBuilderResult>("intake").get("function"))),
  )
  .build(stack, "MailRuleSet");
  • Rules run in declaration order, and actions run in the order added — ordering is semantic (a Stop/Bounce action halts later rules), so it is preserved.
  • .addAction(key, action) takes any CDK IReceiptRuleAction. The s3Action / lambdaAction / snsAction / bounceAction / stopAction / addHeaderAction free functions are optional convenience helpers — they wrap the CDK aws-ses-actions classes and accept Resolvables so a rule can ref() sibling components. For an action type we don't yet provide a helper for, construct the CDK action and pass it to .addAction() directly.
  • s3Action injects the SES bucket policy transparently and, when given a kmsKey, grants ses.amazonaws.com the encrypt permissions the key needs — encryption at rest works out of the box.

Secure defaults

Every rule gets AWS-recommended defaults, each individually overridable:

| Default | Value | Why | | ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | scanEnabled | true | Spam & virus scanning (CloudFormation defaults it off). | | tlsPolicy | REQUIRE | Encrypt in transit. ~95%+ of legitimate inbound mail already uses TLS, and a non-TLS sender receives a hard bounce (fail-loud, not a silent drop). Override with .tlsPolicy(TlsPolicy.OPTIONAL) for a domain that must accept mail from legacy senders without STARTTLS. |

Migrating from raw aws-cdk-lib constructs? tlsPolicy defaults to REQUIRE here, whereas CloudFormation defaults to OPTIONAL — a deliberately more secure posture, but a behaviour change on migration. Senders without STARTTLS will be bounced; set .tlsPolicy(TlsPolicy.OPTIONAL) per rule to retain the raw-construct behaviour.

Activation (on by default)

A receipt rule set is inert until it is the account's active rule set — a separate, account-level ses:SetActiveReceiptRuleSet call that has no CloudFormation resource (SES docs). Miss it and mail silently vanishes, so .activate() is on by default (opt out with .activate(false)).

Activation is backed by a purpose-built provider (see ADR-0016) that conditionally deactivates: on delete it clears the active slot only if the currently-active set is this one, so tearing down a stack never disables another stack's rule set. The custom resource is exposed on the result as activation. It depends on every rule, so the set never goes live empty mid-deploy, and on teardown it is deactivated before its rules are removed.

Both provider Lambdas (the handler and CDK's provider framework) write to one declared log group, activationLogGroup on the result, carrying the @composurecdk/logs defaults (two-year retention, retained on delete). Without it Lambda would create /aws/lambda/<name> on first invocation with indefinite retention, outside the template. Change its removal policy with activationLogGroup.applyRemovalPolicy(...).

Only one rule set is active per account/region. If you run multiple rule sets across stacks and need bespoke arbitration, disable activation here and drive setActiveReceiptRuleSet yourself with @composurecdk/custom-resources.

Receipt filters

A receipt filter allows or blocks inbound mail from specific IP addresses or CIDR ranges, before any rule runs.

import { createReceiptFilterBuilder, createAllowListReceiptFilterBuilder } from "@composurecdk/ses";
import { ReceiptFilterPolicy } from "aws-cdk-lib/aws-ses";

createReceiptFilterBuilder()
  .ip("10.0.0.0/24")
  .policy(ReceiptFilterPolicy.BLOCK)
  .build(stack, "Blocklist");

createAllowListReceiptFilterBuilder().ips(["203.0.113.0/24"]).build(stack, "Allowlist");
  • createReceiptFilterBuilder — a single allow/block rule for one IP or CIDR range.
  • createAllowListReceiptFilterBuilder — blocks all senders except the supplied IPs (a block-all filter plus one allow filter per address).

Region support

SES email receiving is available only in a subset of Regions. The rule-set and filter builders, and .publishInboundMx(), emit a synth-time warning (@composurecdk/ses:receiving-region) when built in a Region that can't receive mail. Identity verification and DKIM work in far more Regions, so the warning is scoped to the receiving constructs. See the email receiving endpoints table.