Skip to content

A record an admin creates over HTTP is immediately invisible to its own creator when the caller has no active organization and the Layer 0 wall is active (organization_id stamped NULL) #8208

Description

@os-zhuang

Found incidentally while building #8119's dogfood control — it is what made a "grant a share on a local record" control fail with 404. Filed unassigned. Scope caveat up front: I have not established whether this reproduces on a real deployment (see "What I did not check"), which is exactly why this is a finding rather than a bug report.

Measured, on a booted showcase stack

Boot: bootStack(showcaseStack, { multiTenant: 'posture-only' }). The boot log reports the wall is on:

INFO [security] tenancy posture 'isolated' — the Layer 0 organization wall is ACTIVE
(organization_id = active organization)

The seeded platform admin (stack.signIn()) resolves an authz context carrying no tenantId at all — its keys are positions, permissions, systemPermissions, org_user_ids, accessible_org_ids, accessToken, email, userId, posture.

That admin then creates a private record through the real HTTP path and reads it back:

POST /api/v1/data/showcase_private_note -> 2xx, row created
stored row: { owner_id: 'PK07N5…', created_by: 'PK07N5…', ← the admin, both
organization_id: null, owning_business_unit_id: null }
GET /api/v1/data/showcase_private_note -> 200 {"records":[],"total":0}
GET /api/v1/data/showcase_private_note/:id -> 404

The row exists (a system-context find returns it in full). Its creator owns it. And its creator cannot see it through either read surface.

Why it happens, as far as I traced it

The write stamps organization_id from the caller's active organization, which is absent, so the row is born org-less. The read then applies the Layer 0 wall, and "no active organization" is deliberately not "every organization" — the fail-closed rule stated in activeOrganizationId's docblock and ADR-0095 D1 / ADR-0105 D1. Both halves are individually correct and defensible. Together they produce a write that succeeds and a record that is unreachable by the person who just made it.

The asymmetry is the interesting part: the write path treats a missing active org as "stamp NULL and proceed", while the read path treats the same missing value as "deny". If the write is allowed to proceed without an organization, something has to be able to read the result back.

What I did not check

  • Whether a real (non-harness) deployment ever puts an admin in this state. A seeded admin with no default organization membership may be specific to the verify harness's boot, in which case this is a test-fixture sharpness issue and not a product defect. Checking that is the first triage step.
  • Whether the same happens under single / group postures, or only isolated.
  • Whether the write path should refuse, stamp a default, or the read path should admit org-less rows to their owner — three different fixes with different blast radii, which is why this carries no recommendation.

Related


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions