Resolution order
- Explicit
:user_idin the path (/v1/users/:user_id/...) X-Naive-User-Idheader- The API key’s
active_tenant_user_id(set at key creation) - The company’s default tenant_user (auto-created on signup)
default and me in positions 1–2 mean “use the default user”.
Cross-tenant guard (the ballgame)
After picking a candidate user, the resolver asserts:X-Naive-User-Id is treated as untrusted input — validated against the key’s
company exactly like a path param, never as an identity assertion. A workspace-wide key
can target any user in its own company, never another company’s.
Ids are also shape-checked: a malformed (non-uuid) :user_id / X-Naive-User-Id /
resource id returns a clean 404, never a 500 from the database rejecting the uuid
cast. (default/me sentinels are exempt — they resolve before the check.)
Every handler that loads a resource by id (card, inbox, vault entry, connection, …)
additionally passes it through assertSameCompany(resource, req) so a forged resource
id from another tenant 404s.
Workspace vs kit-scoped keys
- Workspace-wide (
account_kit_idnull): can target any user in the company. - Kit-scoped (
account_kit_idset): locked to one AccountKit.