106 lines
3.8 KiB
Markdown
106 lines
3.8 KiB
Markdown
# Research: Workspace Model, Memberships & Managed Tenants (v2)
|
||
|
||
**Date**: 2026-01-31
|
||
**Feature**: [spec.md](spec.md)
|
||
|
||
This document consolidates the key design decisions needed to implement the spec in this repo.
|
||
|
||
## Decisions
|
||
|
||
### 1) Workspace identifier in URLs
|
||
|
||
**Decision**: Prefer Workspace `slug` in URLs; fall back to numeric `id` when a workspace has no slug.
|
||
|
||
**Rationale**:
|
||
- Human-friendly URLs for admins (`/admin/w/acme/...`).
|
||
- Keeps migration pain low because slugs can be introduced incrementally.
|
||
|
||
**Alternatives considered**:
|
||
- Always numeric id: easiest but less readable.
|
||
- Slug-only required: clean but increases migration/validation burden.
|
||
|
||
---
|
||
|
||
### 2) Persisting current workspace selection
|
||
|
||
**Decision**: Persist `current_workspace_id` in session AND store a nullable `users.last_workspace_id`.
|
||
|
||
**Rationale**:
|
||
- Session is canonical for the current request.
|
||
- DB persistence improves UX across new sessions / devices without requiring the user to re-select every time.
|
||
|
||
**Alternatives considered**:
|
||
- Session only: simpler but annoying across logins.
|
||
- URL-only: makes deep links harder and doesn’t support “default workspace” semantics.
|
||
|
||
---
|
||
|
||
### 3) Managed Tenant uniqueness
|
||
|
||
**Decision**: Entra tenant id is globally unique (a Managed Tenant belongs to exactly one Workspace).
|
||
|
||
**Rationale**:
|
||
- Avoids ambiguous ownership and accidental double-management of the same Microsoft tenant.
|
||
- Aligns with “no tenant-in-tenant” goal.
|
||
|
||
**Alternatives considered**:
|
||
- Unique per workspace: enables duplicates but creates confusing operational ownership.
|
||
|
||
---
|
||
|
||
### 4) Zero-membership entry behavior
|
||
|
||
**Decision**: Show a neutral `/admin/no-access` page for users with 0 memberships (not 404).
|
||
|
||
**Rationale**:
|
||
- Clear UX while still not leaking any workspace existence.
|
||
|
||
**Alternatives considered**:
|
||
- 404: secure but confusing; users think the app is broken.
|
||
|
||
---
|
||
|
||
### 5) How to scope the admin panel without Filament tenancy
|
||
|
||
**Decision**: Make Workspace context a first-class routing concern (`/admin/w/{workspace}/...`) enforced via middleware + session context. Do not use Filament tenancy (`/admin/t/{tenant}`) as the primary structure.
|
||
|
||
**Rationale**:
|
||
- Meets “no tenant-in-tenant” and removes the overloaded “tenant” concept in UI.
|
||
- Middleware is the cleanest place to enforce deny-as-not-found for non-members.
|
||
|
||
**Alternatives considered**:
|
||
- Use Filament tenancy to represent workspaces: would keep the same tenancy mechanisms but continues the “tenant-in-tenant” confusion.
|
||
|
||
---
|
||
|
||
### 6) Global search scoping
|
||
|
||
**Decision**: Global search must be scoped to the active Workspace and return no results outside it.
|
||
|
||
**Rationale**:
|
||
- Prevents leakage (no hints) and aligns with constitution RBAC-UX.
|
||
- Repo already has a tenant-scoped global search trait; we can introduce a workspace-scoped variant.
|
||
|
||
**Alternatives considered**:
|
||
- Hide search results at render time: insufficient, because global search queries must also be scoped.
|
||
|
||
---
|
||
|
||
### 7) Audit logging for workspace membership changes
|
||
|
||
**Decision**: Introduce a workspace-level audit log stream for membership mutations.
|
||
|
||
**Rationale**:
|
||
- Existing `audit_logs` table is tenant-scoped (requires `tenant_id`) and cannot represent workspace-only changes cleanly.
|
||
- Additive schema avoids breaking existing auditing.
|
||
|
||
**Alternatives considered**:
|
||
- Make `audit_logs.tenant_id` nullable and add `workspace_id`: higher migration and code risk.
|
||
|
||
## Open Questions (implementation-level)
|
||
|
||
These are not spec ambiguities but implementation choices to resolve while coding:
|
||
|
||
- Whether to build a generic “scope context” abstraction (tenant/workspace) or implement a workspace-specific parallel to the existing tenant helpers.
|
||
- How to progressively migrate existing Filament resources from `/admin/t/{tenant}` to `/admin/w/{workspace}` without breaking deep links (redirect strategy).
|