Domain ← Application ← Infrastructure ← API and Worker hosts.
Deployables
Signature hot path
GET /api/v1/signature?context=new|reply|forward&from={smtp?}
Every add-in request follows this path. The hot path never calls Microsoft Graph in steady state and never evaluates raw rule documents; it reads a compiled, immutable snapshot.
- Validate the Office SSO bearer token (audience = our multi-tenant API, signature, lifetime) →
tid,oid. - Look up
tidin the tenant registry (memory cache, 60 s). Unknown →403 tenant_not_onboarded. Not licensed →402 license_inactive(body explains; add-in inserts nothing). - Load the tenant’s current
configSnapshot(memory cache keyed by snapshot version; invalidated via change feed → Redis pub/sub or short TTL). - Load the user’s directory document (point read). Missing → fetch from Graph app-only once and upsert (lazy fill).
- Evaluate rules (README §5) → template version → render placeholders → return HTML +
ETag+ photo/asset references. - Add-in caches by
ETagper session;If-None-Match→304.
Data
Cosmos DB for NoSQL. All containers are partitioned by tenant. Per-tenant configuration is small, so related documents share a partition and can be written atomically with a transactional batch. Every document carriesid, tenantId, type, schemaVersion, createdAtUtc, updatedAtUtc and _etag (optimistic concurrency on all updates).
Container access by project
Databasesignatures (one Cosmos DB account, serverless). Each host authenticates to Cosmos DB with its own managed identity (DefaultAzureCredential, no keys). Which repositories a host registers decides what it can reach: AddInfrastructure is shared; AddPublisherInfrastructure runs only in the Publisher API; AddMarketplaceWebhookInfrastructure runs in the Signature API and the Worker; AddLifecycleInfrastructure runs only in the Worker. Solid arrows exist in code today; dotted arrows are planned (README §12).
Background jobs
All jobs: correlation ID per run, structured logs, dead-letter on poison messages, alert on repeated failure. Exceptions are logged once at the job boundary.
Scaling
Sizing estimates (validate with load tests): 1 000 tenants × average 200 mailboxes = 200 000 mailboxes; ~20 compose events per mailbox per working day → ~4 M signature requests/day ≈ 50 rps average, ~500 rps peak. With add-inETag caching most requests become 304 without a render.
Solution layout
Where the full design lives
The README at the repository root is the blueprint of decisions, assumptions and open questions. Use it for:- Commercial model and Marketplace integration: README §2 and §4.8
- Directory sync and identity: README §4.6 and §4.7
- Metering: README §4.9
- Add-in distribution: README §4.10, and Publishing and test deployment
- Security and compliance: README §9
- Delivery plan and open questions: README §12 and §13