Operations
Runspace pools
Section titled “Runspace pools”- Warm, leased, recycled. Sessions are lease-exclusive (one caller per slot at a time) and recycled on age / request-count / health, with hard per-cmdlet and connect timeouts and off-path recovery of wedged slots.
- Idle-slot sweeper. A background sweeper proactively recycles aged idle slots that the lease-time recycle would otherwise miss - closing an EXO per-runspace memory-growth path on low-traffic pools. It participates in the pool’s permit protocol so it can never race a lease.
Rate limiting & throttling
Section titled “Rate limiting & throttling”- Per-principal token bucket - 100 tokens, replenished 100 every 5 min (so a 100-request
burst, then ≈20 req/min sustained). Partitioned
oid→appid→ anonymous IP, so an app and an interactive user never share a bucket.QueueLimit 0: over-limit is an instant429+Retry-After: 300+ the canonicalThrottledenvelope. - EXO throttles surface as
429with a conservativeRetry-Afterso callers back off instead of retry-storming.
Measured ceiling (2026-06-02, live tenant):
| Probe | Result | Interpretation |
|---|---|---|
| 200 concurrent requests, fresh bucket | exactly 100 × 200 + 100 × 429 |
confirms the bucket size; 429 carries Retry-After: 300 |
| 6 / 12 concurrent EXO-backed reads | 2 / 4 clean waves; ≈7.7 req/s effective | confirms runspace PoolSize = 3 is the concurrency gate |
| single EXO-backed read | ~0.37 s median | per-request latency floor (in-process cmdlet + EXO round-trip) |
The raw engine tops out near ~450 req/min (pool of 3 × ~2.6 reads/s). The 20/min policy sits
well below that by design - it mirrors EXO’s per-tenant cmdlet quota, not the server’s capacity.
The throughput levers are RunspaceHostOptions.PoolSize (concurrency) and the token-bucket settings
(TokenLimit / TokensPerPeriod / ReplenishmentPeriod).
Health & hardening
Section titled “Health & hardening”/healthz(liveness) and/readyz(real per-slot pool readiness).- Security headers on every response (
X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN,Referrer-Policy: no-referrer; HSTS outside Development). - Module hygiene - the EOM module floor is pinned to the documented 3.9.x baseline; an opt-in
EXOModuleBasePathcontrols where the module’s temp copy lives on long-lived hosts.
Local-dev gotchas
Section titled “Local-dev gotchas”- EOM < 3.0 is unsupported; 3.5.0 breaks in-process hosting - use 3.9.x.
- The REST-based
Get-EXOMailboxcan’t run in-process (an OData assembly clash); controllers route through the classicGet-Mailbox, which works. - Mailbox-scoped cmdlets return a clean
404when the identity isn’t a mailbox - expected.

