mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 18:51:09 +00:00
3a925b9b0f
- Deleted the `POST /resources/get/{dataFolder?}` and `GET /resources/get-installer` endpoints as part of the architectural shift towards simplified resource management.
- Removed associated methods and configurations, including `ResourcesService.GetEncryptedResource`, `ResourcesService.GetInstaller`, and related properties in `ResourcesConfig`.
- Cleaned up environment variables and configuration files to reflect the removal of installer-related settings.
- Eliminated the `GetResourceRequest` DTO and its validator, along with the `WrongResourceName` error code.
- Updated documentation to clarify the changes in resource handling and the retirement of per-user file encryption.
Co-authored-by: Cursor <cursoragent@cursor.com>
177 lines
12 KiB
Markdown
177 lines
12 KiB
Markdown
# 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 (upload / list / clear).
|
|
- **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).
|
|
|
|
> **Note (cycle 2, 2026-05-14)**: the encrypted resource download (`POST /resources/get/{dataFolder?}`) and both installer endpoints (`GET /resources/get-installer`, `GET /resources/get-installer/stage`) were removed as obsolete. Their orphaned support code went with them: `ResourcesService.GetEncryptedResource` / `GetInstaller`, `Security.GetApiEncryptionKey` / `EncryptTo` / `DecryptTo`, the `GetResourceRequest` DTO (+ `WrongResourceName` error code 50, gap kept), and the `ResourcesConfig.SuiteInstallerFolder` / `SuiteStageInstallerFolder` properties + their env var rows in every config artifact. The `Azaion.Test` unit-test project became empty and was removed from the solution. Per-user file encryption is no longer part of the system; resource delivery is now upload + list + clear only. ADR-003 below is **retired** as a result.
|
|
|
|
**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 (UNIQUE-indexed via `users_email_uidx`), password hash, role, config (legacy `Hardware` column tombstoned per AZ-197). Subset of users have `Role = CompanionPC` and are auto-provisioned via `POST /devices` (AZ-196), which delegates the insert to `UserService.RegisterUser` (post-security-audit consolidation, finding F-3). | 01 Data Layer |
|
|
| UserConfig | JSON-serialized per-user configuration (queue offsets) | 01 Data Layer |
|
|
| RoleEnum | Authorization role hierarchy (None → ApiAdmin); `ResourceUploader` retained as data only after the OTA endpoints were retired | 01 Data Layer |
|
|
| DetectionClass *(AZ-513, cycle 1)* | Operator-managed detection-class catalogue (Name, ShortName, Color, MaxSizeM, PhotoMode?) backing the UI Detection Classes table | 01 Data Layer |
|
|
| ExceptionEnum | Business error code catalog (HW-related codes 40/45 removed by AZ-197) | Common Helpers |
|
|
|
|
> **Removed in cycle 1 / post-cycle-1**: the `Resource` entity, the `resources` table, and the OTA delivery flow (AZ-183 — F10) were reverted after the security audit (finding F-1). The data model no longer carries an OTA-artifact entity.
|
|
|
|
**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 / list / clear (encrypted download + installer delivery were retired in cycle 2)
|
|
|
|
## 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 |
|
|
| Password hashing | SHA-384 | Per-user | Medium |
|
|
| Cache TTL | 4 hours | User entity cache | Low |
|
|
|
|
> The "File encryption / AES-256-CBC" NFR was retired in cycle 2 along with the encrypted-download endpoint. See ADR-003.
|
|
|
|
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
|
|
- General `[Authorize]` — any authenticated user
|
|
|
|
> The `apiUploaderPolicy` was added by AZ-183 and removed in the post-cycle-1 revert along with the OTA endpoints it guarded. `RoleEnum.ResourceUploader` remains as data only.
|
|
|
|
**Data protection**:
|
|
- At rest: resource files are stored as plain bytes on the server filesystem (per-user AES-256-CBC encryption was retired in cycle 2 — see ADR-003).
|
|
- 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 — RETIRED (cycle 2, 2026-05-14)
|
|
|
|
**Original context**: Resources (DLLs, AI models) had to be delivered only to authorized users via a per-download AES-256-CBC stream keyed off the user's email + password.
|
|
|
|
**Retirement decision**: With the OTA delivery flow (AZ-183) and the hardware-binding flow (AZ-197) both gone, the only remaining consumer of the encrypted-download path was a now-vestigial `POST /resources/get/{dataFolder?}` endpoint and the two installer endpoints. None of them are part of the target architecture (browser SaaS + fTPM Jetsons), so the entire encrypt-on-download stack — `POST /resources/get`, `GET /resources/get-installer`, `GET /resources/get-installer/stage`, `ResourcesService.GetEncryptedResource`, `ResourcesService.GetInstaller`, `Security.GetApiEncryptionKey`, `Security.EncryptTo`, `Security.DecryptTo`, `GetResourceRequest`, `WrongResourceName` (50), `ResourcesConfig.SuiteInstallerFolder` / `SuiteStageInstallerFolder` — was removed. `Security.ToHash` is retained because it still backs SHA-384 password hashing in `UserService`.
|
|
|
|
**Consequences**: resource files now live on disk as plain bytes; any future at-rest encryption must come from filesystem or storage-layer features (LUKS, object-store SSE), not from application code.
|
|
|
|
### 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`.
|