Description
Today, the app scopes data by a shared teamKey string (stored on users and ETL tables). This makes “team code” function like a shared secret: anyone who learns/guesses it can join that workspace.
This epic upgrades the system to a proper model where:
- Teams are first-class records
- Users gain access only through explicit membership
- Joining a team happens via secure invite tokens (not guessable codes)
- Admins can manage members/invites (revoke access without rotating a shared code)
Goals
- Eliminate “guess team code → join workspace” risk
- Support revocation (remove a member without affecting everyone)
- Provide auditability (who invited whom, when used)
- Lay groundwork for roles (ADMIN vs MEMBER)
TODOs
Blockers / Decisions
Notes
This epic can be implemented incrementally:
- Add Teams/Memberships/Invites alongside current
teamKey scoping (bridge phase)
- Migrate ETL partitioning from
teamKey columns to teamId columns (refactor phase)
- Remove legacy
teamKey usage once migration is complete
Description
Today, the app scopes data by a shared
teamKeystring (stored on users and ETL tables). This makes “team code” function like a shared secret: anyone who learns/guesses it can join that workspace.This epic upgrades the system to a proper model where:
Goals
TODOs
teamstable/entityteam_membershipstable/entity (user ↔ team, role)team_invitestable/entity (token, expiry, used state)teamKeyvaluesteamKeyteam_key→team_id(or bridge both temporarily)GET /join?token=...to redeem inviteteamKey...ByTeamKey...queries with...ByTeamId...Blockers / Decisions
Notes
This epic can be implemented incrementally:
teamKeyscoping (bridge phase)teamKeycolumns toteamIdcolumns (refactor phase)teamKeyusage once migration is complete