Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 26 additions & 25 deletions .github/wiki/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
```

Expand All @@ -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

Expand All @@ -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(),
Expand Down Expand Up @@ -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:
Expand All @@ -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"]

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
```

Expand All @@ -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
```

Expand All @@ -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
```

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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);
```

Expand All @@ -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');
```

Expand Down
2 changes: 1 addition & 1 deletion .github/wiki/SHOWCASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
57 changes: 29 additions & 28 deletions .github/wiki/deployment/Monorepo-Setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼─────────────┼─────────────┼─────────────────┘
Expand Down Expand Up @@ -70,7 +70,7 @@ This guide explains how to deploy **multiple nSelf applications** (e.g., ɳChat,
│ │
│ App Tables: │
│ - nchat_* │
│ - ntv_* │
│ - example_* │
│ - nfamily_* │
└────────────────┘
```
Expand All @@ -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`)

Expand Down Expand Up @@ -142,7 +142,7 @@ nself-monorepo/
│ ├── src/
│ ├── package.json
│ └── .env.local
├── ntv/ # ɳTV application
├── example/ # Example application
│ └── frontend/
│ ├── src/
│ ├── package.json
Expand All @@ -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
```

Expand Down Expand Up @@ -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
```

Expand All @@ -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
```

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
```
Expand Down Expand Up @@ -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
Expand All @@ -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
);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 (...);
```

---
Expand Down
2 changes: 1 addition & 1 deletion backend/db/migrations/20260212_add_per_app_rbac.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Loading
Loading