Skip to main content
Wildo.ai Coming soon

Authorization and isolation

Show people the accounts they belong to

Account lists need their own boundary: an organization record is the account itself, rather than a record carrying a parent account ID. Wildo confines those collections to the caller’s organization-wide memberships.

Alex sees Acme and Northwind under My organizations, while unrelated Contoso stays outside the list.

Show people the accounts they belong to

Account lists need their own boundary: an organization record is the account itself, rather than a record carrying a parent account ID. Wildo confines those collections to the caller’s organization-wide memberships.

A requested filter can narrow that set, but cannot turn it into a directory of other customers.

Example — Filter the account switcher

A person belonging to accounts A and B asks for B and C. The confined result can include B; the requested ID for C does not create membership.

For engineers

The same scope-root authority is used by authorization and repository filtering. After identifying a governed organization collection operation, it derives the allowed IDs from the caller’s organization-wide role entries and intersects the requested IDs:

This implementation excerpt from scope-root-collection-confinement.backend.ts shows the decision in context; explanatory source comments are omitted.

function intersectRequestedIdsWithMembership(requested: unknown, membershipIds: readonly string[]): string[] {
  if (requested === undefined || requested === null) return [...membershipIds];

  const membership = new Set(membershipIds);

  if (typeof requested === 'string') {
    return membership.has(requested) ? [requested] : [];
  }

  if (Array.isArray(requested)) {
    return requested.map(String).filter((id) => membership.has(id));
  }

  if (typeof requested === 'object') {
    const operators = Object.keys(requested as Record<string, unknown>);
    const inValue = (requested as { $in?: unknown }).$in;
    if (operators.length === 1 && operators[0] === '$in' && Array.isArray(inValue)) {
      return inValue.map(String).filter((id) => membership.has(id));
    }
    return [];
  }

  return [];
}

Know which operations are set-shaped

The confinement covers list, search, count, update-many and delete-many operations on the organization root. Addressed reads have a separate per-record authorization path. A unit-only role does not become permission to enumerate the whole organization.

Requested filterResult within memberships A and B
No ID restrictionA and B
ID BB
IDs B and CB
An unsupported ID operatorNo matching IDs

The portable empty-set predicate produces no results on either database adapter. A caller-supplied condition is never a reason to drop the membership restriction.

Distinguish tenant lists from application directories

Other scope roots have explicit dispositions: the deployment application row, the person directory and anonymous sessions do not share the same tenant-membership filter. Application-wide directory admission has its own elevation policy. Internally initiated work, verified callbacks and deliberately admitted cross-tenant operations also have distinct handling; an empty membership list must not silently empty a framework maintenance sweep.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.