Skip to content

Day 32 — Security, Tenancy, and Quotas

Today Mosaic assumes every input may be adversarial and every identifier may belong to another tenant.

Do not load a run by global ID and check tenant afterward. Make tenant part of the query and artifact locator so cross-tenant data is never returned to application code.

async fn load_run(&self, tenant: TenantId, run: RunId) -> Result<TenantRun, StoreError>;

Authentication establishes identity. Authorization decides capability. Tenant scope confines data. Quotas govern resources. Keep these questions separate.

Cover prompt injection in documents/OCR, tool argument smuggling, path traversal, SSRF in URL tools, secret exfiltration, oversized media, decompression bombs, model-output injection into HTML/SQL/shell, and replay of approvals or idempotency keys.

Treat model output as untrusted input to every downstream renderer and effect. Escape by destination and prefer typed APIs over command strings.

Reserve upload bytes, stored artifacts, concurrent runs, model calls, tokens, tool calls, accelerator seconds, and cost before dispatch. Commit actual usage and release unused reservation.

Run:

Terminal window
cd rust/rust-ai-engineering
cargo run -q -p mosaic-policy --example tenant_quota
cargo run -q -p mosaic-tools --example untrusted_tool_observation

Attempt cross-tenant artifact lookup, injected OCR instructions, unauthorized external write, quota race, expired grant, and symlink path escape. Required result: no forbidden effect and no secret in error, trace, or model context.

Produce a threat model with assets, trust boundaries, attacker capabilities, abuse cases, controls, tests, residual risk, and incident owner.

Deep references: Authentication, authorization, tenancy, and quotas and Agent threat model.

Next: Day 33 — Observe and Roll Back →.