Organizations
The top-level tenant in Project88 — what an org owns, how membership works, and how to switch between them.
An organization is the top-level tenant. Every resource in Project88 — agents, conversations, pages, data tables, canvases, provider keys, integrations — is scoped to an org.
What an org owns
- Members with one of three roles:
owner,admin,member. - Canvases — every app and every user-created board hangs directly off the org (no intermediate workspace layer). A protected Home canvas is auto-created with the org.
- Provider API keys for the LLM providers you've connected.
- Integrations (OAuth connections to Gmail, Calendar, Slack, etc.).
- Billing plan —
free,pro, orenterprise. Usage is tracked inusage_logsper-org for token-based billing. - Display metadata —
icon_name(Lucide icon) andcolorfor the picker UI.
How orgs are created
On signup we auto-create:
- A profile row linked to your Supabase Auth user.
- A personal organization ("Personal Org" by default).
- An org_members row making you the
owner. - A Home canvas inside the org (via the
handle_new_org_home_canvastrigger).
This is wired up with three Postgres triggers (handle_new_user,
handle_new_user_org, handle_new_org_home_canvas), so the first time
you sign in you already land on a complete environment.
You can create additional orgs from the org switcher in the breadcrumb.
How membership works
Org membership is stored in the org_members table — a join between
auth.users and organizations with a role column. Owners and admins
can:
- Invite and remove members
- Edit org name, slug, icon, color
- Manage provider keys and integrations
- Change the billing plan
- Toggle each canvas between org-wide and private, and manage its member list (see Canvases and widgets)
- Delete the org
Regular members can read every org-wide canvas and create their own resources (agents, pages, data) but can't change org-level config.
When a non-owner is added to an org, every member receives a real-time
notification ("<display name> joined <org>") via the
notify_on_org_member_join trigger — the owner's own self-add at org
creation is skipped.
Invitations live in their own table; see Members and roles.
Switching orgs
The current org is persisted to localStorage and reflected in the URL:
/:orgSlug — lands on Home
/:orgSlug/<appSlug> — reserved apps (pages, data, calendar, …)
/:orgSlug/canvas-<userSlug> — user-created canvasesSwitch orgs from:
- The org switcher in the breadcrumb dropdown (top-left).
- The org settings modal (Settings → General).
Switching navigates to the new org's URL and reloads canvases, agents, pages, data, conversations, and provider keys.
Security & isolation
Every Project88 table has Row Level Security enabled. Policies enforce
that you can only read or write rows whose org_id matches an org you're a
member of. Writes that modify org-level config (members, billing, provider
keys, canvas permissions) additionally check that you're an owner or
admin.
For canvases, RLS adds a second gate: org-wide canvases are visible to
every member, private canvases require an explicit canvas_members row.
The canvas_visible_to and canvas_writable_to security-definer helpers
break the cross-table recursion that pure subquery RLS would hit.
The org_members table itself uses a security-definer helper to break a
recursive RLS dependency (migration 013), but the user-facing policy is
still strict: you only see members of orgs you're in.
Key concepts
The vocabulary of Project88 — orgs, agents, conversations, pages, data, canvases, widgets — in one page.
Canvases and widgets
Apps in Project88 are either native shell surfaces or React Flow canvases you can extend with draggable widgets — with per-canvas permissions and per-user view state.