# Azaion.Loader — System Flows ## Flow Inventory | # | Flow Name | Trigger | Primary Components | Criticality | |---|--------------------|----------------------------|-----------------------------|-------------| | F1| Authentication | POST `/login` | 04 HTTP API, 03 Resource Mgmt | High | | F2| Resource Download | POST `/load/{filename}` | 04, 03, 02 | High | | F3| Resource Upload | POST `/upload/{filename}` | 04, 03, 02 | High | | F4| Docker Unlock | POST `/unlock` | 04, 03 | High | | F5| Unlock Status Poll | GET `/unlock/status` | 04 | Medium | | F6| Health/Status | GET `/health`, `/status` | 04 | Low | ## Flow Dependencies | Flow | Depends On | Shares Data With | |------|--------------------------------|-------------------------------| | F1 | — | F2, F3, F4 (via JWT token) | | F2 | F1 (credentials must be set) | — | | F3 | F1 (credentials must be set) | — | | F4 | — (authenticates internally) | F5 (via unlock_state) | | F5 | F4 (must be started) | — | | F6 | — | F1 (reads auth state) | --- ## Flow F1: Authentication ### Description Client sends email/password to set credentials on the API client singleton. This initializes the CDN manager by downloading and decrypting `cdn.yaml` from the Azaion Resource API. ### Preconditions - Loader service is running - Azaion Resource API is reachable ### Sequence Diagram ```mermaid sequenceDiagram participant Client participant HTTPApi as HTTP API (main) participant ApiClient as ApiClient participant Security as Security participant HW as HardwareService participant ResourceAPI as Azaion Resource API Client->>HTTPApi: POST /login {email, password} HTTPApi->>ApiClient: set_credentials_from_dict(email, password) ApiClient->>ApiClient: set_credentials(Credentials) ApiClient->>ApiClient: login() ApiClient->>ResourceAPI: POST /login {email, password} ResourceAPI-->>ApiClient: {token: "jwt..."} ApiClient->>ApiClient: set_token(jwt) → decode claims → create User ApiClient->>HW: get_hardware_info() HW-->>ApiClient: "CPU: ... GPU: ..." ApiClient->>Security: get_hw_hash(hardware) Security-->>ApiClient: hw_hash ApiClient->>Security: get_api_encryption_key(creds, hw_hash) Security-->>ApiClient: api_key ApiClient->>ResourceAPI: POST /resources/get/ {cdn.yaml, encrypted} ResourceAPI-->>ApiClient: encrypted bytes ApiClient->>Security: decrypt_to(bytes, api_key) Security-->>ApiClient: cdn.yaml content ApiClient->>ApiClient: parse YAML → init CDNManager HTTPApi-->>Client: {"status": "ok"} ``` ### Error Scenarios | Error | Where | Detection | Recovery | |--------------------|--------------------|--------------------|------------------------------| | Invalid credentials| Resource API login | HTTPError (401/409)| Raise Exception → HTTP 401 | | API unreachable | POST /login | ConnectionError | Raise Exception → HTTP 401 | | CDN config decrypt | decrypt_to() | Crypto error | Raise Exception → HTTP 401 | --- ## Flow F2: Resource Download (Big/Small Split) ### Description Client requests a resource by name. The loader downloads the small encrypted part from the API (per-user+hw key), retrieves the big part from local cache or CDN, concatenates them, and decrypts with the shared resource key. ### Preconditions - Credentials set (F1 completed) - Resource exists on API and CDN ### Sequence Diagram ```mermaid sequenceDiagram participant Client participant HTTPApi as HTTP API participant ApiClient as ApiClient participant Security as Security participant ResourceAPI as Azaion Resource API participant CDN as S3 CDN participant FS as Local Filesystem Client->>HTTPApi: POST /load/{filename} {filename, folder} HTTPApi->>ApiClient: load_big_small_resource(name, folder) ApiClient->>ApiClient: load_bytes(name.small, folder) ApiClient->>ResourceAPI: POST /resources/get/{folder} (encrypted) ResourceAPI-->>ApiClient: encrypted small part ApiClient->>Security: decrypt_to(small_bytes, api_key) Security-->>ApiClient: decrypted small part ApiClient->>Security: get_resource_encryption_key() Security-->>ApiClient: shared_key alt Local big part exists ApiClient->>FS: read folder/name.big FS-->>ApiClient: local_big_bytes ApiClient->>Security: decrypt_to(small + local_big, shared_key) Security-->>ApiClient: plaintext resource else Local not found or decrypt fails ApiClient->>CDN: download(folder, name.big) CDN-->>ApiClient: remote_big_bytes ApiClient->>Security: decrypt_to(small + remote_big, shared_key) Security-->>ApiClient: plaintext resource end HTTPApi-->>Client: binary response (octet-stream) ``` ### Error Scenarios | Error | Where | Detection | Recovery | |----------------------|-------------------|-----------------|----------------------------------| | Token expired | request() | 401/403 | Auto re-login, retry once | | CDN download fail | cdn_manager | Exception | Raise to caller → HTTP 500 | | Decrypt failure (local)| Security | Exception | Fall through to CDN download | | API 500 | request() | Status code | Raise Exception → HTTP 500 | --- ## Flow F3: Resource Upload (Big/Small Split) ### Description Client uploads a resource file. The loader encrypts it with the shared resource key, splits into small (≤3KB or 30%) and big parts, uploads small to the API and big to CDN + local cache. ### Preconditions - Credentials set (F1 completed) ### Sequence Diagram ```mermaid sequenceDiagram participant Client participant HTTPApi as HTTP API participant ApiClient as ApiClient participant Security as Security participant ResourceAPI as Azaion Resource API participant CDN as S3 CDN participant FS as Local Filesystem Client->>HTTPApi: POST /upload/{filename} (multipart: file + folder) HTTPApi->>ApiClient: upload_big_small_resource(bytes, name, folder) ApiClient->>Security: get_resource_encryption_key() Security-->>ApiClient: shared_key ApiClient->>Security: encrypt_to(resource, shared_key) Security-->>ApiClient: encrypted_bytes ApiClient->>ApiClient: split: small = min(3KB, 30%), big = rest ApiClient->>CDN: upload(folder, name.big, big_bytes) ApiClient->>FS: write folder/name.big (local cache) ApiClient->>ApiClient: upload_file(name.small, small_bytes, folder) ApiClient->>ResourceAPI: POST /resources/{folder} (multipart) HTTPApi-->>Client: {"status": "ok"} ``` --- ## Flow F4: Docker Image Unlock ### Description Client triggers the unlock workflow with credentials. A background task authenticates, downloads a key fragment, decrypts the encrypted Docker image archive, and loads it into Docker. ### Preconditions - Encrypted archive exists at `IMAGES_PATH` - Docker daemon is accessible (socket mounted) ### Sequence Diagram ```mermaid sequenceDiagram participant Client participant HTTPApi as HTTP API participant BinarySplit as binary_split participant ApiClient as ApiClient participant ResourceAPI as Azaion Resource API participant Docker as Docker CLI Client->>HTTPApi: POST /unlock {email, password} HTTPApi->>HTTPApi: check unlock_state (idle/error?) HTTPApi->>HTTPApi: check IMAGES_PATH exists HTTPApi->>HTTPApi: start background task HTTPApi-->>Client: {"state": "authenticating"} Note over HTTPApi: Background task (_run_unlock) HTTPApi->>BinarySplit: check_images_loaded(version) BinarySplit->>Docker: docker image inspect (×7 services) alt Images already loaded HTTPApi->>HTTPApi: unlock_state = ready else Images not loaded HTTPApi->>ApiClient: set_credentials + login() ApiClient->>ResourceAPI: POST /login ResourceAPI-->>ApiClient: JWT token HTTPApi->>BinarySplit: download_key_fragment(url, token) BinarySplit->>ResourceAPI: GET /binary-split/key-fragment ResourceAPI-->>BinarySplit: key_fragment bytes HTTPApi->>BinarySplit: decrypt_archive(images.enc, key, images.tar) Note over BinarySplit: AES-256-CBC decrypt, strip padding HTTPApi->>BinarySplit: docker_load(images.tar) BinarySplit->>Docker: docker load -i images.tar HTTPApi->>HTTPApi: remove tar, set unlock_state = ready end ``` ### Flowchart ```mermaid flowchart TD Start([POST /unlock]) --> CheckState{State is idle or error?} CheckState -->|No| ReturnCurrent([Return current state]) CheckState -->|Yes| CheckArchive{Archive exists?} CheckArchive -->|No| CheckLoaded{Images already loaded?} CheckLoaded -->|Yes| SetReady([Set ready]) CheckLoaded -->|No| Error404([404: Archive not found]) CheckArchive -->|Yes| StartBG[Start background task] StartBG --> BGCheck{Images already loaded?} BGCheck -->|Yes| BGReady([Set ready]) BGCheck -->|No| Auth[Authenticate + login] Auth --> DownloadKey[Download key fragment] DownloadKey --> Decrypt[Decrypt archive] Decrypt --> DockerLoad[docker load] DockerLoad --> Cleanup[Remove tar] Cleanup --> BGReady ``` ### Error Scenarios | Error | Where | Detection | Recovery | |--------------------|----------------------|----------------------|-----------------------------------| | Archive missing | /unlock endpoint | os.path.exists check | 404 if images not already loaded | | Auth failure | ApiClient.login() | HTTPError | unlock_state = error | | Key download fail | download_key_fragment| HTTPError | unlock_state = error | | Decrypt failure | decrypt_archive | Crypto/IO error | unlock_state = error | | Docker load fail | docker_load | CalledProcessError | unlock_state = error | | Tar cleanup fail | os.remove | OSError | Silently ignored | --- ## Flow F5: Unlock Status Poll ### Description Client polls the unlock workflow progress. Returns current state and any error message. ### Preconditions - F4 has been initiated (or state is idle) ### Data Flow | Step | From | To | Data | Format | |------|--------|--------|-------------------------------|--------| | 1 | Client | HTTPApi| GET /unlock/status | — | | 2 | HTTPApi| Client | {state, error} | JSON | --- ## Flow F6: Health & Status ### Description Liveness probe (`/health`) returns static healthy. Status check (`/status`) returns auth state and model cache dir. ### Data Flow | Step | From | To | Data | Format | |------|--------|--------|----------------------------------------|--------| | 1 | Client | HTTPApi| GET /health or /status | — | | 2 | HTTPApi| Client | {status, authenticated?, modelCacheDir?}| JSON |