Use Sendico SMTP mailbox for local notifications (#57)
This commit is contained in:
1 parent
3e4e496037
commit
b36eb72c23
18 files changed
+528
-36
No files matched your search
+26
-17
@@ -30,7 +30,7 @@ NATS, notifications, BFF and Caddy serving the frontend.
|
||||
On first startup, bootstrap initializes/unseals persistent Vault, provisions
|
||||
AppRoles and secrets, initializes Mongo replica set `rs0`, configures Vault's Mongo
|
||||
plugin, and starts authenticated NATS JetStream. Product startup fails explicitly
|
||||
until Resend credentials exist. Infrastructure remains available for provisioning.
|
||||
until the SMTP mailbox password exists. Infrastructure remains available for provisioning.
|
||||
|
||||
After all services pass their startup health checks and the public Caddy CA is
|
||||
exported, `make up` prints a Sendico-style completion message with the frontend,
|
||||
@@ -56,7 +56,7 @@ kv/
|
||||
├── services/
|
||||
│ ├── bff/jwt # jwt
|
||||
│ └── notifications/
|
||||
│ └── email/resend # api_key only
|
||||
│ └── email/smtp # password only
|
||||
└── infrastructure/
|
||||
├── nats/auth # token; read by NATS and notifications
|
||||
└── mongo/bootstrap # local administrator and replica-set bootstrap material
|
||||
@@ -74,29 +74,35 @@ There are no frontend certificates, notification certificates or generic `tls`
|
||||
entries in KV. Browser certificates belong to Caddy; internal gRPC is plaintext
|
||||
on the trusted private Docker network without caller authentication.
|
||||
|
||||
## Resend
|
||||
## Local SMTP mail
|
||||
|
||||
Open [Vault](http://127.0.0.1:18200) and edit
|
||||
`kv/services/notifications/email/resend`:
|
||||
`kv/services/notifications/email/smtp`:
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| `api_key` | Your Resend API key |
|
||||
| `password` | Password for the configured SMTP mailbox |
|
||||
|
||||
The driver and sender settings belong in `infra/local/notifications/runtime.yml`:
|
||||
|
||||
```yaml
|
||||
email:
|
||||
driver: resend
|
||||
driver: smtp
|
||||
settings:
|
||||
api_key_env: EMAIL_API_KEY
|
||||
sender_address: "your-verified-sender@example.com"
|
||||
host: mail.sendico.io
|
||||
port: 465
|
||||
username: no-reply@sendico.io
|
||||
password_env: SMTP_PASSWORD
|
||||
network_timeout: 10
|
||||
sender_address: no-reply@sendico.io
|
||||
sender_name: "SXCHNGE"
|
||||
```
|
||||
|
||||
Set your verified address in that file and rebuild. It is intentionally empty by
|
||||
default, so notifications cannot start with an unconfigured sender. Vault Agent
|
||||
injects only the API key; it does not generate email configuration.
|
||||
The local stack uses Sendico's existing `no-reply@sendico.io` mailbox with
|
||||
authenticated implicit TLS on port 465 and certificate verification enabled.
|
||||
Vault Agent injects only the password; host, username and sender are non-secret
|
||||
runtime settings. Credentials are provisioned into SXCHNGE's own Vault; the
|
||||
notification service does not read Sendico configuration or secrets at runtime.
|
||||
|
||||
Local administrator login material is in private `.local/dev/admin/init.json`.
|
||||
Product containers cannot access it. Alternatively use the hidden terminal prompt:
|
||||
@@ -106,12 +112,15 @@ make email
|
||||
make build && make up
|
||||
```
|
||||
|
||||
No placeholder key or delivery fallback is installed. Startup validates presence;
|
||||
it does not send an email or validate domain ownership with Resend. Updating the
|
||||
API key causes Agent to restart notifications. Driver/sender changes require an
|
||||
image rebuild. Existing API keys survive bootstrap. Migration removes legacy
|
||||
driver/sender fields from the current Vault secret with a version-checked write;
|
||||
the preceding version is preserved in Vault history and the private archive.
|
||||
No placeholder password or delivery fallback is installed. Startup validates
|
||||
configuration and secret presence; the SMTP connection is opened on demand.
|
||||
Updating the password causes Agent to restart notifications. Driver/sender
|
||||
changes require an image rebuild. Old Resend secrets remain in Vault history
|
||||
but are not used or readable by the notifications AppRole. HTTP providers remain
|
||||
available for other deployments; switching one requires matching Agent/policy
|
||||
and runtime configuration. SMTP has no provider idempotency guarantee: explicit
|
||||
4xx rejections retry, 5xx rejections fail, and ambiguous transport outcomes require
|
||||
operator reconciliation before another send.
|
||||
|
||||
## Browser HTTPS and internal connections
|
||||
|
||||
|
||||
@@ -94,6 +94,39 @@ gRPC failure response. No security scan or real customer email retry was run.
|
||||
|
||||
## Behavior and boundaries
|
||||
|
||||
### Local mail client
|
||||
|
||||
The user subsequently selected Sendico's existing mailbox instead of Resend for
|
||||
local notifications. The authoritative Sendico configuration resolves to
|
||||
`no-reply@sendico.io`, `mail.sendico.io:465`, authenticated implicit TLS and a
|
||||
10-second network timeout. SXCHNGE's owned `smtp` adapter consumes rendered email
|
||||
models through the existing sender contract and queue; it does not import or
|
||||
access Sendico business modules, databases or messaging. The password was copied
|
||||
once, without printing it, from the running Sendico notification environment into
|
||||
`services/notifications/email/smtp` in SXCHNGE Vault. Only `password` is stored
|
||||
there; host, port, username, display name and sender remain in runtime YAML.
|
||||
|
||||
The SMTP adapter verifies certificates, authenticates before submission, sends
|
||||
UTF-8 MIME alternatives, and requires the final DATA 250 reply for acceptance.
|
||||
SMTP 4xx replies retry through the existing worker policy; 5xx replies fail;
|
||||
ambiguous transport outcomes remain UNKNOWN and are not immediately resent.
|
||||
Message-ID is correlation only, not a provider idempotency guarantee. Existing
|
||||
failed Resend notifications are not replayed or rewritten. The unverified
|
||||
`sxchnge.com` Resend domain is no longer the selected local delivery path.
|
||||
|
||||
Live verified-TLS SMTP authentication and NOOP succeeded with reply 250, without
|
||||
submitting email. SMTP adapter race tests and backend lint pass. Local tooling
|
||||
fixtures pass, including password-only Vault validation. Broader validation is
|
||||
currently blocked by Docker: the service integration test timed out starting its
|
||||
Testcontainers reaper; both `GOFLAGS=-p=2 make test` and its `-p=1` retry fail in
|
||||
the existing credential-rotation setup while copying files into a disposable
|
||||
MongoDB container (90-second context timeout). No checks were disabled. Fresh
|
||||
stable image pins resolved on retry; two image builds hit a Go proxy TLS
|
||||
handshake timeout during protobuf generator installation. The generator stage
|
||||
now shares the existing BuildKit Go module/build caches, preserving its pinned
|
||||
tool versions and generation command; the image rebuild is in progress.
|
||||
|
||||
|
||||
With `api.sendico` configured, the product BFF creates the local pending customer
|
||||
and activation token atomically, then registers an independent individual through
|
||||
Sendico's public HTTP API and submits the sign-up assertions. It persists the
|
||||
|
||||
Reference in new issue
Block a user