This commit transforms the single-tenant Docusaurus POC into a fully-featured multi-tenant documentation platform with per-client authentication. Key Changes: - Add query parameter-based authentication system using nginx - Implement multi-client architecture with shared/client-specific content - Create build system for managing multiple client instances - Add secure token generation and management - Update PDF service to support multi-tenant requests - Configure production-ready nginx with SSL/HTTPS support - Add rate limiting and security headers - Create deployment scripts for automated builds New Features: - Multiple clients served from subdirectories (e.g., /cral/, /client2/) - Unique auth keys per client stored in environment variables - Shared content structure (~90% shared, 10% client-specific) - Client-specific branding and customization - Automated client creation script - Custom 403 error page for unauthorized access Architecture: - Static builds served by nginx for performance - Symlinks for shared content to avoid duplication - Environment-based configuration for security - Docker Compose setup for production deployment Security: - HTTPS enforcement with Let's Encrypt certificates - Auth-key validation on every request - Rate limiting to prevent abuse - No direct access to backend services Documentation: - Added comprehensive MULTI_TENANT_PLAN.md - Added detailed IMPLEMENTATION_GUIDE.md This implementation provides a scalable, secure foundation for serving multiple clients from a single docs.netdesk.ca domain while maintaining client isolation and customization capabilities. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
NetDesk Multi-Tenant Documentation System
Complete multi-tenant documentation platform with auth-key protection for docs.netdesk.ca.
Quick Start
# 1. Build CRAL client
cd clients/cral
npm install
npm run build
# 2. Deploy build
sudo mkdir -p /var/www/docs/cral
sudo cp -r build/* /var/www/docs/cral/
# 3. Start services
cd ../..
docker-compose up -d
# 4. Access with auth-key
# https://docs.netdesk.ca/cral/?auth-key=a6c2eeef9587fb3c06b8a2b772fd50973a0781983247a78d
Structure
docusaurus-multi-tenant/
├── shared/ # Shared content (90%)
│ ├── docs/ # Shared documentation
│ ├── src/components/ # Shared React components
│ └── src/css/ # Shared CSS
│
├── clients/
│ └── cral/ # CRAL client instance
│ ├── docs/
│ │ ├── intro.md # Client-specific homepage
│ │ └── netdesk-setup.md -> symlink to shared
│ ├── src/ # Symlinks to shared components
│ └── docusaurus.config.ts
│
├── nginx/
│ ├── nginx.conf # Auth + routing config
│ ├── .env # Auth keys (SECRET!)
│ └── 403.html # Access denied page
│
├── pdf-service/ # PDF generation service
│ ├── server.js
│ └── Dockerfile
│
└── scripts/
├── build-all.sh # Build all clients
├── new-client.sh # Create new client
└── generate-token.sh # Generate auth key
Authentication
Auth Keys
Each client has a unique 48-character auth key stored in nginx/.env:
AUTH_KEY_CRAL=a6c2eeef9587fb3c06b8a2b772fd50973a0781983247a78d
AUTH_KEY_CLIENT2=1fba0277e60cec7201ddb756a2d26ed21a99c2b26d1c5d43
IMPORTANT: nginx/.env is in .gitignore and must NEVER be committed to git!
Access URLs
https://docs.netdesk.ca/cral/?auth-key=YOUR_KEY
https://docs.netdesk.ca/client2/?auth-key=YOUR_KEY
Current Auth Keys
CRAL:
https://docs.netdesk.ca/cral/?auth-key=a6c2eeef9587fb3c06b8a2b772fd50973a0781983247a78d
Managing Clients
Build All Clients
./scripts/build-all.sh
Create New Client
./scripts/new-client.sh acme-corp
This will:
- Copy template from CRAL
- Generate unique auth key
- Create client directory
- Provide next steps
Add Client to Nginx
After running new-client.sh, update nginx/nginx.conf:
# Add to map $request_uri $expected_key
~^/acme-corp/ "${AUTH_KEY_ACME_CORP}";
# Add to location regex
location ~ ^/(cral|client2|client3|acme-corp)/ {
And add to nginx/.env:
AUTH_KEY_ACME_CORP=generated_key_here
Shared Content
Adding Shared Documentation
- Create markdown file in
shared/docs/:
vim shared/docs/new-guide.md
- Symlink from each client:
cd clients/cral/docs
ln -s ../../../shared/docs/new-guide.md new-guide.md
- Rebuild all clients:
./scripts/build-all.sh
All clients now have the new documentation!
Client-Specific Content
Create files directly in clients/CLIENT/docs/:
vim clients/cral/docs/cral-specific-process.md
This appears only for CRAL, not other clients.
PDF Generation
PDFs are generated via the PDF service with auth-key pass-through:
https://docs.netdesk.ca/api/pdf/cral/netdesk-setup?auth-key=YOUR_KEY
The PDFDownload component automatically:
- Detects the current client
- Passes through the auth-key
- Generates the correct PDF URL
Production Deployment
Prerequisites
- Domain: docs.netdesk.ca DNS configured
- Server: Ubuntu 22.04 with Docker
- Ports: 80, 443 open
Initial Setup
# 1. Clone repository
git clone <repo> /opt/docusaurus-multi-tenant
cd /opt/docusaurus-multi-tenant
# 2. Set up auth keys
cp nginx/.env.example nginx/.env
vim nginx/.env # Add real auth keys
# 3. Build all clients
./scripts/build-all.sh
# 4. Set up SSL
sudo certbot --nginx -d docs.netdesk.ca
# 5. Start services
docker-compose up -d
# 6. Verify
curl -I "https://docs.netdesk.ca/cral/?auth-key=YOUR_KEY"
SSL Certificate (Let's Encrypt)
# Install certbot
sudo apt install certbot
# Get certificate
sudo certbot certonly --webroot \
-w /var/www/certbot \
-d docs.netdesk.ca
# Auto-renewal (already configured in docker-compose)
docker-compose --profile production up -d certbot
Update Deployment
# 1. Pull latest changes
git pull
# 2. Rebuild clients
./scripts/build-all.sh
# 3. Restart nginx
docker-compose restart nginx
Development
Local Development
# Start a client in dev mode
cd clients/cral
npm start
# Access at http://localhost:3000
# (No auth-key needed in dev mode)
Testing Auth
# Test valid auth
curl -I "http://localhost/cral/?auth-key=a6c2eeef9587fb3c06b8a2b772fd50973a0781983247a78d"
# Should return: 200 OK
# Test invalid auth
curl -I "http://localhost/cral/?auth-key=wrong"
# Should return: 403 Forbidden
# Test missing auth
curl -I "http://localhost/cral/"
# Should return: 403 Forbidden
Maintenance
Rotate Auth Keys (Quarterly)
# 1. Generate new key
./scripts/generate-token.sh
# 2. Update nginx/.env
vim nginx/.env
# 3. Restart nginx
docker-compose restart nginx
# 4. Notify client of new key
# Send via secure channel
Monitor Access
# View nginx logs
docker-compose logs -f nginx
# Count 403 errors
docker-compose logs nginx | grep "403" | wc -l
# Most accessed docs
docker-compose logs nginx | grep "200" | \
awk '{print $7}' | sort | uniq -c | sort -rn | head -10
Backup
# Backup builds
tar -czf backup-$(date +%Y%m%d).tar.gz /var/www/docs/
# Backup source
tar -czf source-$(date +%Y%m%d).tar.gz \
--exclude=node_modules \
--exclude=build \
--exclude=.docusaurus \
.
Troubleshooting
403 Forbidden
Problem: Always getting 403 even with correct auth-key
Solution:
- Check auth-key in URL matches
.envexactly - Verify nginx loaded env vars:
docker-compose logs nginx | grep AUTH - Check nginx config syntax:
docker-compose exec nginx nginx -t - Restart nginx:
docker-compose restart nginx
PDF Generation Fails
Problem: PDF download shows 500 error
Solution:
- Check PDF service logs:
docker-compose logs pdf-service - Verify auth-key is passed: Check URL has
?auth-key=... - Test direct access:
docker-compose exec pdf-service curl http://nginx/cral/?auth-key=KEY - Restart PDF service:
docker-compose restart pdf-service
Build Fails
Problem: npm run build fails
Solution:
- Clear cache:
rm -rf node_modules .docusaurus build - Reinstall:
npm install - Check symlinks:
ls -la docs/ - Rebuild:
npm run build
Security
Best Practices
- ✅ Auth keys are 48 characters (24 bytes hex)
- ✅ Auth keys stored in .env (not in git)
- ✅ HTTPS enforced (HTTP redirects)
- ✅ HSTS header enabled
- ✅ Rate limiting configured
- ✅ 403 page doesn't leak info
- ✅ Each client has unique token
Token Storage
DO:
- ✅ Store in nginx/.env
- ✅ Use password manager
- ✅ Rotate quarterly
- ✅ Send via encrypted channel
DON'T:
- ❌ Commit to git
- ❌ Email in plaintext
- ❌ Share between clients
- ❌ Hardcode in configs
Support
Documentation
- Architecture: See
docs/MULTI_TENANT_PLAN.md - Implementation: See
docs/IMPLEMENTATION_GUIDE.md
Contact
Thompson Hall Digital Inc.
- Email: support@thompsonhall.ca
- System: docs.netdesk.ca
Version: 1.0.0
Last Updated: December 2025