Focused on concurrency patterns, database optimization, and constraint-driven experiments within a workout tracking domain.
IronTrack is an architectural sandbox built around a realistic domain model.
The workout tracking domain provides sufficient complexity to model real-world interactions (authentication, RBAC, complex relational data), serving as a foundation for honing skills in designing maintainable and extensible systems.
Note
The project is intentionally benchmarked on constrained hardware (AMD FX-8320 | HDD) to evaluate architectural behavior and stability beyond "cloud-ideal" conditions.
Testing under high I/O latency and CPU contention highlights bottlenecks that are typically masked by modern high-performance infrastructure.
Important
This is a learning and exploration project, not a production-driven business application.
Technical solutions (such as PgBouncer, Angie, structured logging) and optimizations (e.g., custom Response via msgspec.json at the FastAPI level) are implemented to study the interaction between the application and the infrastructure layer, and the behavior of the system under load.
- Managed Complexity and Maintainability
Applying Service/Repository patterns to:- Easily extend functionality without a complete code rewrite.
- Reduce cognitive load.
- Ensure Loose Coupling between components.
- Holistic System View
The project intentionally includes key infrastructure components (a reverse proxy, a connection pooler) to move beyond just application code.
The goal is to analyze how different layers interact and influence the application's performance and stability, not to build a high-load system. - Performance & Efficiency Research
Benchmarking on constrained hardware:- Makes it easier to identify and analyze bottlenecks and design load scenarios.
- Forces the writing of resource-efficient code.
- Decision-Making Culture
Structuring knowledge through Architectural Decision Records. This allows one to:- Preserve context: Why a specific solution was chosen.
- Discipline architectural thinking: Every decision must be critically evaluated before being recorded.
Inspiration & Standards
The project conventions, structure, DX workflows, Sphinx documentation generation, and several architectural solutions were borrowed from litestar-org public repositories and adapted for this project.
While the project's foundation is heavily inspired by these practices, the choice of FastAPI was a deliberate decision made to explore its ecosystem and constraints. The detailed rationale is documented in the Tech Stack Rationale | Web Layer Selection.
What's Inside for You
Below is a brief list of implementations that may be useful to the community as examples of solving specific technical tasks.
Important: These implementations do not claim to be universally optimal for every use case. They represent my personal experience in solving tasks within this specific stack and are intended to serve as a starting point for your own projects.
- Scenario: Event Loop blocking during password hashing.
Approach: Offloading CPU-bound Argon2 hashing to a dedicatedThreadPoolExecutor.
Code: src/app/lib/crypt.py - Scenario: Standardizing JWT signatures.
Approach: Implementing Ed25519 (EdDSA) signatures.
Code: src/app/lib/jwt_utils.py - Scenario: Lack of native support for HTTP-only Cookie authorization in standard Swagger UI (OpenAPI) docs.
Approach: CustomJWTCookieSecurityimplementation inheriting fromSecurityBasethat correctly propagates cookie metadata to the OpenAPI schema.
Code: src/app/lib/auth.py - Scenario: Lack of instant JWT-refresh token revocation.
Approach: JTI Blacklisting mechanism integrated with Redis/Valkey.
Code: src/app/domain/users/auth.py and src/app/domain/users/jwt_helpers.py
- Scenario: Network stack overhead during inter-container traffic.
Approach: Orchestration via Unix Domain Sockets (UDS) to bypass the TCP stack overhead in inter-container communication.
Code: docker-compose.yaml - Scenario: JSON serialization bottlenecks (FastAPI jsonable_encoder overhead).
Approach: Response layer based onmsgspecto reduce serialization overhead with automatic CamelCase conversion for frontend compatibility.
Code: src/app/lib/json_response.py and src/app/lib/schema.py
- Scenario: SQLAlchemy conflicts with PgBouncer in
Transaction Poolingmode (prepared statements).
Approach: Fine-tuned Engine configuration (compiled_cache=None,statement_cache_size=0) for full bouncer compatibility.
Code: src/app/config/base.py - Scenario: Password synchronization between PostgreSQL and PgBouncer.
Approach: Makefile script for automateduserlist.txtgeneration directly from PostgreSQL system tables.
Code: Makefile - Scenario: Excessive DB load from redundant permission checks in every request.
Approach: Granular caching ofUserAuthentities in Valkey with automated invalidation.
Code: src/app/domain/users/auth.py and src/app/lib/invalidate_cache.py
- Scenario: Impact of heavy I/O logging on the application's main event loop.
Approach: Non-blocking logging pipeline usingQueueHandlerto offload log writing to a background thread.
Code: src/app/utils/log_utils/setup.py and src/app/utils/log_utils/handlers.py - Scenario: Difficulties in debugging distributed requests and linking logs across different layers.
Approach: Request correlation viaCorrelation ID(Middleware -> Service -> Repository -> DB).
Code: src/app/utils/log_utils/setup.py
- Scenario: Database pollution after tests and slow CI pipelines.
Approach: Transactional Pytest pattern (automatic transaction rollback after each test).
Code: tests/integration/conftest.py - Scenario: Management overhead of multiple commands for server, migrations, and DB ops.
Approach: Unified CLI interface built withTyper, consolidating server commands,advanced-alchemytools, and custom scripts.
Code: src/app/main.py and src/app/utils/server_cli.py - Scenario: Documentation noise from internal ORM fields and unreadable
Annotatedtype signatures.
Approach: Custom Sphinx hooks for automated signature cleaning and SQLAlchemy internal attribute filtering.
Code: docs/conf.py
| Category | Technology |
|---|---|
| Web Server | Angie & Granian |
| Language | Python 3.12+ (Typed, Async) |
| Framework | FastAPI |
| ORM & Repository | Advanced Alchemy |
| Database | PostgreSQL |
| Connection Pooler | PgBouncer |
| Migrations | Alembic |
| Caching | Valkey (Redis-compatible) |
| Logging | structlog |
| Package Manager | uv |
| Configuration | Native Python Dataclasses |
| Documentation | Sphinx (with Shibuya theme) |
| Quality Control | Ruff, Mypy, Pre-commit, Pytest |
View Database Schema
NOTATION: PK: PRIMARY KEY | UUIDv7: UUID version 7 | FK: FOREIGN KEY | UK: UNIQUE | NN: NOT NULL | RULE: Business constraint | SENSITIVE: requires special handling
NOTE: This schema is an iterative draft (MVP).
Focus: Data integrity and history independence (snapshots).
WIP: Deletion logic and edge cases will be refined during implementation.
Quick Jump: Users & Roles | Exercises & Catalog | Templates | History | Access
// --- Relationships ---
role (1) ---- (0..*) user_account : "defines_permissions"
// [x]
role {
UUIDv7 id PK
VARCHAR(100) name UK NN
VARCHAR(255) description NULL
VARCHAR(100) slug UK NN
TIMESTAMPTZ created_at NN DEFAULT CURRENT_TIMESTAMP
TIMESTAMPTZ updated_at NN DEFAULT CURRENT_TIMESTAMP
}
// [x]
user_account {
UUIDv7 id PK
VARCHAR(255) name NULL
VARCHAR(255) email NN
VARCHAR(255) password NN SENSITIVE
BOOLEAN is_active NN DEFAULT true
BOOLEAN is_superuser NN DEFAULT false
UUIDv7 role_id FK role.id ON DELETE RESTRICT
TIMESTAMPTZ created_at NN DEFAULT CURRENT_TIMESTAMP
TIMESTAMPTZ updated_at NN DEFAULT CURRENT_TIMESTAMP
// DB Constraint: ensures email uniqueness regardless of case
RULE uq_user_email UNIQUE (LOWER(email))
}// --- Relationships ---
user_account (0..1) ---- (0..*) exercises : "owns"
equipment (1) ---- (0..*) exercise_equipment : "provides"
exercises (1) ---- (0..*) exercise_equipment : "requires"
muscle_groups (1) ---- (0..*) exercise_primary_muscles : "targets"
exercises (1) ---- (0..*) exercise_primary_muscles : "stresses_primary"
muscle_groups (1) ---- (0..*) exercise_secondary_muscles : "assists"
exercises (1) ---- (0..*) exercise_secondary_muscles : "stresses_secondary"
exercise_tags (1) ---- (0..*) exercise_tag_map : "categorizes"
exercises (1) ---- (0..*) exercise_tag_map : "tagged_with"
// [x]
equipment {
SMALLINT id PK IDENTITY
VARCHAR(100) name UK NN
}
// [x]
muscle_groups {
SMALLINT id PK IDENTITY
VARCHAR(100) name UK NN
}
// [x]
exercise_tags {
SMALLINT id PK IDENTITY
VARCHAR(100) name UK NN
}
// [x]
exercises {
UUIDv7 id PK
VARCHAR(100) name NN
VARCHAR(100) slug UK NULL // Unique when present
ENUM force NULL // pull | push | static
ENUM difficulty_level NN // beginner | intermediate | expert
ENUM mechanic NULL // compound | isolation
ENUM category NN // strength | stretching | cardio | etc.
TEXT instructions NULL
VARCHAR(512) image_path_start NULL
VARCHAR(512) image_path_end NULL
BOOLEAN is_system_default NN DEFAULT false
UUIDv7 created_by FK user_account.id ON DELETE CASCADE NULL
BOOLEAN is_active NN DEFAULT true // Hidden from search if false
TIMESTAMPTZ created_at NN DEFAULT CURRENT_TIMESTAMP
TIMESTAMPTZ updated_at NN DEFAULT CURRENT_TIMESTAMP
// Logical Isolation: separates system defaults from user custom exercises
RULE uq_user_exercise UNIQUE (name, created_by) WHERE created_by IS NOT NULL AND is_active IS TRUE
RULE uq_sys_exerc_slug UNIQUE (slug) WHERE is_system_default IS TRUE
RULE uq_sys_exerc_name UNIQUE (name) WHERE is_system_default IS TRUE
INDEX idx_ex_filters ON (is_system_default, is_active)
/* DESIGN NOTE on idx_ex_filters:
The effectiveness of this index depends on the distribution of data.
It is primarily intended to isolate the global system catalog from
a potentially large volume of user-generated content.
*/
}
// --- Association Tables (M2M Relationships) ---
// [x]
exercise_equipment {
UUIDv7 exercise_id FK PK ON DELETE CASCADE
SMALLINT equipment_id FK PK ON DELETE CASCADE
INDEX idx_ex_equ_id ON (equipment_id)
}
// [x]
exercise_primary_muscles {
UUIDv7 exercise_id FK PK ON DELETE CASCADE
SMALLINT muscle_group_id FK PK ON DELETE CASCADE
INDEX idx_ex_pri_mus_id ON (muscle_group_id)
}
// [x]
exercise_secondary_muscles {
UUIDv7 exercise_id FK PK ON DELETE CASCADE
SMALLINT muscle_group_id FK PK ON DELETE CASCADE
INDEX idx_ex_sec_mus_id ON (muscle_group_id)
}
// [x]
exercise_tag_map {
UUIDv7 exercise_id FK PK ON DELETE CASCADE
SMALLINT tag_id FK PK ON DELETE CASCADE
INDEX idx_ex_tag_id ON (tag_id)
}// --- Relationships ---
user_account (1) ---- (0..*) training_units : "owns"
user_account (1) ---- (0..*) training_plans : "owns"
training_units (1) ---- (0..*) training_unit_exercises : "consists_of"
exercises (0..1) ---- (0..*) training_unit_exercises : "template_for"
training_plans (1) ---- (0..*) training_plan_units : "contains"
training_units (1) ---- (0..*) training_plan_units : "scheduled_as"
// []
training_units {
UUIDv7 id PK
VARCHAR(100) name NN
UUIDv7 created_by FK user_account.id ON DELETE SET NULL
ENUM sharing_status NN DEFAULT 'private' // private | shared | public
TIMESTAMPTZ created_at NN DEFAULT CURRENT_TIMESTAMP
TIMESTAMPTZ updated_at NN DEFAULT CURRENT_TIMESTAMP
TEXT description NULL
UNIQUE (created_by, name)
}
// []
training_plans {
UUIDv7 id PK
VARCHAR(100) name NN
UUIDv7 created_by FK user_account.id ON DELETE SET NULL
ENUM difficulty_level NN // beginner | intermediate | expert
ENUM sharing_status NN DEFAULT 'private' // private | shared | public
TIMESTAMPTZ created_at NN DEFAULT CURRENT_TIMESTAMP
TIMESTAMPTZ updated_at NN DEFAULT CURRENT_TIMESTAMP
TEXT description NULL
UNIQUE (created_by, name)
}
// []
training_unit_exercises {
INTEGER id PK
UUIDv7 training_unit_id FK ON DELETE CASCADE
UUIDv7 exercise_id FK ON DELETE SET NULL
VARCHAR(100) exercise_name_snapshot NN // Copy from exercise for template stability
ENUM mode NN // reps | time | distance | sets_only
SMALLINT order_index NN
TEXT unit_exercise_notes NULL
NUMERIC target_weight NULL
SMALLINT sets NN DEFAULT 1
SMALLINT reps_min NULL
SMALLINT reps_max NULL
SMALLINT target_time_seconds NULL
NUMERIC target_distance_km NULL
SMALLINT rest_seconds NULL // Rest between sets
UNIQUE (training_unit_id, order_index)
// Mode Consistency: ensures required fields for selected training mode
RULE check_mode_consistency CHECK (
(mode = 'reps' AND (reps_min IS NOT NULL OR reps_max IS NOT NULL)) OR
(mode = 'time' AND target_time_seconds IS NOT NULL) OR
(mode = 'distance' AND target_distance_km IS NOT NULL) OR
(mode = 'sets_only' AND
reps_min IS NULL AND
reps_max IS NULL AND
target_time_seconds IS NULL AND
target_distance_km IS NULL)
)
RULE check_sets_count CHECK (sets > 0)
RULE check_reps_range CHECK (reps_min <= reps_max)
}
// []
training_plan_units {
INTEGER id PK
UUIDv7 training_plan_id FK ON DELETE CASCADE
UUIDv7 training_unit_id FK ON DELETE RESTRICT
SMALLINT day_number NN
SMALLINT order_index NN // Sequence in training day
UNIQUE (training_plan_id, day_number, order_index)
/* DESIGN NOTE on day_number:
Gapless day sequence (e.g., 1, 2, 3...) is NOT enforced by the database.
The application layer is responsible for presenting days in order
and managing the user experience around rest days or non-sequential plans.
*/
}// --- Relationships ---
user_account (1) ---- (0..*) training_sessions : "records"
training_sessions (1) ---- (0..*) training_session_exercises : "includes"
exercises (0..1) ---- (0..*) training_session_exercises : "tracks"
training_unit_exercises (0..1) ---- (0..*) training_session_exercises : "fulfills"
training_session_exercises (1) ---- (0..*) training_session_sets : "contains"
// []
training_sessions {
UUIDv7 id PK
UUIDv7 user_id FK user_account.id ON DELETE RESTRICT
UUIDv7 training_plan_id FK ON DELETE SET NULL
UUIDv7 training_unit_id FK ON DELETE SET NULL
/* APP LOGIC REMINDER:
The application MUST correctly handle NULL values for training_plan_id and training_unit_id.
This occurs in two cases:
1. The user performs a workout without a pre-defined plan (a "freestyle" session).
2. The original plan/unit was deleted after the session was completed.
Analytics and history views should not break in these scenarios.
*/
VARCHAR(100) plan_name_snapshot NULL // Plan name at start time
VARCHAR(100) unit_name_snapshot NULL // Name of the workout day (e.g. "Chest/Triceps")
TIMESTAMPTZ start_time NN DEFAULT CURRENT_TIMESTAMP
TIMESTAMPTZ end_time NULL
VARCHAR(50) user_timezone NN // e.g., Europe/Berlin
ENUM status NN DEFAULT 'in_progress' // in_progress | completed | aborted
INTEGER duration_seconds NULL // APP LOGIC: Calculate as (end_time - start_time) on status change to 'completed'
TEXT notes NULL
INDEX idx_ts_user_start ON (user_id, start_time)
}
// []
training_session_exercises {
BIGINT id PK
UUIDv7 training_session_id FK ON DELETE CASCADE
UUIDv7 exercise_id FK ON DELETE SET NULL
INTEGER planned_unit_exercise_id FK training_unit_exercises.id ON DELETE SET NULL // Template reference
ENUM mode_snapshot NN // Copy from template
VARCHAR(100) exercise_name_snapshot NN // Π‘opy from exercise
SMALLINT order_index NN
SMALLINT order_index_snapshot NULL
NUMERIC target_weight_snapshot NULL
SMALLINT sets_snapshot NULL
SMALLINT reps_min_snapshot NULL
SMALLINT reps_max_snapshot NULL
SMALLINT target_time_seconds_snapshot NULL
NUMERIC target_distance_km_snapshot NULL
SMALLINT rest_seconds_snapshot NULL
UNIQUE (training_session_id, order_index)
}
// []
training_session_sets {
BIGINT id PK
BIGINT session_exercise_id FK training_session_exercises.id ON DELETE CASCADE
SMALLINT order_index NN
NUMERIC weight NULL // Optional for all modes
SMALLINT reps_completed NULL
SMALLINT time_seconds NULL
NUMERIC distance_km NULL
UNIQUE (session_exercise_id, order_index)
/* IMPLEMENTATION NOTE (Reordering Logic):
When reordering records (sets) within an exercise, to avoid a UNIQUE constraint
conflict on the order_index field, use temporary negative values or a delete/re-insert logic
within a single transaction.
*/
/* APP LOGIC REMINDER: Mode-Performance Alignment
The database does not enforce a match between the exercise mode (reps/time/dist)
and the set metrics.
The APPLICATION LAYER (Pydantic/API) MUST:
1. Check the 'mode_snapshot' in the parent 'training_session_exercises'.
2. Validate that the user ONLY provides values for the corresponding metric:
- If mode='reps' -> only 'reps_completed' allowed.
- If mode='time' -> only 'time_seconds' allowed.
- If mode='distance' -> only 'distance_km' allowed.
3. Reject the request (422 Unprocessable Entity) if multiple or
mismatched metrics are provided.
*/
// Set Performance Consistency: enforces a single primary metric (reps, time, or distance) per set
RULE check_set_performance CHECK (
(reps_completed >= 0 AND time_seconds IS NULL AND distance_km IS NULL) OR
(time_seconds > 0 AND reps_completed IS NULL AND distance_km IS NULL) OR
(distance_km > 0 AND reps_completed IS NULL AND time_seconds IS NULL) OR
(reps_completed IS NULL AND time_seconds IS NULL AND distance_km IS NULL)
)
/* DESIGN NOTE on check_set_performance:
This constraint intentionally forbids hybrid metrics within a single set
(e.g., completing X reps in Y time). It enforces data purity by forcing
the choice of a single primary metric (reps, time, or distance) per set.
This simplifies analytics. If hybrid metrics are required in the future,
this constraint must be revisited.
*/
}// --- Relationships ---
training_units (1) ---- (0..*) training_unit_access : "governs"
training_plans (1) ---- (0..*) training_plan_access : "governs"
user_account (1) ---- (0..*) training_unit_access : "authorizes (granted_by)"
user_account (1) ---- (0..*) training_plan_access : "authorizes (granted_by)"
user_account (1) ---- (0..*) training_unit_access : "receives (user_id)"
user_account (1) ---- (0..*) training_plan_access : "receives (user_id)"
/* DESIGN NOTE on Access Model:
Access is governed by two complementary systems:
1. The `sharing_status` on `training_units` and `training_plans` handles broad access ('private' vs. 'public').
2. These `*_access` tables manage explicit, granular permissions for specific users when a plan/unit
has a `sharing_status` of 'shared'. The application layer should check both places
to determine if a user can access a resource.
*/
// []
training_unit_access {
UUIDv7 training_unit_id FK PK ON DELETE CASCADE
UUIDv7 user_id FK user_account.id PK ON DELETE CASCADE // The one who gets access
UUIDv7 granted_by FK user_account.id ON DELETE SET NULL // The one who gives access
ENUM access_level NN DEFAULT read // read | write
TIMESTAMPTZ created_at NN DEFAULT CURRENT_TIMESTAMP
// Logical protection: users cannot share plans with themselves
RULE check_not_self_grant CHECK (user_id <> granted_by)
}
// []
training_plan_access {
UUIDv7 training_plan_id FK PK ON DELETE CASCADE
UUIDv7 user_id FK user_account.id PK ON DELETE CASCADE // The one who gets access
UUIDv7 granted_by FK user_account.id ON DELETE SET NULL // The one who gives access
ENUM access_level NN DEFAULT read // read | write
TIMESTAMPTZ created_at NN DEFAULT CURRENT_TIMESTAMP
// Logical protection: users cannot share plans with themselves
RULE check_not_self_grant CHECK (user_id <> granted_by)
}Testing methodology and benchmark specification: SPEC.md.
Optimization Highlights:
- Authentication: Migrated JWT signing from RSA-2048 to Ed25519 (reducing signature verification overhead); offloaded
Argon2idpassword hashing toThreadPoolExecutorto prevent Event Loop blocking. - Access Control: Implemented local JTI caching for Access Tokens, effectively bypassing cryptographic verification for frequently accessed resources.
- Serialization: Replaced standard FastAPI/Pydantic JSON with native
msgspec.jsonfor high-performance output.
For detailed load testing results, hardware configuration, and flame graphs, see: Load Testing Report: Authentication & Serialization.
View Benchmark Results (Baseline vs Final)
For each endpoint, three performance states were tracked: Baseline, Intermediate Optimization, and Final.
Endpoint: `/api/v1/access/signup`
| Metric | Baseline | Final | Delta |
|---|---|---|---|
| Mean Latency | 662.76 ms | 601.42 ms | -9.26% (-61.34 ms) |
| P90 (90%) | 674.30 ms | 654.34 ms | -2.96% (-19.96 ms) |
| CPU Load (Core 0) | 57.55% | 55.46% | -3.63% (-2.09%) |
| CPU Load (Core 1) | 57.66% | 56.51% | -2.00% (-1.15%) |
Analysis: The first optimization phase involved offloading password hashing to a ThreadPoolExecutor. This architectural decision successfully prevented Event Loop blocking, ensuring the parallel processing of light and medium-weight incoming requests even during intensive cryptographic computations. In the final stage, a ~9% reduction in mean latency and a slight decrease in CPU load were observed. Performance gains were achieved by disabling auto_refresh=False at the ORM level and switching to native msgspec.json serialization. However, Argon2id cryptography remains the primary limiting factor. It is worth noting that since this endpoint handles a small data payload, the performance delta between custom implementations and FastAPIβs standard serialization is negligible in this specific context.
The full iterative report, including all intermediate steps, flame graphs, and full metric sets, is available in the linked report.
Endpoint: `/api/v1/access/signin`
| Metric | Baseline | Final | Delta |
|---|---|---|---|
| Mean Latency | 857.39 ms | 520.95 ms | -39.2% (-336.44 ms) |
| P90 (90%) | 1100.00 ms | 676.86 ms | -38.5% (-423.14 ms) |
| CPU Load (Core 0) | 58.76% | 33.31% | -43.3% (-25.45%) |
| CPU Load (Core 1) | 59.04% | 34.18% | -42.1% (-24.86%) |
Analysis: During the first optimization phase, the computationally heavy RSA-2048 token signing algorithm was replaced with Ed25519. This eliminated Event Loop blocking caused by intensive mathematical operations and reduced CPU load by approximately 38β39%. In the final optimization, the data loading strategy was modified by limiting the selection of User model fields and applying the noload(m.User.role) directive. This resulted in a ~39% reduction in mean latency. While the ORM optimization improved average latency by reducing I/O and memory overhead, Argon2id cryptography remains the deterministic factor, consistent with the findings for the /signup endpoint.
The full iterative report, including all intermediate steps, flame graphs, and full metric sets, is available in the linked report.
Endpoint: `/api/v1/access/me`
| Metric | Baseline | Final | Delta |
|---|---|---|---|
| Mean Latency | 3.70 ms | 2.35 ms | -36.5% (-1.35 ms) |
| P90 (90%) | 4.36 ms | 3.01 ms | -31.0% (-1.35 ms) |
| CPU Load (Core 0) | 44.56% | 28.93% | -35.1% (-15.63%) |
| CPU Load (Core 1) | 45.12% | 30.19% | -33.1% (-14.93%) |
Analysis: During the first phase of optimization, profiling revealed that the cryptographic verification of access token signatures using the Ed25519 algorithm incurred significant overhead. The final stage included optimizations at both the transport and ORM layers; however, a substantial performance gain and a ~33β35% reduction in CPU load were achieved through local access token caching. This effectively shifted the workload toward I/O-bound operations and application business logic. The /me endpoint serves as a baseline for assessing the cost of authentication within the system. Since this authentication mechanism is utilized across all protected endpoints, this optimization provides a multiplicative effect, significantly increasing the system's overall throughput and reducing the computational resource requirements per incoming request.
The full iterative report, including all intermediate steps, flame graphs, and full metric sets, is available in the linked report.
βββ src/app/ # Application core and business logic.
βββ dev/adr/ # Architecture Decision Records (design rationale and history).
βββ deploy/ # Infrastructure and deployment setup (PostgreSQL, Angie, etc.).
βββ benchmarks/ # Performance testing suite and results.
βββ docs/ # Sphinx documentation source files.
βββ tests/ # Unit and integration tests.
Quick Access:
| Document | Description |
|---|---|
| Installation Guide | Step-by-step first-time setup. |
| Development Workflow | Hot-reload, Docker volumes, and Makefile. |
| CLI Reference | Application management commands. |
| Changelog | Full history of project changes. |
Ensure you have Docker, Make, and uv installed.
git clone https://github.com/bizoxe/iron-track.git
cd iron-trackInstall dependencies and set up your local configuration files:
# Install dependencies via uv
make install
# Copy the development environment template
cp .env.local.template src/app/config/.envThe application uses the Ed25519 algorithm (EdDSA) for secure token signing. This requires a JWK (JSON Web Key) stored in your environment configuration.
- Generate the Key: Create a new secure key pair using the Makefile shortcut:
make gen-key- Setup:
- Copy the output from the command above.
- Open your
src/app/config/.envfile. - Assign the value to
JWT_PRIVATE_KEYusing single quotes to avoid shell parsing issues:
# src/app/config/.env
JWT_PRIVATE_KEY='{"crv": "Ed25519", "x": "...", "d": "...", "kty": "OKP"}'Note
A single Ed25519 JWK contains both the private and public components required for the EdDSA flow.
# Start PostgreSQL and Valkey
make infra-up
# (Optional) Verify containers are running
docker ps
# Initialize Database & Permissions
app database upgrade --no-prompt
app users create-roles
make seedImportant
Creating roles is mandatory. You must execute app users create-roles before registering any users, as it initializes the base permission system.
app server devTip
Once started, the API will be available at http://127.0.0.1:8000.
Explore the interactive docs at http://127.0.0.1:8000/docs.
Below are the most frequently used commands for managing the project.
βοΈ Makefile Commands (Automation)
make infra-upβ Start PostgreSQL and Valkey in Docker.make infra-downβ Stop and remove infrastructure containers.make seedβ Populate the database with initial data.make pgbouncer-syncβ Synchronize PostgreSQL roles with PgBouncer.
make fixβ Auto-fix linting issues and format code (Ruff).make lintβ Run all quality checks (pre-commit, Mypy).make testβ Execute the test suite with pytest.make installβ Refresh environment and install dependencies via uv.
π» Application Commands (App CLI)
app server devβ Start the FastAPI server with hot-reload enabled.app server runβ Start the server in production mode.
app database upgradeβ Apply migrations to the latest version.app users create-rolesβ Initialize mandatory system roles.
Tip
Use app -h to explore the built-in CLI for advanced database and user management.
Contributing
Contributions are welcome! See the Contributing Guide.
License
Licensed under the MIT License.