Add opt-in tenant-to-client bindings for invitation mail
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 35s
Account journey acceptance / journeys (push) Failing after 7s

User-engine side of EMAIL-WP-0008: USER_ENGINE_MAIL_CLIENT_BINDINGS maps
tenant globs to a single client_id carried on invitation mail envelopes
only. Unmatched or conflicting tenants fail as mail_binding_denied before
either lane is contacted, and the outbox now records that reason instead
of collapsing it to delivery_unavailable. Unset keeps legacy envelopes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 445231@bnt-lap001
Assistant-Session: 024a807e-0cc5-4f2c-bc8a-b83322d1395d
This commit is contained in:
tegwick 2026-10-09 11:21:17 +02:00
parent a9360842f8
commit adf6e992a6
9 changed files with 199 additions and 2 deletions

View file

@ -0,0 +1,43 @@
---
id: USER-WP-ADHOC-2026-10-09
type: workplan
title: "Scoped invitation mail client bindings"
domain: communication
repo: user-engine
status: finished
owner: claude
created: "2026-10-09"
updated: "2026-10-09"
---
## Send an admitted tenant/client pair on invitation mail
```task
id: USER-WP-ADHOC-2026-10-09-T01
status: done
priority: medium
```
User-engine side of email-connect EMAIL-WP-0008 (caller policy draft
2026-10-08): current invitation envelopes omit `client_id`, so a scoped
user-engine caller cannot be admitted. Opt-in
`USER_ENGINE_MAIL_CLIENT_BINDINGS` maps tenant globs to one literal client id.
Files:
- `src/user_engine/mail_bindings.py` — strict loader and single-match resolver
- `src/user_engine/adapters/delivery.py` — adds `client_id` to the mail
envelope only; denial contacts neither lane
- `src/user_engine/runtime.py` — reads the env var
- `src/user_engine/service.py`, `src/user_engine/web.py` — keep
`mail_binding_denied` as the recorded failure reason with an operator status
line instead of collapsing it to `delivery_unavailable`
- `docs/configuration.md`, `tests/test_mail_bindings.py`,
`tests/test_service_operations.py`
Acceptance: unset keeps legacy envelopes; invalid config stops startup;
unmatched or conflicting tenants are denied; platform events and outbox payloads
never carry `client_id`; `make test` and `make check-layer` pass.
Not done here: activation. Deploy config, email-connect caller admission
(EMAIL-WP-0008-T03) and token custody stay with their owners and the founder.