24 KiB
Feature Research
Domain: Multi-tenancy, user accounts, and role-based access control Researched: 2026-03-18 Confidence: HIGH
Feature Landscape
Table Stakes (Users Expect These)
Features users assume exist. Missing these = product feels incomplete.
| Feature | Why Expected | Complexity | Notes |
|---|---|---|---|
| Individual user accounts | Standard expectation in any multi-user app; shared passwords are deprecated UX | MEDIUM | Email/password auth, JWT tokens (already have JWT infrastructure) |
| Per-user project ownership | Users expect "my projects" vs "shared with me" distinction | MEDIUM | Requires user_id column on projects table, workspace isolation |
| Invite-only registration | Standard pattern for self-hosted tools; prevents open registration abuse | LOW | Invite codes/tokens in database, validation before signup |
| Basic role hierarchy (viewer/editor/admin) | Industry standard in collaborative tools (GitHub, Notion, Figma all use 3-tier) | MEDIUM | Role enum on project_user junction table, permission checks |
| Edit attribution in history | Users expect to see "who changed what" in version history | LOW | Already have history table; add user_id foreign key |
| Project sharing UI | Ability to add collaborators and set their role | MEDIUM | UI for managing project members, permission checks |
| Session management | Users expect to see active sessions, logout from devices | LOW | JWT refresh tokens, token revocation list |
| Password reset flow | Expected security hygiene for email/password auth | MEDIUM | Email service integration, reset tokens, secure workflows |
Differentiators (Competitive Advantage)
Features that set the product apart. Not required, but valuable.
| Feature | Value Proposition | Complexity | Notes |
|---|---|---|---|
| Granular audit trail with rollback | Beyond basic attribution—show full change context and allow per-edit rollback | MEDIUM | Leverage existing history system; add user context to each snapshot |
| Workspace-level dashboards | Per-user analytics showing activity across all their projects | LOW | Aggregate existing project stats by user |
| Inherited permissions on requirement hierarchy | Child requirements inherit parent permissions; reduces management overhead | HIGH | Complex to implement correctly; easy to introduce leaks |
| Anonymous viewer links | Share read-only project views without requiring account | MEDIUM | Signed tokens with expiry, no-auth view endpoints |
| Activity feeds | "What changed since I last logged in" per project | MEDIUM | Requires tracking last_seen timestamps and filtering history |
| Workspace templates | Users can save project configurations as templates for reuse | LOW | Export/import existing project structure |
| Fine-grained permission customization | Beyond viewer/editor/admin—custom permission sets | HIGH | Significantly increases complexity; likely over-engineering for v2 |
Anti-Features (Commonly Requested, Often Problematic)
Features that seem good but create problems.
| Feature | Why Requested | Why Problematic | Alternative |
|---|---|---|---|
| Real-time collaborative editing | "Like Google Docs" — users assume all modern tools have it | Massive architectural lift; requires WebSockets, operational transforms, conflict resolution; SQLite poor fit | Optimistic locking with "edited by X" warnings; auto-refresh on save |
| Organization/team hierarchies | Enterprises expect nested orgs (company → department → team) | Dramatically increases permission model complexity; complicates billing/limits | Single workspace per user; flat project sharing model |
| Social login (OAuth) | Convenient for users, reduces friction | Adds external dependencies; complicates self-hosted deployments; privacy concerns | Email/password sufficient for self-hosted tool; invite-only provides security |
| Fine-grained field-level permissions | "Editor can change description but not acceptance criteria" | Explosion of permission checks; confusing UX; high maintenance burden | Role-based with clear responsibilities; don't split access within entities |
| Public project discovery | "Browse all public projects like GitHub" | Privacy concerns for self-hosted tool; invites spam/abuse | Explicit sharing only; anonymous links for specific cases |
| User-to-user direct messaging | "Chat with collaborators in-app" | Scope creep; users already have Slack/email/Discord | Keep tool focused; collaboration happens in comments/edits |
Feature Dependencies
Individual User Accounts (foundational)
├──requires──> User Authentication Flow
│ ├──requires──> Login/Logout
│ ├──requires──> Password Reset
│ └──requires──> Session Management
│
├──requires──> Per-User Project Ownership
│ ├──requires──> Workspace Isolation (tenant_id pattern)
│ └──requires──> "My Projects" vs "Shared With Me" views
│
├──requires──> Edit Attribution
│ ├──enhances──> Existing History System
│ └──requires──> User Foreign Keys on History Table
│
└──requires──> Invite-Only Registration
├──requires──> Invite Token Generation
├──requires──> Invite Validation
└──requires──> Admin Controls
Role-Based Access Control
├──requires──> Individual User Accounts (above)
├──requires──> Project-User Junction Table
│ └──stores──> Role (viewer/editor/admin/owner)
│
├──requires──> Permission Enforcement Layer
│ ├──frontend──> Route guards, UI element visibility
│ └──backend──> Endpoint authorization checks
│
└──enhances──> Project Sharing UI
└──requires──> Member Management Interface
Project Sharing UI
├──requires──> Role-Based Access Control (above)
└──requires──> Invite Notification System
Audit Trail with User Context
├──requires──> Edit Attribution (above)
└──enhances──> Existing History Visualization
Dependency Notes
- Individual User Accounts is foundational: All other features depend on this being implemented first
- Workspace Isolation must be implemented correctly from the start: Retrofitting tenant isolation is high-risk for data leaks
- Edit Attribution enhances existing history: Already have requirement_history table; adding user_id is low-risk
- RBAC requires careful sequencing: Must implement permission checks in backend before exposing sharing UI
- Invite-only registration can be parallel to RBAC: These features are independent after user accounts exist
MVP Definition
Launch With (v2.0)
Minimum viable multi-tenancy — what's needed to validate the concept.
- Individual user accounts with email/password authentication — Essential foundation
- Invite-only registration with admin-generated invite codes — Security requirement for self-hosted
- Per-user project ownership with workspace isolation — Core multi-tenancy feature
- Three-tier RBAC (owner/editor/viewer) on projects — Table stakes for collaboration
- Edit attribution in history (user_id on edits) — Minimal audit trail
- Project sharing UI with role assignment — Required to make RBAC usable
- Basic permission enforcement (backend + frontend) — Security requirement
- Password reset flow — Expected security hygiene
Add After Validation (v2.x)
Features to add once core multi-tenancy is working.
- Activity feeds per project — Add once users report "losing track of changes"
- Workspace-level dashboard — Add when users manage 5+ projects
- Anonymous viewer links — Add when users request "share with client" workflows
- Granular audit trail with per-edit rollback — Add when attribution proves insufficient
- Session management UI — Add if users report security concerns with active sessions
- Workspace templates — Add when users request "clone project structure"
Future Consideration (v3+)
Features to defer until product-market fit is established.
- Fine-grained permission customization — Defer until 3-tier RBAC proves insufficient
- Inherited permissions on hierarchy — Complex; defer until users report permission management overhead
- Organization/team hierarchies — Defer until serving enterprise customers
- OAuth/SSO providers — Defer until serving organizations requiring SSO
- Real-time collaborative editing — Major architectural change; defer until core collaboration proves valuable
Feature Prioritization Matrix
| Feature | User Value | Implementation Cost | Priority |
|---|---|---|---|
| Individual user accounts | HIGH | MEDIUM | P1 |
| Invite-only registration | HIGH | LOW | P1 |
| Per-user project ownership | HIGH | MEDIUM | P1 |
| Three-tier RBAC (owner/editor/viewer) | HIGH | MEDIUM | P1 |
| Edit attribution | HIGH | LOW | P1 |
| Project sharing UI | HIGH | MEDIUM | P1 |
| Permission enforcement layer | HIGH | HIGH | P1 |
| Password reset flow | MEDIUM | MEDIUM | P1 |
| Activity feeds | MEDIUM | MEDIUM | P2 |
| Workspace dashboard | MEDIUM | LOW | P2 |
| Anonymous viewer links | MEDIUM | MEDIUM | P2 |
| Granular audit trail | LOW | MEDIUM | P2 |
| Session management UI | LOW | LOW | P2 |
| Workspace templates | LOW | LOW | P2 |
| Fine-grained permissions | LOW | HIGH | P3 |
| Inherited permissions | MEDIUM | HIGH | P3 |
| Organization hierarchies | LOW | HIGH | P3 |
| OAuth/SSO | LOW | HIGH | P3 |
| Real-time collaboration | HIGH | VERY HIGH | P3 |
Priority key:
- P1: Must have for v2.0 launch — core multi-tenancy
- P2: Should have for v2.x — enhances collaboration
- P3: Nice to have for v3+ — future consideration
Expected Behavior and UX Patterns
Invite-Only Registration
Standard Flow:
- Admin generates invite code/link from admin panel
- Invite stored in database with: code (UUID), email (optional), created_by, expires_at, used (boolean)
- Registration page requires valid invite code before showing signup form
- After successful signup, invite marked as used (single-use) or incremented (multi-use)
- Expired/invalid codes show clear error messages
UX Patterns (2026):
- Invite links preferred over codes: Users click link → auto-fills invite code → shows signup form
- Email pre-fill: If invite includes email, pre-populate signup form (can't be changed)
- Context in email: Invitation email shows who invited them and to which workspace
- Clear expiry: Show "This invite expires in X days" prominently
- Admin controls: Dashboard showing pending/used/expired invites
Sources:
- User Onboarding Strategies in a B2B SaaS Application
- How to onboard invited users and fast-track user engagement
Per-User Project Ownership
Standard Patterns:
- Shared database with tenant_id: All users share same tables; user_id column differentiates ownership
- Workspace views: "My Projects" (owned), "Shared With Me" (collaborator), optionally "All Projects" (admin)
- Default visibility: Projects private by default; owner explicitly shares
- Transfer ownership: Admin capability to reassign project owner
- Orphaned projects: Handle deleted user accounts (reassign or archive)
Data Model:
projects:
- owner_id (FK to users)
- created_by (FK to users)
- updated_by (FK to users)
project_users (junction):
- project_id (FK to projects)
- user_id (FK to users)
- role (enum: owner, editor, viewer)
- added_by (FK to users)
- added_at (timestamp)
Critical Security Pattern:
- Every query MUST include
WHERE user_id = ? OR EXISTS (SELECT 1 FROM project_users WHERE project_id = ? AND user_id = ?) - Enforce at ORM level or middleware layer, not per-endpoint (reduces chance of missing checks)
Sources:
- The developer's guide to SaaS multi-tenant architecture — WorkOS
- Tenant Data Isolation: 5 Patterns That Actually Work
Role-Based Access Control (Viewer/Editor/Admin)
Industry Standard Three-Tier Model:
| Role | Read | Create/Edit | Delete | Manage Members | Transfer Ownership |
|---|---|---|---|---|---|
| Owner | ✓ | ✓ | ✓ | ✓ | ✓ |
| Editor | ✓ | ✓ | ✓ (own edits) | ✗ | ✗ |
| Viewer | ✓ | ✗ | ✗ | ✗ | ✗ |
Implementation Pattern:
# Backend: Permission decorator
@require_project_permission(Permission.EDIT)
def update_requirement(project_id, requirement_id, data):
# Only called if user has editor+ role
pass
# Frontend: Structural directives
<button *ngIf="hasPermission('edit')">Edit</button>
UX Expectations:
- Graceful degradation: Viewers see read-only interface, not error messages
- Clear role badges: Show user's role in project header ("You are an Editor")
- Contextual actions: Only show actions user can perform (hide "Delete" for viewers)
- Permission feedback: If user somehow triggers unauthorized action, show clear explanation
Common Mistakes to Avoid:
- Authorization only on frontend: Always enforce on backend; frontend is convenience only
- Inconsistent enforcement: Missing permission checks on some endpoints creates security holes
- Role explosion: Don't create custom roles too early; three tiers sufficient for most cases
- Cache-based authorization: Store role in user-controllable location (localStorage) for UI only; always verify on backend
Sources:
- How to Build a Role-Based Access Control Layer
- Role-Based Access Control Best Practices for 2026
- Guide to sharing and permissions – Figma Learn
Edit Attribution
Expected Behavior:
- Every edit records: user_id, timestamp, before/after state
- History view shows: "John Doe edited requirement title at 2:34 PM"
- Hover/click shows full context: what changed, by whom, when
- Filter history: "Show only my edits" or "Show edits by John"
Google Docs Pattern (Gold Standard):
- Show Editors feature: Right-click text → "Show Editors" → see who edited that specific range
- Named versions: Users can create named checkpoints ("Before client review")
- Continuous recording: Every change captured automatically (no manual "save version")
- Real-time attribution: See colored cursors/highlights showing who's editing what
Adapted for Non-Real-Time:
- Display "Last edited by X at Y" on each requirement card
- History panel shows chronological list with user avatars
- Diff view highlights changes with attribution ("Added by John", "Removed by Jane")
- Filter/search history by user
Implementation Notes:
- Already have requirement_history table
- Add user_id foreign key to requirement_history
- Add created_by, updated_by to requirement_nodes
- Frontend displays user info from history records
Sources:
- Google Workspace Updates: "Show Editors" provides more context on changes made in Google Docs
- How to See Version History in Google Docs
Project Sharing UI
Standard Patterns:
- Share button: Prominent in project header
- Member list: Shows current collaborators with roles
- Add member flow:
- Input: Email or username
- Select role: Owner/Editor/Viewer (with tooltips explaining each)
- Optional: Add message to invitation
- Send: Generates notification and adds to project
- Role modification: Click role dropdown → change role → confirm
- Remove member: Click X or "Remove" → confirm dialog → remove access
GitHub Pattern:
- Settings → Collaborators → "Add people" → username → role → "Add"
- Shows pending invitations separately from active members
- Email notification when invited
Notion Pattern:
- "Share" button → modal with member list
- Inline role editing (click role → dropdown)
- "Copy link" for anonymous sharing
- Shows "X has access via Team Y" (inherited permissions)
Figma Pattern:
- Share button → modal showing team/project/file hierarchy
- Link sharing with permission level attached to link
- "Anyone with link can view/edit"
Recommended Approach:
- Hybrid: GitHub-style explicit invitations + Notion-style inline role editing
- No link-based sharing in v2.0 (add in v2.x as anonymous viewer links)
- Clear visual distinction between owner (1 per project) and collaborators
Sources:
Complexity on Existing Features
Impact on Existing Edit History
Current State:
- requirement_history table stores snapshots (before/after JSON)
- No user attribution — assumes single user
Required Changes:
- Add user_id column (FK to users)
- Migrate existing records: set user_id to admin or mark as "system"
- Update create_history_record() to include current_user_id
- Update history display to show user info
Complexity: LOW — Additive change, doesn't break existing functionality
Impact on Existing Graph Visualization
Current State:
- Graph shows all requirements in project
- No permission-based filtering
Required Changes:
- Filter requirements based on project access (user is owner/editor/viewer)
- Show read-only indicators for viewers
- Disable edit actions (drag, delete, create) for viewers
- Show permission indicators in detail panel
Complexity: MEDIUM — Requires permission checks in graph component, but no architectural changes
Impact on Existing CRUD Operations
Current State:
- No user context on create/update/delete
- No ownership checks
Required Changes:
- Add user_id to CREATE operations (set owner)
- Add permission checks to UPDATE operations (is editor+?)
- Add permission checks to DELETE operations (is owner or editor with own edits?)
- Add created_by, updated_by columns to requirement_nodes
- Migrate existing records: set created_by to admin or null
Complexity: MEDIUM — Every endpoint needs permission middleware, but follows consistent pattern
Impact on Existing Dashboard
Current State:
- Shows all projects globally
- Statistics aggregate all data
Required Changes:
- Filter projects by ownership + shared access
- Separate "My Projects" and "Shared With Me" views
- Per-project statistics respect permissions
- Add "Collaborators" count to project cards
Complexity: LOW — Mostly UI changes, backend already has project filtering
Impact on Database Schema
New Tables Required:
users (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
name TEXT,
created_at TIMESTAMP,
last_login TIMESTAMP
)
invite_codes (
id INTEGER PRIMARY KEY,
code TEXT UNIQUE NOT NULL,
email TEXT, -- optional: pre-assign to email
created_by INTEGER REFERENCES users(id),
expires_at TIMESTAMP,
used BOOLEAN DEFAULT FALSE,
used_by INTEGER REFERENCES users(id),
used_at TIMESTAMP
)
project_users (
id INTEGER PRIMARY KEY,
project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
role TEXT CHECK(role IN ('owner', 'editor', 'viewer')),
added_by INTEGER REFERENCES users(id),
added_at TIMESTAMP,
UNIQUE(project_id, user_id)
)
Modified Tables:
projects:
+ owner_id INTEGER REFERENCES users(id)
+ created_by INTEGER REFERENCES users(id)
+ updated_by INTEGER REFERENCES users(id)
requirement_nodes:
+ created_by INTEGER REFERENCES users(id)
+ updated_by INTEGER REFERENCES users(id)
requirement_history:
+ user_id INTEGER REFERENCES users(id)
graph_layouts:
+ user_id INTEGER REFERENCES users(id) -- per-user layouts
Migration Strategy:
- Create users table first
- Create admin user (id=1)
- Add foreign key columns to existing tables (nullable initially)
- Set all existing records to admin user (id=1)
- Make columns NOT NULL after backfill
- Create invite_codes and project_users tables
Complexity: MEDIUM — Careful sequencing required, but no data loss risk if done correctly
Sources
UX and Design Patterns
- Best Sign Up Flows (2026): 15 UX Examples That Convert
- Login & Signup UX: The 2025 Guide to Best Practices
- Designing an intuitive user flow for inviting teammates
RBAC and Authorization
- How to Build a Role-Based Access Control Layer
- Role-Based Access Control Best Practices for 2026
- Role-based Access Control: Five Common Authorization Patterns
- Best Practices for Multi-Tenant Authorization
Multi-Tenancy Architecture
- The developer's guide to SaaS multi-tenant architecture — WorkOS
- Multi-Tenant SaaS Architecture: Complete Guide
- Data Isolation in Multi-Tenant SaaS
- Tenant Data Isolation: 5 Patterns That Actually Work
Security and Data Isolation
- Preventing Cross-Tenant Data Leakage in Multi-Tenant SaaS Systems
- Multi-Tenant Leakage: When "Row-Level Security" Fails in SaaS
- Tenant Isolation in Multi-Tenant Systems
- Access control vulnerabilities and privilege escalation
Audit Trails and Attribution
- What Is an Audit Trail? Definition and Best Practices
- 10 Essential Audit Trail Best Practices for 2026
- Google Workspace Updates: "Show Editors" in Google Docs
- How to See Version History in Google Docs
User Onboarding
- User Onboarding Strategies in a B2B SaaS Application
- How to onboard invited users and fast-track user engagement
- SaaS Onboarding Flows That Actually Convert in 2026
Collaboration Patterns
Feature research for: req-planner v2.0 Multi-Tenancy & User Accounts Researched: 2026-03-18