# Azaion Admin API — Architecture ## 1. System Context **Problem being solved**: Azaion Suite requires a centralized admin API to manage users, assign roles, bind hardware to user accounts, and securely distribute encrypted software resources (DLLs, AI models, installers) to authorized devices. **System boundaries**: - **Inside**: User management, authentication (JWT), role-based authorization, file-based resource storage with per-user AES encryption, hardware fingerprint validation. - **Outside**: Client applications (Azaion Suite desktop app, admin web panel at admin.azaion.com), PostgreSQL database, server filesystem for resource storage. **External systems**: | System | Integration Type | Direction | Purpose | |--------|-----------------|-----------|---------| | PostgreSQL | Database (linq2db) | Both | User data persistence | | Server filesystem | File I/O | Both | Resource file storage and retrieval | | Azaion Suite client | REST API | Inbound | Resource download, hardware check, login | | Admin web panel (admin.azaion.com) | REST API | Inbound | User management, resource upload | ## 2. Technology Stack | Layer | Technology | Version | Rationale | |-------|-----------|---------|-----------| | Language | C# | .NET 10.0 | Modern, cross-platform, strong typing | | Framework | ASP.NET Core Minimal API | 10.0 | Lightweight, minimal boilerplate | | Database | PostgreSQL | (server-side) | Open-source, robust relational DB | | ORM | linq2db | 5.4.1 | Lightweight, LINQ-native, no migrations overhead | | Cache | LazyCache (in-memory) | 2.4.0 | Simple async caching for user lookups | | Auth | JWT Bearer | 10.0.3 | Stateless token authentication | | Validation | FluentValidation | 11.3.0 / 11.10.0 | Declarative request validation | | Logging | Serilog | 4.1.0 | Structured logging (console + file) | | API Docs | Swashbuckle (Swagger) | 10.1.4 | OpenAPI specification | | Serialization | Newtonsoft.Json | 13.0.1 | JSON for DB field mapping and responses | | Container | Docker | .NET 10.0 images | Multi-stage build, ARM64 support | | CI/CD | Woodpecker CI | — | Branch-based ARM64 builds | | Registry | docker.azaion.com | — | Private container registry | ## 3. Deployment Model **Environments**: Development (local), Production (Linux server) **Infrastructure**: - Self-hosted Linux server (evidenced by `env/` provisioning scripts for Debian/Ubuntu) - Docker containerization with private registry (`docker.azaion.com`, `localhost:5000`) - No orchestration (single container deployment via `deploy.cmd`) **Environment-specific configuration**: | Config | Development | Production | |--------|-------------|------------| | Database | Local PostgreSQL (port 4312) | Remote PostgreSQL (same custom port) | | Secrets | Environment variables (`ASPNETCORE_*`) | Environment variables | | Logging | Console + file | Console + rolling file (`logs/log.txt`) | | Swagger | Enabled | Disabled | | CORS | Same as prod | `admin.azaion.com` | ## 4. Data Model Overview **Core entities**: | Entity | Description | Owned By Component | |--------|-------------|--------------------| | User | System user with email, password hash, hardware binding, role, config | 01 Data Layer | | UserConfig | JSON-serialized per-user configuration (queue offsets) | 01 Data Layer | | RoleEnum | Authorization role hierarchy (None → ApiAdmin) | 01 Data Layer | | ExceptionEnum | Business error code catalog | Common Helpers | **Key relationships**: - User → RoleEnum: each user has exactly one role - User → UserConfig: optional 1:1 JSON field containing queue offsets **Data flow summary**: - Client → API → UserService → PostgreSQL: user CRUD operations - Client → API → ResourcesService → Filesystem: resource upload/download - Client → API → Security → ResourcesService: encrypted resource retrieval (key derived from user credentials + hardware) ## 5. Integration Points ### Internal Communication | From | To | Protocol | Pattern | Notes | |------|----|----------|---------|-------| | Admin API | User Management | Direct DI call | Request-Response | Scoped service injection | | Admin API | Auth & Security | Direct DI call | Request-Response | Scoped service injection | | Admin API | Resource Management | Direct DI call | Request-Response | Scoped service injection | | User Management | Data Layer | Direct DI call | Request-Response | Singleton DbFactory | | Auth & Security | User Management | Direct DI call | Request-Response | IUserService.GetByEmail | ### External Integrations | External System | Protocol | Auth | Rate Limits | Failure Mode | |----------------|----------|------|-------------|--------------| | PostgreSQL | TCP (Npgsql) | Username/password | None configured | Exception propagation | | Filesystem | OS I/O | OS-level permissions | None | Exception propagation | ## 6. Non-Functional Requirements | Requirement | Target | Measurement | Priority | |------------|--------|-------------|----------| | Max upload size | 200 MB | Kestrel MaxRequestBodySize | High | | File encryption | AES-256-CBC | Per-resource | High | | Password hashing | SHA-384 | Per-user | Medium | | Cache TTL | 4 hours | User entity cache | Low | No explicit availability, latency, throughput, or recovery targets found in the codebase. ## 7. Security Architecture **Authentication**: JWT Bearer tokens (HMAC-SHA256 signed, validated for issuer/audience/lifetime/signing key). **Authorization**: Role-based (RBAC) via ASP.NET Core authorization policies: - `apiAdminPolicy` — requires `ApiAdmin` role - `apiUploaderPolicy` — requires `ResourceUploader` or `ApiAdmin` (defined but never applied to any endpoint) - General `[Authorize]` — any authenticated user **Data protection**: - At rest: Resources encrypted with AES-256-CBC using per-user derived key (email + password + hardware hash) - In transit: HTTPS (assumed, not enforced in code) - Secrets management: Environment variables (`ASPNETCORE_*` prefix) **Audit logging**: No explicit audit trail. Serilog logs business exceptions (WARN) and general events (INFO). ## 8. Key Architectural Decisions ### ADR-001: Minimal API over Controllers **Context**: API has ~17 endpoints with simple request/response patterns. **Decision**: Use ASP.NET Core Minimal API with top-level statements instead of MVC controllers. **Consequences**: All endpoints in a single `Program.cs`. Simple for small APIs but could become unwieldy as endpoints grow. ### ADR-002: Read/Write Database Connection Separation **Context**: Needed different privilege levels for read vs. write operations. **Decision**: `DbFactory` maintains two connection strings — a read-only one (`AzaionDb`) and an admin one (`AzaionDbAdmin`) — with separate `Run` and `RunAdmin` methods. **Consequences**: Write operations are explicitly gated through `RunAdmin`. Prevents accidental writes through the reader connection. Requires maintaining two DB users with different privileges. ### ADR-003: Per-User Resource Encryption **Context**: Resources (DLLs, AI models) must be delivered only to authorized hardware. **Decision**: Resources are encrypted at download time using AES-256-CBC with a key derived from the user's email, password, and hardware hash. The client must know all three to decrypt. **Consequences**: Strong per-user binding. However, encryption happens in memory (MemoryStream), which limits practical file sizes. Key derivation is deterministic — same inputs always produce the same key. ### ADR-004: Hardware Fingerprint Binding **Context**: Resources should only be usable on a specific physical machine. **Decision**: On first resource access, the user's hardware fingerprint string is stored. Subsequent accesses compare the hash of the provided hardware against the stored value. **Consequences**: Ties resources to a single device. Hardware changes require admin intervention to reset. The raw hardware string is stored in the DB; only the hash is compared. ### ADR-005: linq2db over Entity Framework **Context**: Needed a lightweight ORM for PostgreSQL. **Decision**: Use linq2db instead of Entity Framework Core. **Consequences**: No migration framework — schema managed via SQL scripts (`env/db/`). Lighter runtime footprint. Manual mapping configuration in `AzaionDbSchemaHolder`.