Platform half of objectstack-ai/cloud#1512 (filed by the triage seat; the hotcrm crm_contract half stays on that card). Measured on a composed multi-org boot (EE 4.1.0 image, OS_TENANCY_POSTURE=isolated), the tenant-audit instrument prints:
[tenant-audit] updateMany on tenant-scoped object "sys_http_delivery" without
options.tenantId — writes will not be tenant-isolated.
[tenant-audit] updateMany on tenant-scoped object "sys_notification_delivery" without
options.tenantId — writes will not be tenant-isolated.
These are the platform's own delivery objects (webhook / notification delivery bookkeeping). On single-org shapes the missing scope is harmless; on multi-org shapes an unscoped updateMany is a cross-tenant write.
What the fix must decide, per call site
Find the updateMany call sites for these two objects (webhooks / notifications delivery maintenance paths) and, for each, either:
- pass the request's
options.tenantId (if the operation is tenant-contextual), or - mark it
bypassTenantAuditonly if it is genuinely a global maintenance task (e.g. env-wide retention sweep) — with a comment saying why.
Silencing the audit line without classifying the write is not a fix; the audit exists to force exactly this classification.
Repro
EE 4.1.0 image + deploy/composed.env.example + a composed artifact boot; read docker compose logs app during startup. Green = no [tenant-audit] line for these two objects, or an explicit, justified bypass at each remaining site.
Platform half of objectstack-ai/cloud#1512 (filed by the triage seat; the hotcrm
crm_contracthalf stays on that card). Measured on a composed multi-org boot (EE 4.1.0 image,OS_TENANCY_POSTURE=isolated), the tenant-audit instrument prints:These are the platform's own delivery objects (webhook / notification delivery bookkeeping). On single-org shapes the missing scope is harmless; on multi-org shapes an unscoped
updateManyis a cross-tenant write.What the fix must decide, per call site
Find the
updateManycall sites for these two objects (webhooks / notifications delivery maintenance paths) and, for each, either:options.tenantId(if the operation is tenant-contextual), orbypassTenantAuditonly if it is genuinely a global maintenance task (e.g. env-wide retention sweep) — with a comment saying why.Silencing the audit line without classifying the write is not a fix; the audit exists to force exactly this classification.
Repro
EE 4.1.0 image +
deploy/composed.env.example+ a composed artifact boot; readdocker compose logs appduring startup. Green = no[tenant-audit]line for these two objects, or an explicit, justified bypass at each remaining site.