Files
admin/_docs/02_document/architecture.md
T
Oleksandr Bezdieniezhnykh 5e90512987 [AZ-197] Remove hardware ID binding from resource flow
Sealed-Jetson + SaaS architecture eliminates the credential-reuse-across-
machines threat that motivated hardware fingerprint binding. The binding's
only remaining effect was a real production failure mode on legitimate
hardware events.

Production:
- Drop PUT /users/hardware/set and POST /resources/check.
- Simplify POST /resources/get/{dataFolder?} (no Hardware field).
- Remove CheckHardwareHash, UpdateHardware, Security.GetHWHash.
- GetApiEncryptionKey signature: (email, password) — no hardwareHash.
- Drop SetHWRequest DTO and Hardware property from GetResourceRequest.
- Remove HardwareIdMismatch (40) and BadHardware (45) ExceptionEnum
  entries; numeric codes left as a gap, not for reuse.

Wire-compat policy: drop entirely (no Loader; no in-flight legacy
clients). Stale callers will see 404s, which is the right loud failure.

Tombstones:
- User.Hardware DB column kept (nullable, unused) — separate cleanup
  ticket for the migration per workspace "no rename without confirmation".
- User.LastLogin is now never written by app code (only writer was inside
  the deleted CheckHardwareHash); flagged in batch_06_review for a future
  ticket.

Tests:
- Delete e2e HardwareBindingTests (165 lines) and Azaion.Test
  UserServiceTest (sole test was CheckHardwareHashTest).
- Drop Hardware payloads + /resources/check preconditions from e2e
  ResourceTests, SecurityTests, ResilienceTests; drop hardwareId arg
  from Azaion.Test SecurityTest.
- Add SecurityTests.Hardware_endpoints_are_removed_AZ_197 (AC-2 regression
  asserting both removed routes return 404).

Docs:
- architecture.md: System Context note, ADR-003 new key formula, ADR-004
  retired with rationale.
- diagrams/flows/flow_hardware_check.md: tombstoned.

Also archives the four batch-1+batch-2 task files into _docs/02_tasks/done/
(file moves were missed by the batch_05 commit).

Code review: PASS — see _docs/03_implementation/reviews/batch_06_review.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 04:46:39 +03:00

9.7 KiB

Azaion Admin API — Architecture

1. System Context

Problem being solved: Azaion Suite requires a centralized admin API to manage users, assign roles, and securely distribute encrypted software resources (DLLs, AI models, installers) to authorized devices and SaaS users.

System boundaries:

  • Inside: User management, authentication (JWT), role-based authorization, file-based resource storage with per-user AES encryption.
  • Outside: Client applications (admin web panel at admin.azaion.com, fTPM-secured Jetson edge devices), PostgreSQL database, server filesystem for resource storage.

Note (AZ-197, 2026-05-13): hardware-fingerprint binding (User.Hardware, CheckHardwareHash, PUT /users/hardware/set, POST /resources/check, HardwareIdMismatch/BadHardware error codes) was removed. Edge devices now ship as fTPM-secured Jetsons; server/desktop access is SaaS-only. The User.Hardware DB column remains as a nullable tombstone (no migration in AZ-197).

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, 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, role, config (legacy Hardware column tombstoned per AZ-197) 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 email + password; hardware-hash component removed in AZ-197)

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). The hardware-hash component was removed in AZ-197 (sealed-Jetson + SaaS architecture).
  • 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 users.

Decision: Resources are encrypted at download time using AES-256-CBC with a key derived from the user's email and password. The client must know both 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.

Update (AZ-197, 2026-05-13): the hardware-hash component of the derivation was removed. The new key formula is SHA384(email + "-" + password + "-#%@AzaionKey@%#---"). See ADR-004 for context on why the hardware binding was retired.

ADR-004: Hardware Fingerprint Binding — RETIRED (AZ-197)

Original context: Resources should only be usable on a specific physical machine.

Original decision: On first resource access, the user's hardware fingerprint string was stored. Subsequent accesses compared the hash of the provided hardware against the stored value.

Retirement decision (2026-05-13, AZ-197): The threat model that motivated this binding (credential reuse across machines via desktop installers) no longer applies:

  • Edge devices ship as fTPM-secured Jetsons (secure boot, fTPM-protected key storage, no user filesystem access, no installer redistribution). Hardware identity is anchored in the fTPM, not in a SHA-384 of CPU/GPU/Memory/DriveSerial strings.
  • Server / desktop access is SaaS-only (browser → admin API). There is no installer to copy and no hardware fingerprint to take.

The binding's only remaining effect was a real production failure mode (HardwareIdMismatch, error code 40) on legitimate hardware events. AZ-197 removed CheckHardwareHash, UpdateHardware, Security.GetHWHash, the PUT /users/hardware/set and POST /resources/check endpoints, and the Hardware field from GetResourceRequest. The User.Hardware DB column is a nullable tombstone (no migration in AZ-197; separate ticket if/when the column is dropped).

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.