Reduces the security section to a brief summary without exposing specific middleware names, rate limit thresholds, lockout parameters, or implementation details that could aid threat actors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
239 lines
12 KiB
Markdown
239 lines
12 KiB
Markdown
# UMBRA
|
||
|
||
A self-hosted, multi-user life administration app with a dark-themed UI and role-based access control. Manage your todos, calendar events, projects, reminders, contacts, and locations from a single dashboard.
|
||
|
||
## Features
|
||
|
||
- **Multi-user RBAC** — Admin and standard user roles, per-user data isolation, admin portal with IAM, system config, and audit logs
|
||
- **Dashboard** — Contextual greeting, week timeline, stat cards, upcoming events, weather widget, day briefing
|
||
- **Todos** — Task management with priorities, due dates, recurrence, and grouped sections (overdue/today/upcoming)
|
||
- **Calendar** — Multi-calendar system with month/week/day views, recurring events, drag-and-drop, event templates, shared calendars with permission-based access
|
||
- **Shared Calendars** — Invite connections to calendars with granular permissions (read-only, create/modify, full access). Event locking prevents concurrent edits. Near-real-time sync via 5s polling
|
||
- **Event Invitations** — Invite connections to individual events with RSVP (accept/tentative/decline), per-occurrence status overrides for recurring events, display calendar assignment, and optional edit access via can_modify toggle
|
||
- **Projects** — Project boards with kanban view, nested tasks/subtasks, comments, progress tracking
|
||
- **Reminders** — Time-based reminders with snooze, dismiss, recurrence, and real-time alert notifications (dashboard banner + toasts)
|
||
- **People & Connections** — Contact directory with avatar initials, favourites, birthday tracking. Social connections via umbral name search with bidirectional Person records on accept
|
||
- **Locations** — Location management with OSM search integration, category filtering, frequent locations
|
||
- **Weather** — Dashboard weather widget with temperature, conditions, and contextual rain warnings
|
||
- **Settings** — Accent color picker (8 presets), first day of week, weather city, ntfy push notifications, TOTP two-factor auth, auto-lock, password management
|
||
- **Notifications** — In-app notification centre with toast popups, plus ntfy push notifications for reminders (configurable per-user)
|
||
- **Admin Portal** — User management (create, delete, activate/deactivate, role assignment, password reset), system configuration (open registration, MFA enforcement), audit log viewer
|
||
|
||
## Tech Stack
|
||
|
||
| Layer | Technology |
|
||
|--------------|------------|
|
||
| Frontend | React 18, TypeScript, Vite 6, Tailwind CSS 3 |
|
||
| UI Components | Custom shadcn/ui-style components, FullCalendar 6, Lucide icons, Sonner toasts |
|
||
| Fonts | Sora (headings), DM Sans (body) via Google Fonts |
|
||
| State | TanStack Query v5, React Router v6 |
|
||
| Backend | FastAPI, Python 3.12, Pydantic v2 |
|
||
| Database | PostgreSQL 16, SQLAlchemy 2.0 (async), Alembic (56 migrations) |
|
||
| Auth | Argon2id hashing, DB-backed sessions (signed httpOnly cookies), TOTP MFA, CSRF middleware, role-based access control |
|
||
| Scheduler | APScheduler (async) for ntfy notification dispatch |
|
||
| Deployment | Docker Compose (3 services), Nginx reverse proxy |
|
||
|
||
## Quick Start
|
||
|
||
### Prerequisites
|
||
|
||
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/)
|
||
|
||
### Setup
|
||
|
||
1. **Clone the repository**
|
||
```bash
|
||
git clone https://your-gitea-instance/youruser/umbra.git
|
||
cd umbra
|
||
```
|
||
|
||
2. **Configure environment variables**
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
Edit `.env` and set secure values (see [Production Hardening](#production-hardening) below for generation commands):
|
||
```env
|
||
POSTGRES_USER=umbra
|
||
POSTGRES_PASSWORD=your-secure-password
|
||
POSTGRES_DB=umbra
|
||
DATABASE_URL=postgresql+asyncpg://umbra:your-secure-password@db:5432/umbra
|
||
SECRET_KEY=your-random-secret-key
|
||
OPENWEATHERMAP_API_KEY=your-openweathermap-api-key
|
||
```
|
||
|
||
> **Weather widget**: The dashboard weather widget requires a free [OpenWeatherMap](https://openweathermap.org/api) API key. Set `OPENWEATHERMAP_API_KEY` in `.env`, then configure your city in Settings.
|
||
|
||
3. **Build and run**
|
||
```bash
|
||
docker-compose up --build
|
||
```
|
||
|
||
4. **Open the app**
|
||
|
||
Navigate to `http://localhost` in your browser. On first launch you'll be prompted to create an admin account.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
+-----------+
|
||
| Browser |
|
||
+-----+-----+
|
||
|
|
||
port 80 (HTTP)
|
||
|
|
||
+-------+-------+
|
||
| Nginx |
|
||
| (frontend) |
|
||
| non-root:8080 |
|
||
+---+-------+---+
|
||
| |
|
||
static | | /api/*
|
||
files | | (rate-limited auth)
|
||
v v
|
||
+---+-------+---+
|
||
| FastAPI |
|
||
| (backend) |
|
||
| non-root |
|
||
+-------+-------+
|
||
|
|
||
+-------+-------+
|
||
| PostgreSQL |
|
||
| (db) |
|
||
| port 5432 |
|
||
+---------------+
|
||
```
|
||
|
||
- **Frontend** is built as static files and served by `nginxinc/nginx-unprivileged`. Nginx also reverse-proxies API requests to the backend with rate limiting on auth endpoints.
|
||
- **Backend** runs Alembic migrations on startup as a non-root user (`appuser`), then serves the FastAPI application with `--no-server-header`.
|
||
- **Database** uses a named Docker volume (`postgres_data`) for persistence.
|
||
- **Backend port 8000 is not exposed externally** — only accessible via the internal Docker network.
|
||
|
||
## Security
|
||
|
||
UMBRA is hardened by default with multi-user data isolation, role-based access control, CSRF protection, non-root containers, rate limiting, account lockout, optional TOTP MFA, and secure session management. Multiple penetration tests have been conducted with no exploitable findings.
|
||
|
||
### Production Hardening
|
||
|
||
Before deploying to production, generate secure values for your `.env`:
|
||
|
||
```bash
|
||
# Generate a secure SECRET_KEY (64-char hex string)
|
||
python3 -c "import secrets; print(secrets.token_hex(32))"
|
||
|
||
# Generate a secure database password
|
||
python3 -c "import secrets; print(secrets.token_urlsafe(24))"
|
||
```
|
||
|
||
Additionally for production:
|
||
- Set `ENVIRONMENT=production` — disables API docs and auto-enables HTTPS-only session cookies
|
||
- Place behind a reverse proxy with TLS termination (e.g., Caddy, Traefik, or nginx with Let's Encrypt)
|
||
- Set `CORS_ORIGINS` to your actual domain (e.g., `https://umbra.example.com`)
|
||
|
||
## API Overview
|
||
|
||
All endpoints require authentication (signed session cookie) except auth routes and the health check. Admin endpoints require the `admin` role.
|
||
|
||
| Endpoint | Description |
|
||
|-----------------------|-------------|
|
||
| `GET /health` | Health check |
|
||
| `/api/auth/*` | Login, logout, setup, register, status, password change, TOTP MFA |
|
||
| `/api/admin/*` | User management, system config, audit logs (admin only) |
|
||
| `/api/todos/*` | Todos CRUD + toggle completion |
|
||
| `/api/events/*` | Calendar events CRUD (incl. recurring) + event invitations |
|
||
| `/api/event-invitations/*` | Invitation responses, per-occurrence overrides, can_modify toggle, leave |
|
||
| `/api/event-templates/*` | Event templates CRUD |
|
||
| `/api/calendars/*` | User calendars CRUD + visibility |
|
||
| `/api/shared-calendars/*` | Shared calendar management, invitations, permissions, event locking |
|
||
| `/api/reminders/*` | Reminders CRUD + dismiss + snooze + due alerts |
|
||
| `/api/projects/*` | Projects + nested tasks + comments CRUD |
|
||
| `/api/people/*` | People CRUD |
|
||
| `/api/locations/*` | Locations CRUD + search |
|
||
| `/api/settings/*` | App settings + ntfy config |
|
||
| `/api/dashboard` | Dashboard aggregation |
|
||
| `/api/upcoming` | Unified upcoming items feed |
|
||
| `/api/weather/*` | Weather data proxy |
|
||
| `/api/connections/*` | Social connections (search, request, respond, manage) |
|
||
| `/api/notifications/*` | In-app notifications (list, read, delete) |
|
||
|
||
API documentation is available at `/api/docs` (Swagger UI) when `ENVIRONMENT=development`.
|
||
|
||
## Development
|
||
|
||
### Rebuild a single service
|
||
|
||
```bash
|
||
docker-compose up --build backend # Backend only
|
||
docker-compose up --build frontend # Frontend only
|
||
```
|
||
|
||
### View logs
|
||
|
||
```bash
|
||
docker-compose logs -f # All services
|
||
docker-compose logs -f backend # Backend only
|
||
```
|
||
|
||
### Reset database
|
||
|
||
```bash
|
||
docker-compose down -v && docker-compose up --build
|
||
```
|
||
|
||
### Stop all services
|
||
|
||
```bash
|
||
docker-compose down
|
||
```
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
umbra/
|
||
├── docker-compose.yaml
|
||
├── .env / .env.example
|
||
├── backend/
|
||
│ ├── Dockerfile
|
||
│ ├── requirements.txt
|
||
│ ├── alembic.ini
|
||
│ ├── alembic/versions/ # 56 migrations (001–056)
|
||
│ └── app/
|
||
│ ├── main.py # FastAPI app, CSRF middleware, router registration, health endpoint
|
||
│ ├── config.py # Pydantic BaseSettings (DATABASE_URL, SECRET_KEY, CORS, etc.)
|
||
│ ├── database.py # Async SQLAlchemy engine + session factory
|
||
│ ├── models/ # 20 SQLAlchemy ORM models (incl. User, UserSession, EventInvitation, CalendarMember)
|
||
│ ├── schemas/ # 14 Pydantic v2 request/response schema modules
|
||
│ ├── routers/ # 16 API route handlers (incl. auth, admin, event_invitations, shared_calendars)
|
||
│ ├── services/ # Auth (Argon2id), recurrence, TOTP, ntfy, audit, calendar_sharing, event_invitation, notification
|
||
│ └── jobs/ # APScheduler notification dispatch
|
||
└── frontend/
|
||
├── Dockerfile
|
||
├── nginx.conf
|
||
├── proxy-params.conf # Shared proxy settings (DRY include)
|
||
├── package.json
|
||
└── src/
|
||
├── App.tsx # Routes, ProtectedRoute, AdminRoute auth guards
|
||
├── lib/ # api.ts (axios + 401 interceptor), date-utils.ts, utils.ts
|
||
├── hooks/ # useAuth, useAdmin, useSettings, useTheme, useCalendars, useConfirmAction, useConnections, useNotifications, useEventInvitations
|
||
├── types/ # TypeScript interfaces
|
||
└── components/
|
||
├── ui/ # 18 base components (Button, Dialog, Sheet, Card, Input, Select, Switch, DatePicker, ...)
|
||
├── shared/ # EntityTable, EntityDetailPanel, CategoryFilterBar, CategoryAutocomplete, CopyableField
|
||
├── layout/ # AppLayout, Sidebar, LockOverlay
|
||
├── auth/ # LockScreen, AmbientBackground
|
||
├── admin/ # AdminPortal, IAMPage, ConfigPage, AdminDashboardPage, CreateUserDialog, UserActionsMenu
|
||
├── dashboard/ # DashboardPage + 8 widgets
|
||
├── calendar/ # CalendarPage, CalendarSidebar, EventDetailPanel, InviteeSection, LeaveEventDialog, CalendarForm, EventForm, TemplateForm
|
||
├── todos/ # TodosPage, TodoList, TodoItem, TodoForm, TodoDetailPanel
|
||
├── reminders/ # RemindersPage, ReminderList, ReminderItem, ReminderForm, SnoozeDropdown, AlertBanner
|
||
├── projects/ # ProjectsPage, ProjectCard, ProjectDetail, ProjectForm, KanbanBoard, TaskRow, TaskForm, TaskDetailPanel
|
||
├── people/ # PeoplePage, PersonForm
|
||
├── connections/ # ConnectionSearch, ConnectionRequestCard, ConnectionsTab
|
||
├── notifications/ # NotificationsPage, NotificationToaster
|
||
├── locations/ # LocationsPage, LocationForm
|
||
└── settings/ # SettingsPage, NtfySettingsSection, TotpSetupSection
|
||
```
|
||
|
||
## License
|
||
|
||
This project is for personal use. Feel free to fork and adapt for your own needs.
|