Kumo Shaping
Kumo Shaping is PostMTA's epoch-based sending-rate control system. It replaces iptables-based throttling with a Kumo-native engine that models ISP delivery windows, reputation tiers, and warmup curves as first-class primitives. Every subuser operates inside a shaping epoch that governs how many messages per day may be injected into the transport layer.
Kumo-Native vs iptables
Legacy MTA shaping used iptables rules to meter connection counts and message injection rates at the kernel level. This approach is opaque, stateless, and ISP-agnostic. Kumo Shaping runs entirely in user space and exposes a programmable API.
| Dimension | iptables Throttling | Kumo Shaping |
|---|---|---|
| Statefulness | Stateless counters | Epoch-aware state machine per subuser |
| ISP modelling | None | Per-ISP delivery windows and hourly buckets |
| Reputation coupling | Indirect, manual | Automatic: reputation score gates epoch progression |
| API surface | None (CLI only) | REST: apply policy, override, rollback |
| Warmup | Separate script | Built into epoch ladder |
| Audit trail | syslog only | Full event log with operator identity |
Epoch Model
Each subuser occupies exactly one active epoch at any given time. The epoch encodes three signals:
- sent_today — cumulative messages injected so far in the current UTC calendar day.
- epoch_number — the current tier in the warmup ladder (1 = coldest, 10 = fully warmed).
- next_epoch_at — UTC timestamp when the next epoch promotion (or demotion) is scheduled.
Epoch State Fields
| Field | Type | Description |
|---|---|---|
subuser_id | string | Subuser that owns this epoch |
sent_today | integer | Messages injected today (resets at 00:00 UTC) |
epoch_number | integer 1–10 | Current warmup tier |
next_epoch_at | ISO 8601 | Scheduled epoch transition time |
rate_limit | integer | Messages-per-day cap for current epoch |
warmup_stage | string | Named stage (stage_1 … stage_10) |
reputation_score | float 0–1 | Latest computed reputation score |
policy | string | Active policy name (conservative / moderate / aggressive) |
Apply Shaping Policy via API
Operators can apply a named shaping policy to a subuser. Valid policy names are conservative, moderate, and aggressive. Each policy encodes a different warmup ladder trajectory.
POST /v1/shaping/apply
Content-Type: application/json
Authorization: Bearer pmta_live_...
{
"subuser_id": "sub_usr_01HX...",
"policy": "aggressive",
"override": false,
"reason": "campaign_launch"
}Response:
{
"epoch_id": "ep_01HX9P7N3M4Q6R8T2V4W6Y8",
"subuser_id": "sub_usr_01HX...",
"policy": "aggressive",
"sent_today": 0,
"epoch_number": 1,
"next_epoch_at": "2026-07-18T12:00:00Z",
"rate_limit": 5000,
"warmup_stage": "stage_1",
"applied_by": "operator@example.com",
"applied_at": "2026-07-18T00:00:00Z"
}Warmup Ladder
New subusers or subusers with degraded reputation start at epoch 1. Each epoch has a defined daily send cap and a minimum dwell time before promotion is allowed. Promotion requires a positive reputation delta and no active suppression flags. Demotion occurs automatically if hard bounce rate exceeds 3% or complaint rate exceeds 0.1% over a rolling 7-day window.
| Epoch | Stage Name | Daily Cap | Min Dwell (days) | Reputation Floor |
|---|---|---|---|---|
| 1 | cold_start | 100 | 1 | 0.00 |
| 2 | seed_1 | 500 | 2 | 0.10 |
| 3 | seed_2 | 2,000 | 3 | 0.25 |
| 4 | seed_3 | 5,000 | 4 | 0.40 |
| 5 | early_volume | 15,000 | 5 | 0.55 |
| 6 | growth_1 | 40,000 | 7 | 0.65 |
| 7 | growth_2 | 100,000 | 10 | 0.75 |
| 8 | mid_volume | 250,000 | 14 | 0.82 |
| 9 | high_volume | 500,000 | 21 | 0.90 |
| 10 | full_warm | unlimited | — | 0.95 |
Manual Epoch Override
Operators may manually promote or demote a subuser's epoch using the override endpoint. Overrides trigger a mandatory cooldown period of 30 minutes (promotions) or 120 minutes (demotions) before another override can be issued, preventing accidental rapid cycling.
POST /v1/shaping/override
Content-Type: application/json
Authorization: Bearer pmta_live_...
{
"subuser_id": "sub_usr_01HX...",
"target_epoch": 5,
"reason": "emergency_auction_reminder",
"duration_minutes": 60
}Response:
{
"override_id": "ovr_01HX...",
"subuser_id": "sub_usr_01HX...",
"previous_epoch": 3,
"target_epoch": 5,
"cooldown_until": "2026-07-18T04:30:00Z",
"reason": "emergency_auction_reminder",
"applied_at": "2026-07-18T03:30:00Z"
}cooldown_active and a retry_after timestamp. Always check cooldown_until before issuing consecutive overrides.Suppression Pipeline Order
When a message is submitted to Kumo Shaping, it passes through a deterministic filter stack. The order matters:
- Global blocklist — Hard blocks from internal blocklists, RFC 2606 reserves, and PMH-SEC-026 policy.
- Subuser suppression list — Recipient addresses explicitly suppressed by the subuser.
- Complaint cache — FBL-derived complaints with a 30-day TTL from last seen date.
- Bounce cache — Known hard/soft bounces with TTLs of 7 days (hard) and 1 day (soft).
- Epoch check — Is sent_today below current epoch cap? If yes, proceed; otherwise queue.
- Reputation gate — Is reputation_score ≥ epoch_number × 0.1? If yes, proceed.
- ISP-specific routing — Apply per-ISP delivery windows and concurrency limits.
- Inject to transport — Message is handed off to the MTA transport layer.
Messages that fail the epoch check or reputation gate are held in the shaping queue and retried at the next epoch window boundary (top of each UTC hour) without 计 time penalty against the subuser.
Reputation-Shape Coupling
Kumo Shaping enforces a hard coupling between the reputation score and the maximum accessible epoch. This prevents high-volume subusers from continuing to send at full capacity when their reputation is degrading.
- Reputation score ≥ 0.95 — Full access to all epochs; unlimited sends at epoch 10.
- Reputation score 0.80 – 0.94 — Capped at epoch 8 even if epoch_number in DB is higher.
- Reputation score 0.60 – 0.79 — Capped at epoch 6; promotion ladder paused.
- Reputation score 0.30 – 0.59 — Capped at epoch 3; subuser flagged for review.
- Reputation score < 0.30 — Capped at epoch 1; all sends held pending operator review (PMH-SEC-031).
Checking Epoch Status via SQL
SELECT
subuser_id,
sent_today,
epoch_number,
next_epoch_at,
warmup_stage,
reputation_score
FROM shaping_epochs
WHERE subuser_id = 'sub_usr_01HX...'
AND date(next_epoch_at) = CURRENT_DATE
ORDER BY epoch_number DESC
LIMIT 10;ISP-Specific Delivery Windows
Kumo Shaping enforces per-ISP delivery windows that reflect each ISP's accepted sending patterns. These windows are hard limits enforced at the transport layer, not just shaping limits.
| ISP | Peak Window | Off-Peak Cap | Concurrency Limit | Notes |
|---|---|---|---|---|
| Gmail | 06:00–22:00 local recipient TZ | 1,000/hr off-peak | 100 simultaneous connections | Enforces RFC 5321 size limits (50 MB) |
| Microsoft / Outlook | 07:00–21:00 local | 500/hr off-peak | 50 connections | Throttles at 3rd sequential day of high volume |
| Yahoo | 08:00–20:00 local | 300/hr off-peak | 30 connections | Requires DMARC p=reject for best routing |
| Apple iCloud | 07:00–22:00 local | 200/hr off-peak | 20 connections | Strict spam button threshold (0.08%) |
| Generic / Other | No restriction | No restriction | Unlimited | Follows domain MX record TTLs |
Queue Priority Tiers
Messages held by Kumo Shaping (waiting for epoch window or reputation gate) are ordered by queue priority. Priority is set at injection time and cannot be changed while in queue. Higher priority messages are processed first when the shaping gate opens.
| Priority | Name | Source | Max Queue Time |
|---|---|---|---|
| 1 (highest) | critical | System-generated (e.g., MFA tokens, password resets) | 15 minutes |
| 2 | transactional | API messages with priority: transactional | 1 hour |
| 3 | standard | Default for all normal sends | 24 hours |
| 4 | marketing | Campaign sends with priority: marketing | 72 hours |
| 5 (lowest) | batch | Bulk imports and list uploads | 7 days |
Messages that exceed their maximum queue time are moved to the DLQ with reason max_queue_time_exceeded. Transactional and critical messages are never downqueued to a lower priority tier.
Monitoring Shaping State
Real-time Metrics API
GET /v1/shaping/metrics?subuser_id=sub_usr_01HX...
Authorization: Bearer pmta_live_...
{
"subuser_id": "sub_usr_01HX...",
"current_epoch": 5,
"sent_today": 8432,
"epoch_cap": 15000,
"pct_used": 56.2,
"reputation_score": 0.72,
"epoch_promotion_due": "2026-07-19T00:00:00Z",
"active_queue_messages": 312,
"queue_breakdown": {
"critical": 0,
"transactional": 12,
"standard": 245,
"marketing": 55,
"batch": 0
},
"suppression_snapshot": {
"addresses_suppressed": 1842,
"addresses_suppressed_today": 14
}
}Promotion and Demotion Alerts
Configure alerting on the following shaping events to monitor subuser health:
- Epoch demotion: Triggered when reputation threshold crossed. Alert threshold: immediate PagerDuty for demotions beyond epoch 3.
- Complaint rate warning:
complaint_rate_pct > 0.08%in rolling 7-day window. - Bounce rate warning: Hard bounce rate > 1.5% over 3-day rolling window.
- Queue depth warning: More than 1,000 messages queued for a single subuser for more than 2 hours.
- Epoch cap reached: sent_today reaches 95% of epoch_cap before 18:00 UTC.
Policy Reference
| Policy | Warmup Duration | Starting Epoch | Auto-promotion |
|---|---|---|---|
conservative | 45 days | 1 | Enabled, slower dwell |
moderate | 21 days | 2 | Enabled, standard dwell |
aggressive | 7 days | 3 | Enabled, fast dwell |
manual | None | Current epoch | Disabled — operator-driven only |