Project Overview — ShiftIt
What it is
ShiftIt is a shift planning and employee scheduling platform. Admins create events and shifts, define roles, assign or release shifts for employee self-claiming, and track worked hours with overtime balance management. It also supports building customer-facing event-booking forms that feed directly into the admin workflow.
Initial target: single-tenant deployments for one business. Long-term: multi-tenant SaaS plus licensed self-hosted deployments. Multi-tenancy is built into the schema and runtime today (RLS-enforced) — operators can run single-tenant without changes.
Consuming clients (current and planned):
-
Leptos PWA — primary web frontend (this repo,
frontend/) -
Tauri mobile app — iOS/Android, separate project, same REST API
Repository structure
Cargo workspace with two members and one excluded sub-crate:
| Part | Path | Type | Stack |
|---|---|---|---|
Backend |
|
Rust modular monolith REST API |
Axum 0.8, sqlx 0.8, PostgreSQL 16 (RLS), Zitadel JWTs, utoipa, web-push, aws-sdk-s3 |
Frontend |
|
Rust → WASM SPA + PWA |
Leptos 0.8 (CSR + nightly), Trunk, Tailwind v4, rust-ui components |
License tool |
|
Standalone CLI (not in workspace) |
ed25519-dalek signed |
Architecture at a glance
Browser (Leptos PWA WASM)
└── /api/v1 → nginx → anubis (PoW bot challenge) → Axum backend
├── PostgreSQL (RLS-bound)
├── RustFS (S3-compatible)
└── Zitadel (JWKS only — backend never issues tokens)
Key design decisions (see docs/superpowers/specs/2026-05-08-shiftit-design.md for the full rationale):
-
Axum is a pure resource server. Validates Zitadel-issued JWTs; never issues tokens.
-
Zitadel Organizations = ShiftIt Tenants. SSO federation, customer IdP integration (Entra/Keycloak/Authentik) is Zitadel’s job.
-
PostgreSQL RLS enforces tenant isolation.
app.tenant_idGUC set per transaction;<table>_isolationpolicies filter every read and write. Application code cannot leak across tenants — provably tested. -
Two roles in the DB: the connecting role (privileged, runs migrations) and
shiftit_app(NOLOGIN, RLS-bound, used at runtime viaSET ROLEper-connection). -
No unauthenticated endpoints. Even customer forms require a Zitadel JWT (customers are provisioned with role
customer). The single RLS bypass iscustomer_forms_lookup_by_slugSECURITY DEFINER. -
time_trackingis feature-flagged per tenant.tenant_settings.time_tracking_enabled; routes return 404 when off. -
SaaS vs self-hosted is a compile-time
--features saasflag. SaaS builds skip license validation entirely.
Auth tiers
| Role | Access |
|---|---|
|
Full tenant access |
|
Own assignments, shift claiming, notifications, overtime balance |
|
Assigned forms only — read form definition + submit |
Status (as of 2026-05-10)
| Area | State |
|---|---|
Backend |
80 sqlx tests passing; every public route has |
Frontend |
Nightly Rust + Tailwind v4 + rust-ui components. 3 surfaces: |
Migrations |
0001—0007 applied (tenants/users, roles+locations, events+shifts, time_tracking, forms+documents, form_lookup_function, notifications) |
Deployment |
Distroless backend Dockerfile, nginx-alpine frontend Dockerfile, Compose stack with anubis + Zitadel + RustFS, ed25519 license tool, GitLab CI |
The 9 original implementation plans (docs/superpowers/plans/01..09) are all complete plus a Phase 1 styling refactor and an MVP fill round on top.
Outstanding work is tracked in docs/follow-ups.md. Two P0 items (Zitadel OAuth wiring + slot-picker UI) block real users; the rest are P1/P2.
Where to go next
| To… | Read… |
|---|---|
Run the dev stack |
|
See the full endpoint surface |
|
Understand the data model |
|
Understand the source layout |
|
Understand backend internals |
|
Understand frontend internals |
|
Understand how the two parts talk |
|
See the catalog of UI components |
|
Deploy somewhere |
|
Pick up open work |
|
See the original design rationale |
|