Files
galaxy-game/mail/docs/flows.md
T
2026-04-17 18:39:16 +02:00

3.3 KiB

Main Flows

Auth / Session -> Mail

sequenceDiagram
    participant Auth as Auth / Session Service
    participant Mail as Mail Service
    participant Redis
    participant Scheduler
    participant SMTP as Provider

    Auth->>Mail: POST /api/v1/internal/login-code-deliveries + Idempotency-Key
    Mail->>Mail: validate request and idempotency scope
    alt MAIL_SMTP_MODE = stub
        Mail->>Redis: persist delivery as suppressed
        Mail-->>Auth: 200 {outcome=suppressed}
    else MAIL_SMTP_MODE = smtp
        Mail->>Redis: persist delivery as queued + attempt #1 scheduled
        Mail-->>Auth: 200 {outcome=sent}
        Scheduler->>Redis: claim due attempt
        Scheduler->>SMTP: send rendered auth mail
        SMTP-->>Scheduler: accepted or classified failure
        Scheduler->>Redis: commit sent / retry / failed / dead_letter
    end

sent on this boundary means durable intake into the mail-delivery pipeline. It does not mean SMTP completion.

Notification -> Mail

sequenceDiagram
    participant Notify as Notification Service
    participant Stream as Redis Stream mail:delivery_commands
    participant Consumer as Command consumer
    participant Mail as Mail Service
    participant Redis

    Notify->>Stream: XADD generic command
    Consumer->>Stream: XREAD from last stored offset
    Consumer->>Mail: decode and validate command
    alt malformed or conflicting command
        Mail->>Redis: record malformed command entry
        Consumer->>Redis: save stream offset
    else valid command
        Mail->>Redis: persist delivery + first attempt + optional payload bundle
        Consumer->>Redis: save stream offset
    end

Retry and Dead Letter

sequenceDiagram
    participant Scheduler
    participant Redis
    participant Worker as Attempt worker
    participant SMTP as Provider

    Scheduler->>Redis: find next due delivery
    Scheduler->>Redis: load work item
    alt template delivery not yet rendered
        Scheduler->>Redis: render and store materialized content
    end
    Scheduler->>Redis: claim scheduled attempt
    Scheduler->>Worker: enqueue claimed work
    Worker->>SMTP: send materialized message
    SMTP-->>Worker: accepted / suppressed / transient_failure / permanent_failure
    alt accepted
        Worker->>Redis: commit sent + provider_accepted
    else suppressed
        Worker->>Redis: commit suppressed + provider_rejected
    else transient failure before retry budget ends
        Worker->>Redis: commit transport_failed|timed_out + next scheduled attempt
    else retry budget exhausted
        Worker->>Redis: commit dead_letter + dead-letter entry
    else permanent failure
        Worker->>Redis: commit failed + provider_rejected
    end

Operator Resend

sequenceDiagram
    participant Ops as Trusted operator
    participant Mail as Mail Service
    participant Redis

    Ops->>Mail: POST /api/v1/internal/deliveries/{delivery_id}/resend
    Mail->>Redis: load original delivery and optional payload bundle
    Mail->>Mail: verify original status is terminal
    Mail->>Redis: create clone delivery with source=operator_resend
    Mail-->>Ops: 200 {delivery_id=<clone>}

Resend always creates a new delivery and never mutates the original delivery or its attempt history.