During the email-confirmation stage the frontend shows "email sent", but the email never arrives. A manual/test message from SpikerSoft does go through (so the SMTP server itself is up).
Why the frontend says "sent" but nothing arrives
After #225/#226/#227, all email sending was moved out of the API into the notifications worker and is dispatched fire-and-forget over RabbitMQ:
POST /api/authentication/initiate-pre-registration → InitiatePreRegistrationCommandHandler.SendEmailVerification(...) publishes a SendEmailCommand (IRemoteCommand, exchange notifications, routing key notifications.email.send.requested).
So "sent" only means "published to the queue", not "delivered".
Root cause #1 (primary): the notifications email worker is not built or deployed
SpikerSoft.EventHandlers.Notifications is the only consumer of SendEmailCommand (NotificationsRpcConsumerHostedService → SendEmailCommandHandler → EmailService MailKit SMTP send). But it is missing from every deployment layer:
No Dockerfile — every other event handler has one (SpikerSoft.EventHandlers.*/Dockerfile); SpikerSoft.EventHandlers.Notifications does not.
No CI workflow — there is no .gitea/workflows/spikersoft-notifications.yml (every other worker has one).
No stack service — deploy/docker-stack.yml has no notifications service (it deploys the API + ~16 workers, but not this one).
Result: SendEmailCommand messages are published to RabbitMQ with no consumer running, so registration verification emails are never sent (they sit on the queue / eventually dead-letter). This is a regression from the #225/#226/#227 refactor — email-sending was moved into a worker that was never containerized or deployed.
The API's SMTP health check is TCP-only (SpikerSoft.Api/Infrastructure/HealthChecks/SMTP_HealthCheck.cs), so it (and a manual/test send) can pass while the actual app pipeline is dead.
Root cause #2 (latent, will bite once deployed): Email/Smtp config-key mismatch
EmailService reads Email:Host / Email:Port / Email:Username / Email:Password / Email:SkipSslValidation / Email:TimeoutSeconds (and throws ArgumentException if Host/Username/Password are missing):
The canonical key elsewhere is Email (SpikerSoft.Api/appsettings.json → "Email": { ... }). So even once the worker is deployed, unless Email__* env vars are supplied it will throw on construction and send nothing.
Suggested fix
Deploy the worker: add SpikerSoft.EventHandlers.Notifications/Dockerfile (mirror CalendarReminders, EXPOSE 8085 per WithKestrel(8085)), a .gitea/workflows/spikersoft-notifications.yml, and a notifications service in deploy/docker-stack.yml (networks: seq-attachable, jaeger, rabbitmq, mongo, mailserver) plus the server-side per-service stack used by the deploy step.
Fix config: rename the host's Smtp block to Email (Host/Port/Username/Password/...) so it matches EmailService, and wire Email__Host/Username/Password as production secrets/env for the worker.
(Optional hardening) Have the frontend/initiate response distinguish "queued" from "delivered", and add a delivery alert/dead-letter alarm so a missing consumer is caught.
Security note (separate)
SpikerSoft.Api/appsettings.json (~L383-389) contains a plaintext SMTP password committed to the repo. It should be rotated and moved to a secret. (Worth its own ticket.)
## Severity
High — no new user can complete signup: the frontend reports the verification email was sent, but it never arrives.
## Area
Registration email verification ("Verificar" stage) — backend email delivery pipeline.
## Symptom
During the email-confirmation stage the frontend shows "email sent", but the email never arrives. A manual/test message from SpikerSoft does go through (so the SMTP server itself is up).
## Why the frontend says "sent" but nothing arrives
After #225/#226/#227, all email sending was moved out of the API into the notifications worker and is dispatched **fire-and-forget over RabbitMQ**:
1. `POST /api/authentication/initiate-pre-registration` → `InitiatePreRegistrationCommandHandler.SendEmailVerification(...)` publishes a `SendEmailCommand` (`IRemoteCommand`, exchange `notifications`, routing key `notifications.email.send.requested`).
- `SpikerSoft.Business/Domain/Authentication/Commands/InitiatePreRegistration/InitiatePreRegistrationCommandHandler.cs` (~L354-368)
2. `RemoteDispatchBehavior` publishes to RabbitMQ and immediately returns `JobAccepted` — it does **not** wait for SMTP delivery.
- `SpikerSoft.Business.Edge/Behaviors/RemoteDispatchBehavior.cs` (~L51-67)
3. The API returns success; the frontend optimistically shows the success banner (it never inspects an `emailSent` flag).
- `registration-stepper.component.ts` `handleRegistrationSuccess(...)` (~L743-759)
So "sent" only means "published to the queue", not "delivered".
## Root cause #1 (primary): the notifications email worker is not built or deployed
`SpikerSoft.EventHandlers.Notifications` is the **only** consumer of `SendEmailCommand` (`NotificationsRpcConsumerHostedService` → `SendEmailCommandHandler` → `EmailService` MailKit SMTP send). But it is missing from every deployment layer:
- **No Dockerfile** — every other event handler has one (`SpikerSoft.EventHandlers.*/Dockerfile`); `SpikerSoft.EventHandlers.Notifications` does not.
- **No CI workflow** — there is no `.gitea/workflows/spikersoft-notifications.yml` (every other worker has one).
- **No stack service** — `deploy/docker-stack.yml` has no `notifications` service (it deploys the API + ~16 workers, but not this one).
Result: `SendEmailCommand` messages are published to RabbitMQ with **no consumer running**, so registration verification emails are never sent (they sit on the queue / eventually dead-letter). This is a regression from the #225/#226/#227 refactor — email-sending was moved into a worker that was never containerized or deployed.
The API's SMTP health check is TCP-only (`SpikerSoft.Api/Infrastructure/HealthChecks/SMTP_HealthCheck.cs`), so it (and a manual/test send) can pass while the actual app pipeline is dead.
## Root cause #2 (latent, will bite once deployed): Email/Smtp config-key mismatch
`EmailService` reads `Email:Host` / `Email:Port` / `Email:Username` / `Email:Password` / `Email:SkipSslValidation` / `Email:TimeoutSeconds` (and throws `ArgumentException` if Host/Username/Password are missing):
- `SpikerSoft.Workers.Notifications/Services/EmailService.cs` (~L16-21)
But the notifications host config defines an `Smtp` block instead of `Email`:
- `SpikerSoft.EventHandlers.Notifications/appsettings.json` (~L41-49: `"Smtp": { ... }`)
The canonical key elsewhere is `Email` (`SpikerSoft.Api/appsettings.json` → `"Email": { ... }`). So even once the worker is deployed, unless `Email__*` env vars are supplied it will throw on construction and send nothing.
## Suggested fix
1. Deploy the worker: add `SpikerSoft.EventHandlers.Notifications/Dockerfile` (mirror `CalendarReminders`, EXPOSE 8085 per `WithKestrel(8085)`), a `.gitea/workflows/spikersoft-notifications.yml`, and a `notifications` service in `deploy/docker-stack.yml` (networks: `seq-attachable`, `jaeger`, `rabbitmq`, `mongo`, `mailserver`) plus the server-side per-service stack used by the deploy step.
2. Fix config: rename the host's `Smtp` block to `Email` (Host/Port/Username/Password/...) so it matches `EmailService`, and wire `Email__Host/Username/Password` as production secrets/env for the worker.
3. (Optional hardening) Have the frontend/initiate response distinguish "queued" from "delivered", and add a delivery alert/dead-letter alarm so a missing consumer is caught.
## Security note (separate)
`SpikerSoft.Api/appsettings.json` (~L383-389) contains a **plaintext SMTP password** committed to the repo. It should be rotated and moved to a secret. (Worth its own ticket.)
## Pointers
- `SpikerSoft.EventHandlers.Notifications/` (Program.cs, appsettings.json, **missing** Dockerfile)
- `SpikerSoft.Workers.Notifications/Services/EmailService.cs`
- `SpikerSoft.EventHandlers.Notifications/Services/NotificationsRpcConsumerHostedService.cs`
- `SpikerSoft.Business/Domain/Authentication/Commands/InitiatePreRegistration/InitiatePreRegistrationCommandHandler.cs`
- `deploy/docker-stack.yml`, `.gitea/workflows/`
spikerj
added the bug label 2026-06-25 16:48:45 +00:00
Resolved in spikersoft-backend PR #18 (merged to master on 2026-06-25, merge commit 4903eb9).
Root cause: after #225/#226/#227 moved all email sending into the SpikerSoft.EventHandlers.Notifications worker (published fire-and-forget over RabbitMQ), that worker was never containerized or deployed, so SendEmailCommand messages had no consumer and registration verification emails were never delivered (while the API/frontend optimistically reported "sent").
Fix:
Added the worker's Dockerfile, a .gitea CI workflow, and a notifications service in deploy/docker-stack.yml.
Fixed the Smtp → Email config-key mismatch (and aligned the Twilio block) so EmailService binds its SMTP settings.
Standardized the Twilio Verify SID config key on Twilio:VerifyServiceSid (the registration SMS path had been reading an undefined Twilio:VerificationServiceSid).
The worker has since been deployed and is consuming notifications.email.send (and the SMS/verify/voice/confirmation queues). Closing.
Resolved in spikersoft-backend PR #18 (merged to `master` on 2026-06-25, merge commit `4903eb9`).
Root cause: after #225/#226/#227 moved all email sending into the `SpikerSoft.EventHandlers.Notifications` worker (published fire-and-forget over RabbitMQ), that worker was never containerized or deployed, so `SendEmailCommand` messages had no consumer and registration verification emails were never delivered (while the API/frontend optimistically reported "sent").
Fix:
- Added the worker's `Dockerfile`, a `.gitea` CI workflow, and a `notifications` service in `deploy/docker-stack.yml`.
- Fixed the `Smtp` → `Email` config-key mismatch (and aligned the Twilio block) so `EmailService` binds its SMTP settings.
- Standardized the Twilio Verify SID config key on `Twilio:VerifyServiceSid` (the registration SMS path had been reading an undefined `Twilio:VerificationServiceSid`).
The worker has since been deployed and is consuming `notifications.email.send` (and the SMS/verify/voice/confirmation queues). Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity
High — no new user can complete signup: the frontend reports the verification email was sent, but it never arrives.
Area
Registration email verification ("Verificar" stage) — backend email delivery pipeline.
Symptom
During the email-confirmation stage the frontend shows "email sent", but the email never arrives. A manual/test message from SpikerSoft does go through (so the SMTP server itself is up).
Why the frontend says "sent" but nothing arrives
After #225/#226/#227, all email sending was moved out of the API into the notifications worker and is dispatched fire-and-forget over RabbitMQ:
POST /api/authentication/initiate-pre-registration→InitiatePreRegistrationCommandHandler.SendEmailVerification(...)publishes aSendEmailCommand(IRemoteCommand, exchangenotifications, routing keynotifications.email.send.requested).SpikerSoft.Business/Domain/Authentication/Commands/InitiatePreRegistration/InitiatePreRegistrationCommandHandler.cs(~L354-368)RemoteDispatchBehaviorpublishes to RabbitMQ and immediately returnsJobAccepted— it does not wait for SMTP delivery.SpikerSoft.Business.Edge/Behaviors/RemoteDispatchBehavior.cs(~L51-67)emailSentflag).registration-stepper.component.tshandleRegistrationSuccess(...)(~L743-759)So "sent" only means "published to the queue", not "delivered".
Root cause #1 (primary): the notifications email worker is not built or deployed
SpikerSoft.EventHandlers.Notificationsis the only consumer ofSendEmailCommand(NotificationsRpcConsumerHostedService→SendEmailCommandHandler→EmailServiceMailKit SMTP send). But it is missing from every deployment layer:SpikerSoft.EventHandlers.*/Dockerfile);SpikerSoft.EventHandlers.Notificationsdoes not..gitea/workflows/spikersoft-notifications.yml(every other worker has one).deploy/docker-stack.ymlhas nonotificationsservice (it deploys the API + ~16 workers, but not this one).Result:
SendEmailCommandmessages are published to RabbitMQ with no consumer running, so registration verification emails are never sent (they sit on the queue / eventually dead-letter). This is a regression from the #225/#226/#227 refactor — email-sending was moved into a worker that was never containerized or deployed.The API's SMTP health check is TCP-only (
SpikerSoft.Api/Infrastructure/HealthChecks/SMTP_HealthCheck.cs), so it (and a manual/test send) can pass while the actual app pipeline is dead.Root cause #2 (latent, will bite once deployed): Email/Smtp config-key mismatch
EmailServicereadsEmail:Host/Email:Port/Email:Username/Email:Password/Email:SkipSslValidation/Email:TimeoutSeconds(and throwsArgumentExceptionif Host/Username/Password are missing):SpikerSoft.Workers.Notifications/Services/EmailService.cs(~L16-21)But the notifications host config defines an
Smtpblock instead ofEmail:SpikerSoft.EventHandlers.Notifications/appsettings.json(~L41-49:"Smtp": { ... })The canonical key elsewhere is
Email(SpikerSoft.Api/appsettings.json→"Email": { ... }). So even once the worker is deployed, unlessEmail__*env vars are supplied it will throw on construction and send nothing.Suggested fix
SpikerSoft.EventHandlers.Notifications/Dockerfile(mirrorCalendarReminders, EXPOSE 8085 perWithKestrel(8085)), a.gitea/workflows/spikersoft-notifications.yml, and anotificationsservice indeploy/docker-stack.yml(networks:seq-attachable,jaeger,rabbitmq,mongo,mailserver) plus the server-side per-service stack used by the deploy step.Smtpblock toEmail(Host/Port/Username/Password/...) so it matchesEmailService, and wireEmail__Host/Username/Passwordas production secrets/env for the worker.Security note (separate)
SpikerSoft.Api/appsettings.json(~L383-389) contains a plaintext SMTP password committed to the repo. It should be rotated and moved to a secret. (Worth its own ticket.)Pointers
SpikerSoft.EventHandlers.Notifications/(Program.cs, appsettings.json, missing Dockerfile)SpikerSoft.Workers.Notifications/Services/EmailService.csSpikerSoft.EventHandlers.Notifications/Services/NotificationsRpcConsumerHostedService.csSpikerSoft.Business/Domain/Authentication/Commands/InitiatePreRegistration/InitiatePreRegistrationCommandHandler.csdeploy/docker-stack.yml,.gitea/workflows/Resolved in spikersoft-backend PR #18 (merged to
masteron 2026-06-25, merge commit4903eb9).Root cause: after #225/#226/#227 moved all email sending into the
SpikerSoft.EventHandlers.Notificationsworker (published fire-and-forget over RabbitMQ), that worker was never containerized or deployed, soSendEmailCommandmessages had no consumer and registration verification emails were never delivered (while the API/frontend optimistically reported "sent").Fix:
Dockerfile, a.giteaCI workflow, and anotificationsservice indeploy/docker-stack.yml.Smtp→Emailconfig-key mismatch (and aligned the Twilio block) soEmailServicebinds its SMTP settings.Twilio:VerifyServiceSid(the registration SMS path had been reading an undefinedTwilio:VerificationServiceSid).The worker has since been deployed and is consuming
notifications.email.send(and the SMS/verify/voice/confirmation queues). Closing.