Hub Development
Patterns and conventions for Hub development: backend and frontend.
Architecture Overview
Hub is the orchestration platform for Sigilweaver deployments. It manages:
- Server registration and health monitoring
- User authentication and RBAC
- Workflow storage and execution scheduling
- Audit logging
hub/
├── backend/ # FastAPI + PostgreSQL
│ ├── app/
│ │ ├── api/ # Routes and models
│ │ ├── db/ # SQLAlchemy models + Alembic
│ │ └── domain/ # Business logic
│ └── tests/
├── frontend/ # React + Vite
│ └── src/
│ ├── api/ # API client
│ ├── components/ # Reusable components
│ ├── pages/ # Route pages
│ └── state/ # State management
└── docker-compose.yml # PostgreSQL
Development Setup
Prerequisites
From the repository root:
# Start PostgreSQL
cd hub && docker compose up -d && cd ..
Backend
cd hub/backend
# Install dependencies
uv sync --all-extras
# Run migrations
uv run alembic upgrade head
# Start dev server
uv run uvicorn app.main:app --reload --port 25802
Frontend
cd hub/frontend
# Install dependencies
npm install
# Start dev server
npm run dev
Or use the dev script from the repository root:
./scripts/dev.py --hub
Database Migrations
Hub uses Alembic for database migrations.
cd hub/backend
# Create a new migration
uv run alembic revision --autogenerate -m "Add workflow table"
# Apply all migrations
uv run alembic upgrade head
# Rollback one migration
uv run alembic downgrade -1
# View migration history
uv run alembic history
Important: Always review autogenerated migrations before applying. Alembic sometimes generates unnecessary operations.
Role-Based Access Control
Hub implements hierarchical RBAC:
| Role | Level | Capabilities |
|---|---|---|
| Owner | 0 | Everything. |
| Admin | 1 | Manage users, servers, schedules. |
| Auditor | 2 | Read-only access to audit logs. |
| User | 3 | Execute workflows, view assigned servers. |
# Checking permissions in routes
from app.domain.auth.roles import require_role, Role
@router.post("/servers")
async def register_server(
data: ServerCreate,
user: User = Depends(require_role(Role.ADMIN))
):
# Only admins can register servers
...
Authentication Flow
- Registration: First user automatically becomes Owner
- Login: Returns JWT access token + sets refresh token cookie
- Sessions: Server-side session tracking enables revocation
- Expiration: Configurable session lifetime (default 24 hours)
Zero-Trust Principles
- All requests require authentication (except
/auth/login,/auth/register) - Sessions are database-backed for server-side invalidation
- Data source credentials never transit through Hub; issued as capability tokens
API Patterns
Route Structure
# app/api/routes/servers.py
from fastapi import APIRouter, Depends
from app.api.dependencies import get_current_user
from app.domain.servers import ServerService
router = APIRouter(prefix="/servers", tags=["servers"])
@router.get("")
async def list_servers(
user: User = Depends(get_current_user),
service: ServerService = Depends()
):
return await service.list_for_user(user)
Dependency Injection
# app/api/dependencies.py
async def get_current_user(
token: str = Depends(oauth2_scheme),
session: AsyncSession = Depends(get_db)
) -> User:
# Validate token, return user
...
Testing
Backend
cd hub/backend
# All tests
uv run pytest tests/ -v
# With coverage
uv run pytest tests/ --cov=app --cov-report=html
# Specific test
uv run pytest tests/test_auth.py::test_login -v
Frontend
cd hub/frontend
# Run tests
npm test
# Watch mode
npm run test:watch
Environment Variables
Backend
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL | Yes | - | PostgreSQL connection string |
SECRET_KEY | Yes | - | JWT signing key |
SESSION_LIFETIME_HOURS | No | 24 | Session expiration |
CORS_ORIGINS | No | * | Allowed CORS origins |
Frontend
| Variable | Required | Default | Description |
|---|---|---|---|
VITE_API_URL | No | http://localhost:25802 | Backend API URL |
Common Pitfalls
| Mistake | Consequence | Fix |
|---|---|---|
| Forgetting to run migrations | Tables don't exist | uv run alembic upgrade head |
| Hardcoding role checks | Bypasses RBAC | Use require_role() dependency |
| Sync database calls | Blocks event loop | Use async SQLAlchemy methods |
| Missing CORS configuration | Frontend can't connect | Set CORS_ORIGINS |