diff --git a/.github/wiki/ARCHITECTURE.md b/.github/wiki/ARCHITECTURE.md index 12761f464..e475645ef 100644 --- a/.github/wiki/ARCHITECTURE.md +++ b/.github/wiki/ARCHITECTURE.md @@ -54,16 +54,16 @@ One app, one backend, independent deployment. monorepo/ ├── backend/ # Shared nSelf backend ├── nchat/ # This app -├── ntv/ # Another app +├── example/ # Another app └── nfamily/ # Another app ``` Multiple apps, one backend, shared authentication, per-app roles. ### 3. Per-App RBAC/ACL In monorepo deployments, users authenticate once but can have **different roles in different apps**: -- Admin in ɳChat, regular user in ɳTV +- Admin in ɳChat, regular user in Example App - Owner in ɳFamily, guest in ɳChat -- Moderator in ɳTV, member in ɳFamily +- Moderator in Example App, member in ɳFamily This is implemented via three PostgreSQL tables: - `apps` - Registry of all applications @@ -184,13 +184,13 @@ monorepo/ │ ├── migrations/ │ │ ├── 001_create_users.sql │ │ ├── 002_add_nchat_tables.sql -│ │ ├── 003_add_ntv_tables.sql +│ │ ├── 003_add_example_tables.sql │ │ └── 004_add_per_app_rbac.sql │ └── docker-compose.yml ├── nchat/ # ɳChat app │ ├── frontend/ │ └── package.json -├── ntv/ # ɳTV app (example) +├── example/ # Example application │ ├── frontend/ │ └── package.json └── nfamily/ # ɳFamily app (example) @@ -208,12 +208,12 @@ nself start # 2. Run migrations for all apps nself exec postgres psql -U postgres -d nself < migrations/001_create_users.sql nself exec postgres psql -U postgres -d nself < migrations/002_add_nchat_tables.sql -nself exec postgres psql -U postgres -d nself < migrations/003_add_ntv_tables.sql +nself exec postgres psql -U postgres -d nself < migrations/003_add_example_tables.sql nself exec postgres psql -U postgres -d nself < migrations/004_add_per_app_rbac.sql # 3. Start each app cd ../nchat/frontend && pnpm dev -cd ../ntv/frontend && pnpm dev --port 3001 +cd ../example/frontend && pnpm dev --port 3001 cd ../nfamily/frontend && pnpm dev --port 3002 ``` @@ -225,9 +225,9 @@ NEXT_PUBLIC_APP_NAME=ɳChat NEXT_PUBLIC_GRAPHQL_URL=http://api.localhost/v1/graphql NEXT_PUBLIC_AUTH_URL=http://auth.localhost/v1/auth -# ntv/frontend/.env.local -NEXT_PUBLIC_APP_ID=ntv -NEXT_PUBLIC_APP_NAME=ɳTV +# example/frontend/.env.local +NEXT_PUBLIC_APP_ID=example +NEXT_PUBLIC_APP_NAME="Example App" NEXT_PUBLIC_GRAPHQL_URL=http://api.localhost/v1/graphql NEXT_PUBLIC_AUTH_URL=http://auth.localhost/v1/auth @@ -252,8 +252,8 @@ The per-app RBAC system allows users to have **different roles in different appl ```sql CREATE TABLE public.apps ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - app_id TEXT UNIQUE NOT NULL, -- 'nchat', 'ntv', 'nfamily' - app_name TEXT NOT NULL, -- 'ɳChat', 'ɳTV', 'ɳFamily' + app_id TEXT UNIQUE NOT NULL, -- 'nchat', 'example', 'nfamily' + app_name TEXT NOT NULL, -- 'ɳChat', 'Example App', 'ɳFamily' app_url TEXT, -- 'https://chat.nself.org' is_active BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT NOW(), @@ -432,7 +432,7 @@ mutation GrantUserRole( When multiple apps share a backend: 1. **User logs in to ɳChat** → Nhost creates session -2. **User visits ɳTV** → Session is valid (same auth service) +2. **User visits Example App** → Session is valid (same auth service) 3. **User visits ɳFamily** → Session is valid (same auth service) All apps use the same: @@ -454,7 +454,7 @@ user.email = "alice@example.com" user.appRoles = ["admin"] user.appContext.permissions = ["channels.delete", "users.ban"] -// In ɳTV (same user): +// In Example App (same user): user.appRoles = ["member"] user.appContext.permissions = ["videos.view", "videos.upload"] @@ -515,7 +515,7 @@ cd backend nself exec postgres psql -U postgres -d nself < migrations/001_create_users.sql nself exec postgres psql -U postgres -d nself < migrations/002_add_per_app_rbac.sql nself exec postgres psql -U postgres -d nself < migrations/003_add_nchat_tables.sql -nself exec postgres psql -U postgres -d nself < migrations/004_add_ntv_tables.sql +nself exec postgres psql -U postgres -d nself < migrations/004_add_example_tables.sql ``` ### Row-Level Security (RLS) @@ -764,7 +764,7 @@ api_limits: ### Step 1: Create Directory Structure ```bash -mkdir -p monorepo/{backend,nchat,ntv,nfamily} +mkdir -p monorepo/{backend,nchat,example,nfamily} cd monorepo ``` @@ -785,7 +785,8 @@ nself init --demo ```bash cd .. git clone https://github.com/nself/nself-chat.git nchat -git clone https://github.com/nself/nself-tv.git ntv +# Set EXAMPLE_APP_REPOSITORY to your own second app repository +git clone "${EXAMPLE_APP_REPOSITORY:?Set your second app repository URL}" example git clone https://github.com/nself/nself-family.git nfamily ``` @@ -799,7 +800,7 @@ nself exec postgres psql -U postgres -d nself < ../nchat/backend/db/migrations/2 # App-specific tables nself exec postgres psql -U postgres -d nself < ../nchat/backend/db/migrations/init_nchat.sql -nself exec postgres psql -U postgres -d nself < ../ntv/backend/db/migrations/init_ntv.sql +nself exec postgres psql -U postgres -d nself < ../example/backend/db/migrations/init_example.sql nself exec postgres psql -U postgres -d nself < ../nfamily/backend/db/migrations/init_nfamily.sql ``` @@ -811,10 +812,10 @@ cd ../nchat/frontend cp .env.example .env.local # Edit NEXT_PUBLIC_APP_ID=nchat -# ɳTV -cd ../../ntv/frontend +# Example App +cd ../../example/frontend cp .env.example .env.local -# Edit NEXT_PUBLIC_APP_ID=ntv +# Edit NEXT_PUBLIC_APP_ID=example # ɳFamily cd ../../nfamily/frontend @@ -833,8 +834,8 @@ nself start cd nchat/frontend pnpm dev -# Terminal 3: ɳTV -cd ntv/frontend +# Terminal 3: Example App +cd example/frontend pnpm dev --port 3001 # Terminal 4: ɳFamily @@ -849,7 +850,7 @@ Visit Hasura console (http://localhost:8080) and run: ```sql INSERT INTO public.apps (app_id, app_name, app_url, is_active) VALUES ('nchat', 'ɳChat', 'http://localhost:3000', true), - ('ntv', 'ɳTV', 'http://localhost:3001', true), + ('example', 'Example App', 'http://localhost:3001', true), ('nfamily', 'ɳFamily', 'http://localhost:3002', true); ``` @@ -859,7 +860,7 @@ INSERT INTO public.apps (app_id, app_name, app_url, is_active) VALUES -- Make first user an owner in all apps INSERT INTO public.app_user_roles (app_id, user_id, role) VALUES ('nchat', 'user-uuid-here', 'owner'), - ('ntv', 'user-uuid-here', 'owner'), + ('example', 'user-uuid-here', 'owner'), ('nfamily', 'user-uuid-here', 'owner'); ``` diff --git a/.github/wiki/SHOWCASE.md b/.github/wiki/SHOWCASE.md index b8e081a35..1fd5b56be 100644 --- a/.github/wiki/SHOWCASE.md +++ b/.github/wiki/SHOWCASE.md @@ -128,7 +128,7 @@ monorepo/ **User Experience:** 1. User logs in to ɳChat → Admin role -2. User visits ɳTV → Member role (different!) +2. User visits Example App → Member role (different!) 3. User visits ɳFamily → Owner role (different again!) 4. **No separate logins** - SSO across all apps diff --git a/.github/wiki/deployment/Monorepo-Setup.md b/.github/wiki/deployment/Monorepo-Setup.md index c94c1d8ed..20408f9bc 100644 --- a/.github/wiki/deployment/Monorepo-Setup.md +++ b/.github/wiki/deployment/Monorepo-Setup.md @@ -8,12 +8,12 @@ ## Overview -This guide explains how to deploy **multiple nSelf applications** (e.g., ɳChat, ɳTV, ɳFamily) that share a single backend, single authentication system, and single user base—while maintaining **per-app role-based access control (RBAC)**. +This guide explains how to deploy **multiple nSelf applications** (e.g., ɳChat, Example App, ɳFamily) that share a single backend, single authentication system, and single user base—while maintaining **per-app role-based access control (RBAC)**. **What You Get:** - 🔐 **One authentication system** - Users log in once across all apps (SSO) - 👥 **One user base** - Single `auth.users` table shared by all apps -- 🎭 **Per-app roles** - User can be admin in ɳChat, regular user in ɳTV +- 🎭 **Per-app roles** - User can be admin in ɳChat, regular user in Example App - 🔌 **Independent frontends** - Each app has its own UI and features - 🗄️ **Shared backend** - One nSelf CLI instance serves all apps @@ -41,7 +41,7 @@ This guide explains how to deploy **multiple nSelf applications** (e.g., ɳChat, ┌─────────────────────────────────────────────────────┐ │ Users' Browsers │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ ɳChat │ │ ɳTV │ │ ɳFamily │ │ +│ │ ɳChat │ │Example App│ │ ɳFamily │ │ │ │ :3000 │ │ :3001 │ │ :3002 │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ └───────┼─────────────┼─────────────┼─────────────────┘ @@ -70,7 +70,7 @@ This guide explains how to deploy **multiple nSelf applications** (e.g., ɳChat, │ │ │ App Tables: │ │ - nchat_* │ - │ - ntv_* │ + │ - example_* │ │ - nfamily_* │ └────────────────┘ ``` @@ -86,7 +86,7 @@ This guide explains how to deploy **multiple nSelf applications** (e.g., ɳChat, **Per-App Components:** - Frontend application (Next.js, React, etc.) -- App-specific tables (e.g., `nchat_channels`, `ntv_videos`) +- App-specific tables (e.g., `nchat_channels`, `example_videos`) - App-specific roles (`app_user_roles` table) - App configuration (`NEXT_PUBLIC_APP_ID`) @@ -142,7 +142,7 @@ nself-monorepo/ │ ├── src/ │ ├── package.json │ └── .env.local -├── ntv/ # ɳTV application +├── example/ # Example application │ └── frontend/ │ ├── src/ │ ├── package.json @@ -161,7 +161,7 @@ nself-monorepo/ ### Step 1: Create Directory Structure ```bash -mkdir -p nself-monorepo/{backend,nchat,ntv,nfamily} +mkdir -p nself-monorepo/{backend,nchat,example,nfamily} cd nself-monorepo ``` @@ -192,7 +192,8 @@ cd .. # Clone apps into monorepo git clone https://github.com/nself/nself-chat.git nchat -git clone https://github.com/nself/nself-tv.git ntv +# Set EXAMPLE_APP_REPOSITORY to your own second app repository +git clone "${EXAMPLE_APP_REPOSITORY:?Set your second app repository URL}" example git clone https://github.com/nself/nself-family.git nfamily ``` @@ -207,7 +208,7 @@ mkdir -p db/migrations # Copy migrations in order cp ../nchat/backend/db/migrations/20260212_add_per_app_rbac.sql db/migrations/001_per_app_rbac.sql cp ../nchat/backend/db/migrations/*.sql db/migrations/002_nchat_tables.sql -cp ../ntv/backend/db/migrations/*.sql db/migrations/003_ntv_tables.sql +cp ../example/backend/db/migrations/*.sql db/migrations/003_example_tables.sql cp ../nfamily/backend/db/migrations/*.sql db/migrations/004_nfamily_tables.sql ``` @@ -264,7 +265,7 @@ Then run: ```sql INSERT INTO public.apps (app_id, app_name, app_url, is_active) VALUES ('nchat', 'ɳChat', 'http://localhost:3000', true), - ('ntv', 'ɳTV', 'http://localhost:3001', true), + ('example', 'Example App', 'http://localhost:3001', true), ('nfamily', 'ɳFamily', 'http://localhost:3002', true); -- Verify @@ -294,17 +295,17 @@ NEXT_PUBLIC_STORAGE_URL=http://storage.localhost/v1/storage NEXT_PUBLIC_ENV=development ``` -**ɳTV (.env.local):** +**Example App (.env.local):** ```bash -cd ../../ntv/frontend +cd ../../example/frontend cp .env.example .env.local ``` Edit `.env.local`: ```bash # App Identity -NEXT_PUBLIC_APP_ID=ntv -NEXT_PUBLIC_APP_NAME=ɳTV +NEXT_PUBLIC_APP_ID=example +NEXT_PUBLIC_APP_NAME="Example App" # Backend URLs (shared - SAME AS NCHAT) NEXT_PUBLIC_GRAPHQL_URL=http://api.localhost/v1/graphql @@ -343,8 +344,8 @@ NEXT_PUBLIC_ENV=development cd ../../nchat/frontend pnpm install -# ɳTV -cd ../../ntv/frontend +# Example App +cd ../../example/frontend pnpm install # ɳFamily @@ -363,9 +364,9 @@ pnpm dev # Runs on http://localhost:3000 ``` -**Terminal 2 - ɳTV:** +**Terminal 2 - Example App:** ```bash -cd ntv/frontend +cd example/frontend pnpm dev --port 3001 # Runs on http://localhost:3001 ``` @@ -399,7 +400,7 @@ SELECT id, email FROM auth.users WHERE email = 'admin@example.com'; -- Assign roles (replace UUID with actual user ID) INSERT INTO public.app_user_roles (app_id, user_id, role) VALUES ('nchat', 'user-uuid-here', 'owner'), - ('ntv', 'user-uuid-here', 'admin'), + ('example', 'user-uuid-here', 'admin'), ('nfamily', 'user-uuid-here', 'member'); -- Verify @@ -420,8 +421,8 @@ The per-app RBAC system allows users to have **different roles in different appl ```sql CREATE TABLE public.apps ( id UUID PRIMARY KEY, - app_id TEXT UNIQUE NOT NULL, -- 'nchat', 'ntv', 'nfamily' - app_name TEXT NOT NULL, -- 'ɳChat', 'ɳTV', 'ɳFamily' + app_id TEXT UNIQUE NOT NULL, -- 'nchat', 'example', 'nfamily' + app_name TEXT NOT NULL, -- 'ɳChat', 'Example App', 'ɳFamily' app_url TEXT, -- 'https://chat.example.com' is_active BOOLEAN DEFAULT true ); @@ -502,9 +503,9 @@ if (hasRole('owner')) { INSERT INTO public.app_user_roles (app_id, user_id, role) VALUES ('nchat', 'user-uuid', 'admin'); --- Grant member role in ɳTV +-- Grant member role in Example App INSERT INTO public.app_user_roles (app_id, user_id, role) -VALUES ('ntv', 'user-uuid', 'member'); +VALUES ('example', 'user-uuid', 'member'); ``` ### Assigning Roles via GraphQL @@ -599,12 +600,12 @@ Grant permissions in Hasura console (http://localhost:8080): ```sql INSERT INTO public.app_user_roles (app_id, user_id, role) VALUES ('nchat', 'alice-uuid', 'admin'), - ('ntv', 'alice-uuid', 'member'); + ('example', 'alice-uuid', 'member'); ``` **Result:** - Alice can create/delete channels in ɳChat -- Alice can only view/post content in ɳTV +- Alice can only view/post content in Example App - Alice logs in once, seamless switch between apps ### Scenario 2: Temporary Moderator Role @@ -691,14 +692,14 @@ GRANT EXECUTE ON FUNCTION public.user_has_app_permission TO hasura; **Symptom:** Migrations fail due to table conflicts. **Solution:** -- Prefix app-specific tables with app ID (e.g., `nchat_channels`, `ntv_videos`) +- Prefix app-specific tables with app ID (e.g., `nchat_channels`, `example_videos`) - Use schema separation: ```sql CREATE SCHEMA nchat; CREATE TABLE nchat.channels (...); - CREATE SCHEMA ntv; - CREATE TABLE ntv.videos (...); + CREATE SCHEMA example; + CREATE TABLE example.videos (...); ``` --- diff --git a/backend/db/migrations/20260212_add_per_app_rbac.sql b/backend/db/migrations/20260212_add_per_app_rbac.sql index 61bb720e6..ed876a8d2 100644 --- a/backend/db/migrations/20260212_add_per_app_rbac.sql +++ b/backend/db/migrations/20260212_add_per_app_rbac.sql @@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS public.apps ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), app_id TEXT UNIQUE NOT NULL, -- e.g., "nchat", "ntv", "nfamily" - app_name TEXT NOT NULL, -- e.g., "ɳChat", "ɳTV", "ɳFamily" + app_name TEXT NOT NULL, -- e.g., "ɳChat", "Example App", "ɳFamily" app_url TEXT, -- e.g., "https://chat.nself.org" is_active BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT NOW(), diff --git a/backend/db/seeds/README.md b/backend/db/seeds/README.md index 7dfa20101..1c7956f66 100644 --- a/backend/db/seeds/README.md +++ b/backend/db/seeds/README.md @@ -174,7 +174,7 @@ These demo users demonstrate: When deployed in a monorepo with multiple apps: - Users log in once across all apps - Same account, different roles per app -- Example: Owner in ɳChat, Member in ɳTV +- Example: Owner in ɳChat, Member in Example App ### 5. **GraphQL Subscriptions** - Real-time message delivery @@ -221,8 +221,8 @@ Note: Do NOT seed these users in production ### Scenario 3: SSO Across Apps (Monorepo Only) 1. Login to ɳChat as `admin@nself.org` -2. Visit ɳTV → Already logged in (SSO) -3. Check role in ɳTV → Might be different (per-app RBAC) +2. Visit Example App → Already logged in (SSO) +3. Check role in Example App → Might be different (per-app RBAC) ## Resetting Seed Data diff --git a/frontend/src/types/app-rbac.ts b/frontend/src/types/app-rbac.ts index c18682722..9da98effe 100644 --- a/frontend/src/types/app-rbac.ts +++ b/frontend/src/types/app-rbac.ts @@ -13,7 +13,7 @@ export interface App { id: string; app_id: string; // e.g., "nchat", "ntv", "nfamily" - app_name: string; // e.g., "ɳChat", "ɳTV", "ɳFamily" + app_name: string; // e.g., "ɳChat", "Example App", "ɳFamily" app_url?: string; // e.g., "https://chat.nself.org" is_active: boolean; created_at: string;