Multi-tenant enterprise application with wildcard subdomain routing.
- Ona Agent guide: AGENTS.md
- Secrets configuration: docs/secrets/README.md
- Frontend: React + TypeScript + Vite
- Backend: Node.js + Express
- Database: Supabase (PostgreSQL with RLS)
- Auth: Supabase Auth
- Infrastructure: Fly.io (production), Ona (staging & test), Docker Compose (development)
- Multi-tenant architecture with subdomain routing (*.tld)
- Row-Level Security (RLS) for tenant data isolation
- Regional dashboards with tenant-specific data
- Admin and counsel management per tenant
- Supabase authentication
# Install dependencies
make install
# Build the application
make build
# Start with docker-compose
make up
# View logs
make logs
# Run health checks
make health
# Stop services
make downSee make help for all available commands.
Run backend and frontend separately:
# Terminal 1 - Backend
cd backend
npm run dev
# Terminal 2 - Frontend
cd frontend
npm run devAccess the app at http://localhost:5173
# Create .env file in root with Supabase credentials
echo "SUPABASE_URL=https://your-project.supabase.co" > .env
echo "SUPABASE_ANON_KEY=your-anon-key" >> .env
# Start services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose downZenbase uses Screwdriver.cd for automated builds and deployments. See SCREWDRIVER.md for detailed documentation.
Quick commands:
# Deploy to development
make deploy-dev
# Deploy to staging
make deploy-staging
# Deploy to production
make deploy-prodUse query parameters to simulate different tenants:
Subdomains automatically route to tenants:
curl -L https://fly.io/install.sh | shfly auth loginfly apps create zenbasefly secrets set SUPABASE_URL=https://your-project.supabase.co
fly secrets set SUPABASE_ANON_KEY=your-anon-key# Add wildcard certificate for *.zenbase.online
fly certs add "*.zenbase.online"
fly certs add "zenbase.online"Configure DNS:
- Add A record:
@→ Fly.io IP - Add A record:
*→ Fly.io IP
fly deploy.
├── backend/
│ ├── src/
│ │ ├── lib/
│ │ │ └── supabase.js # Supabase client with RLS
│ │ ├── middleware/
│ │ │ ├── auth.js # Authentication middleware
│ │ │ └── tenant.js # Tenant context extraction
│ │ ├── routes/
│ │ │ └── tenant.js # Tenant API routes
│ │ └── server.js # Express server
│ ├── package.json
│ └── .env.example
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ ├── hooks/
│ │ │ └── useTenant.ts # Tenant context hook
│ │ ├── lib/
│ │ │ └── supabase.ts # Supabase client
│ │ ├── pages/
│ │ │ ├── Dashboard.tsx # Tenant dashboard
│ │ │ └── Login.tsx # Authentication
│ │ ├── App.tsx
│ │ └── main.tsx
│ ├── package.json
│ └── .env.example
├── docker-compose.yml
├── Dockerfile
├── fly.toml
└── README.md
GET /health- Health check
GET /api/tenant/info- Get tenant informationGET /api/tenant/dashboard- Get dashboard data (requires auth)GET /api/tenant/admins- Get tenant admins (requires auth)
Tenant isolation is enforced at multiple levels:
- Subdomain Routing: Middleware extracts tenant from subdomain
- Database RLS: PostgreSQL Row-Level Security policies
- API Layer: Tenant context validated on every request
- Client Context: Frontend hooks manage tenant state
This project is built on Ona (Gitpod) ephemeral environments, enabling rapid development and testing.
MIT