Skip to content

feat(email): add MessageIdUtil for RFC 5322 threading + signed Reply-To#21

Open
mpge wants to merge 1 commit intomainfrom
feat/email-message-id
Open

feat(email): add MessageIdUtil for RFC 5322 threading + signed Reply-To#21
mpge wants to merge 1 commit intomainfrom
feat/email-message-id

Conversation

@mpge
Copy link
Copy Markdown
Member

@mpge mpge commented Apr 24, 2026

Summary

Ports the NestJS email/message-id.ts helpers to .NET. Mirrors the Spring dev.escalated.services.email.MessageIdUtil and the WordPress Message_Id_Util.

API

  • BuildMessageId(ticketId, replyId?, domain) — RFC 5322 Message-ID
  • ParseTicketIdFromMessageId(raw) — returns long?
  • BuildReplyTo(ticketId, secret, domain) — signed Reply-To
  • VerifyReplyTo(address, secret) — returns ticket id on match

Uses HMACSHA256 + CryptographicOperations.FixedTimeEquals for timing-safe verification.

Scope

Utility only. Follow-up PR will:

  1. Wire MessageIdUtil into EmailService so outbound notifications carry RFC-compliant Message-ID + signed Reply-To headers
  2. Add EscalatedOptions.Email config block for Domain + InboundSecret
  3. Add an inbound webhook endpoint that calls VerifyReplyTo

Test plan

  • 16 xUnit tests ([Theory] for malformed-input cases) covering round-trip, tamper rejection, case-insensitive hex, malformed input, local-part-only acceptance
  • CI green: test, lint

Ports the NestJS email/message-id.ts helpers to .NET. Mirrors the
Spring dev.escalated.services.email.MessageIdUtil and the WordPress
Message_Id_Util.

API:
  BuildMessageId(ticketId, replyId?, domain)
  ParseTicketIdFromMessageId(raw)
  BuildReplyTo(ticketId, secret, domain)
  VerifyReplyTo(address, secret)

Uses HMACSHA256 + CryptographicOperations.FixedTimeEquals for
timing-safe verification.

Pure static helpers, no DI — 16 xUnit tests ([Theory] for the
malformed-input cases) covering round-trip, tamper rejection,
case-insensitive hex, malformed input, local-part-only acceptance.

Follow-up PR will wire the util into EmailService so outbound
notifications carry the RFC-compliant Message-ID + signed Reply-To,
and add an EscalatedOptions.Email config block for domain + inbound
secret.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant