Files
2026-04-16 06:25:36 +03:00

8.1 KiB

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.