Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/architecture/tenant-identity-read-leak-matrix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Tenant identity read isolation evidence

[EN-01 backend #157](https://github.com/bdgrz/compliance/issues/157) requires
standalone and split API/worker proof for each implemented tenant-owned read.
`TenantIdentityReadLeakMatrixE2ETests` runs the same two-tenant broker scenario in
both host modes. It creates unrelated verified owners, tenant records, and two
invitations per tenant, then uses separate owner, outsider, and platform-operator
sessions over HTTP and MCP. Personal invitation acceptance is HTTP-only and is
outside this read matrix.

| Read family | Current HTTP and MCP proof | Remaining proof |
| --- | --- | --- |
| Tenant detail | Owners see their own tenant; another owner and an outsider receive `NotFound`. A platform operator can read both tenants as the accepted metadata exception. | Other tenant-owned details are inventoried in their own capability matrices. |
| My tenants | Each owner sees only their tenant. The outsider and operator see no memberships. Both transports traverse `limit=1` cursors, including empty filtered pages; unauthenticated HTTP is rejected. | This is a membership view, not a tenant inventory for the operator. |
| Tenant members | The operator sees only the owner of each tenant. A tenant owner and outsider cannot use the operator-only list. | This fixture has one member per tenant, so it does not prove member-list pagination after a second membership activates. |
| Tenant invitations | Each owner sees only their own two invitation emails while traversing `limit=1` cursors. Foreign tenant paths fail; filtering the owned tenant with the other tenant's email returns an empty page. A cursor issued in tenant A is rejected in B with HTTP `BadRequest` and MCP `Validation`. | Delivery and personal acceptance have separate lifecycle evidence. |
| Effective member access | Each owner reads their own tenant access. Foreign tenant and foreign user combinations return `NotFound`. | Actor snapshots and later identity replacement/deprovisioning still need lifecycle proof. |

These read contracts return items and continuation cursors, without a total
count field. The scenario waits for projections and invitation authority before
asserting isolation; it does not treat an empty page caused by lag as proof of
filtering. Broader current-surface coverage and actor lifecycle evidence remain
on [#157](https://github.com/bdgrz/compliance/issues/157).
15 changes: 8 additions & 7 deletions docs/architecture/tenant-read-leak-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ The route inventory below follows `src/Compliance.App/Program.cs`.
| Application list and boundary references | `ProjectionReadConsistencyE2ETests.ShouldNeverReturnStaleProjectionOrCrossTenantRowsGivenStandaloneOrSplitHost` covers application-list isolation and boundary-reference exact/non-disclosure in both modes. | Application revision, system-instance and reference list pagination, change preview, and remaining HTTP/MCP permutations. |
| Snapshot read, verification and manifest regeneration | `SnapshotE2ETests.ShouldFreezeExactScopeGivenStandaloneOrSplitWorker` covers cross-tenant verification/manifest and outsider denial in both modes. | Two-tenant snapshot list and cursor, plus full MCP list proof. |
| Platform metadata exception | `OperatorPortfolioE2ETests.ShouldListPlatformTenantMetadataGivenConfiguredOperatorAndHostMode` covers operator-only tenant metadata pagination in both modes. | Keep this separate from client business-record access. |
| Tenant detail, self-list, operator member list, invitation list, effective member access and matching MCP reads | `TenantIdentityReadLeakMatrixE2ETests.ShouldScopeTenantMemberInvitationAndSelfReadsGivenTwoTenants`: both modes; separate tenant owners and an outsider, operator metadata and member-list authority, HTTP/MCP self-list and invitation cursors through exhaustion, strict foreign invitation cursor rejection, foreign paths and email filters, and foreign member-access denial. [Detailed evidence](tenant-identity-read-leak-matrix.md). | Member-list pagination after a second membership activates and actor replacement/deprovisioning lifecycle proof. These reads have no total-count field. |

Still open for the same two-tenant, two-host HTTP/MCP matrix: tenant member,
invitation, self-list and access reads; program list, revision and setup-work
reads; control, commitment and risk draft/history pages; client-service tenant
and program lists/history; boundary program lists, versions, decisions and impact
preview; and the remaining application and snapshot reads above. Existing
capability tests often prove standalone cross-tenant denial or split-host
recovery, but that does not prove every list and cursor in both modes.
Still open for the same two-tenant, two-host HTTP/MCP matrix: program list,
revision and setup-work reads; control, commitment and risk draft/history pages;
client-service tenant and program lists/history; boundary program lists,
versions, decisions and impact preview; and the remaining application and
snapshot reads above. Existing capability tests often prove standalone
cross-tenant denial or split-host recovery, but that does not prove every list
and cursor in both modes.

The current API has no separate tenant-owned count, export, job, notification
or artifact-content route. Import batch status carries row and invalid counts
Expand Down
Loading