mirror of
https://github.com/azaion/loader.git
synced 2026-04-22 06:46:32 +00:00
[AZ-182][AZ-184][AZ-187] Batch 1
Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# Jetson device provisioning runbook
|
||||
|
||||
This runbook describes the end-to-end flow to fuse, flash, provision a device identity, and reach a state where the Azaion Loader can authenticate against the admin/resource APIs. It targets a Jetson Orin Nano class device; adapt paths and NVIDIA bundle versions to your manufacturing image.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Provisioning workstation with bash, curl, openssl, python3, and USB/network access to the Jetson in recovery or mass-storage mode as required by your flash tools.
|
||||
- Admin API reachable from the workstation (base URL, for example `https://admin.internal.example.com`).
|
||||
- NVIDIA Jetson Linux Driver Package (L4T) and flash scripts for your SKU (for example `odmfuse.sh`, `flash.sh` from the board support package).
|
||||
- Root filesystem staging directory on the workstation that will be merged into the image before `flash.sh` (often a `Linux_for_Tegra/rootfs/` tree or an extracted sample rootfs overlay).
|
||||
|
||||
## Admin API contract (provisioning)
|
||||
|
||||
The `scripts/provision_device.sh` script expects:
|
||||
|
||||
1. **POST** `{admin_base}/users` with JSON body `{"email":"<string>","password":"<string>","role":"CompanionPC"}`
|
||||
- **201** or **200**: user created.
|
||||
- **409**: user with this email already exists (idempotent re-run).
|
||||
|
||||
2. **PATCH** `{admin_base}/users/password` with JSON body `{"email":"<string>","password":"<string>"}`
|
||||
- Used when POST returns **409** so the password in `device.conf` matches the account after re-provisioning.
|
||||
- **200** or **204**: password updated.
|
||||
|
||||
Adjust URL paths or JSON field names in the script if your deployment uses a different but equivalent contract.
|
||||
|
||||
## Device identity and `device.conf`
|
||||
|
||||
For serial **AZJN-0042**, the script creates email **azaion-jetson-0042@azaion.com** (suffix is the segment after the last hyphen in the serial, lowercased). The password is 32 hexadecimal characters from `openssl rand -hex 16`.
|
||||
|
||||
The script writes:
|
||||
|
||||
`{rootfs_staging}/etc/azaion/device.conf`
|
||||
|
||||
On the flashed device this becomes **`/etc/azaion/device.conf`** with:
|
||||
|
||||
- `AZAION_DEVICE_EMAIL=...`
|
||||
- `AZAION_DEVICE_PASSWORD=...`
|
||||
|
||||
File permissions on the staging file are set to **600**. Ensure your image build preserves ownership and permissions appropriate for the service user that runs the Loader.
|
||||
|
||||
## Step-by-step flow
|
||||
|
||||
### 1. Unbox and record the serial
|
||||
|
||||
Read the manufacturing label or use your factory barcode process. Example serial: `AZJN-0042`.
|
||||
|
||||
### 2. Fuse (if your product requires it)
|
||||
|
||||
Run your approved **fuse** workflow (for example NVIDIA `odmfuse.sh` or internal wrapper). This task does not replace secure boot or fTPM scripts; complete them per your security phase checklist before or after provisioning, according to your process.
|
||||
|
||||
### 3. Prepare the rootfs staging tree
|
||||
|
||||
Extract or sync the rootfs you will flash into a directory on the workstation, for example:
|
||||
|
||||
`/work/images/orin-nano/rootfs-staging/`
|
||||
|
||||
Ensure `etc/` exists or can be created under this tree.
|
||||
|
||||
### 4. Provision the CompanionPC user and embed credentials
|
||||
|
||||
From the Loader repository root (or using an absolute path to the script):
|
||||
|
||||
```bash
|
||||
./scripts/provision_device.sh \
|
||||
--serial AZJN-0042 \
|
||||
--api-url "https://admin.internal.example.com" \
|
||||
--rootfs-dir "/work/images/orin-nano/rootfs-staging"
|
||||
```
|
||||
|
||||
Confirm the script prints success and that `rootfs-staging/etc/azaion/device.conf` exists.
|
||||
|
||||
Re-running the same command for the same serial must not create a duplicate user; the script updates the password via **PATCH** when POST returns **409**.
|
||||
|
||||
If the admin API requires authentication (Bearer token, mTLS), extend the script or shell wrapper to pass the required `curl` headers or use a local proxy; the stock script assumes network-restricted admin access without extra headers.
|
||||
|
||||
### 5. Flash the device
|
||||
|
||||
Run your normal **flash** procedure (for example `flash.sh` or SDK Manager) so the staged rootfs—including `etc/azaion/device.conf`—is written to the device storage.
|
||||
|
||||
### 6. First boot
|
||||
|
||||
Power the Jetson, complete first-boot configuration if any, and verify the Loader service starts. The Loader should read `AZAION_DEVICE_EMAIL` and `AZAION_DEVICE_PASSWORD` from `/etc/azaion/device.conf`, then use them when calling **POST /login** on the Loader HTTP API (which forwards credentials to the configured resource API per your deployment). After a successful login path, the device can request resources and unlock flows as designed.
|
||||
|
||||
### 7. Smoke verification
|
||||
|
||||
- From another host: Loader **GET /health** returns healthy.
|
||||
- **POST /login** on the Loader with the same email and password as in `device.conf` returns success (for example `{"status":"ok"}` in the reference implementation).
|
||||
- Optional: trigger your normal resource or unlock smoke test against a staging API.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Check |
|
||||
|--------|--------|
|
||||
| curl fails to reach admin API | DNS, VPN, firewall, and `API_URL` trailing slash (script strips one trailing slash). |
|
||||
| HTTP 4xx/5xx from POST /users | Admin logs; confirm role value **CompanionPC** and email uniqueness rules. |
|
||||
| 409 then failure on PATCH | Implement or enable **PATCH /users/password** (or change script to match your upsert API). |
|
||||
| Loader cannot log in | `device.conf` path, permissions, and that the password in the file matches the account after the last successful provision. |
|
||||
|
||||
## Security notes
|
||||
|
||||
- Treat `device.conf` as a secret at rest; restrict file permissions and disk encryption per your product policy.
|
||||
- Prefer short-lived credentials or key rotation if the admin API supports it; this runbook describes the baseline manufacturing flow.
|
||||
@@ -1,8 +1,10 @@
|
||||
# Dependencies Table
|
||||
|
||||
**Date**: 2026-04-13
|
||||
**Total Tasks**: 8
|
||||
**Total Complexity Points**: 29
|
||||
**Date**: 2026-04-15
|
||||
**Total Tasks**: 14
|
||||
**Total Complexity Points**: 55
|
||||
|
||||
## Completed Tasks (Blackbox Tests & Refactoring)
|
||||
|
||||
| Task | Name | Complexity | Dependencies | Epic |
|
||||
|------|------|-----------|-------------|------|
|
||||
@@ -15,17 +17,31 @@
|
||||
| 07 | refactor_thread_safety | 3 | None | 01-quality-cleanup |
|
||||
| 08 | refactor_cleanup | 2 | 06 | 01-quality-cleanup |
|
||||
|
||||
## Execution Batches
|
||||
## Active Tasks (Loader Security Modernization)
|
||||
|
||||
| Batch | Tasks | Parallel? | Total Points |
|
||||
|-------|-------|-----------|-------------|
|
||||
| 1 | 01_test_infrastructure | No | 5 |
|
||||
| 2 | 02_test_health_auth | No | 3 |
|
||||
| 3 | 03_test_resources, 04_test_unlock, 05_test_resilience_perf | Yes (parallel) | 13 |
|
||||
| 4 | 06_refactor_crypto_uploads, 07_refactor_thread_safety | Yes (parallel) | 6 |
|
||||
| 5 | 08_refactor_cleanup | No | 2 |
|
||||
| Task | Name | Complexity | Dependencies | Epic |
|
||||
|------|------|-----------|-------------|------|
|
||||
| AZ-182 | tpm_security_provider | 5 | None | AZ-181 |
|
||||
| AZ-183 | resources_table_update_api | 3 | None | AZ-181 |
|
||||
| AZ-184 | resumable_download_manager | 3 | None | AZ-181 |
|
||||
| AZ-185 | update_manager | 5 | AZ-183, AZ-184 | AZ-181 |
|
||||
| AZ-186 | cicd_artifact_publish | 3 | AZ-183 | AZ-181 |
|
||||
| AZ-187 | device_provisioning_script | 2 | None | AZ-181 |
|
||||
|
||||
## Test Scenario Coverage
|
||||
## Execution Batches (AZ-181 Epic)
|
||||
|
||||
| Batch | Tasks | Parallel? | Total Points | Notes |
|
||||
|-------|-------|-----------|-------------|-------|
|
||||
| 1 | AZ-182, AZ-184, AZ-187 | Yes (no dependencies between them) | 10 | AZ-183 excluded: admin API repo |
|
||||
| 2 | AZ-185, AZ-186 | Yes (both depend on batch 1) | 8 | AZ-185 depends on AZ-183 (cross-repo) |
|
||||
|
||||
## Out-of-Repo Tasks
|
||||
|
||||
| Task | Name | Target Repo | Status |
|
||||
|------|------|------------|--------|
|
||||
| AZ-183 | resources_table_update_api | admin/ | To Do — implement in admin API workspace |
|
||||
|
||||
## Test Scenario Coverage (Blackbox Tests - completed)
|
||||
|
||||
| Test Scenario | Task |
|
||||
|--------------|------|
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Code Review Report
|
||||
|
||||
**Batch**: 1 (AZ-182, AZ-184, AZ-187) — loader repo only
|
||||
**Date**: 2026-04-15
|
||||
**Verdict**: PASS_WITH_WARNINGS
|
||||
|
||||
**Note**: AZ-183 (Resources Table & Update API) is scoped to the admin API repository and was excluded from this batch. A mock /get-update endpoint was added to the loader's e2e mock API. See cross-repo notes below.
|
||||
|
||||
## Spec Compliance
|
||||
|
||||
All 16 acceptance criteria across 3 tasks are satisfied with corresponding tests.
|
||||
|
||||
| Task | ACs | Covered | Status |
|
||||
|------|-----|---------|--------|
|
||||
| AZ-182 TPM Security Provider | 6 | 6/6 | All pass (AC-2 skips without swtpm) |
|
||||
| AZ-184 Resumable Download Manager | 5 | 5/5 | All pass (8/8 unittest) |
|
||||
| AZ-187 Device Provisioning Script | 5 | 5/5 | All pass (5/5 pytest) |
|
||||
|
||||
## Findings
|
||||
|
||||
| # | Severity | Category | File:Line | Title |
|
||||
|---|----------|----------|-----------|-------|
|
||||
| 1 | Medium | Style | src/download_manager.py:113 | Union syntax inconsistency |
|
||||
| 2 | Low | Style | tests/test_download_manager.py:9-11 | Redundant sys.path manipulation |
|
||||
| 3 | Low | Scope | AZ-183 | Out-of-repo task excluded |
|
||||
|
||||
### Finding Details
|
||||
|
||||
**F1: Union syntax inconsistency** (Medium / Style)
|
||||
- Location: `src/download_manager.py:113`
|
||||
- Description: Uses `Callable[[], requests.Session] | None` syntax while the rest of the project uses `Optional[...]` (e.g., `main.py` uses `Optional[str]`)
|
||||
- Suggestion: Use `Optional[Callable[[], requests.Session]]` for consistency
|
||||
- Task: AZ-184
|
||||
|
||||
**F2: Redundant sys.path manipulation** (Low / Style)
|
||||
- Location: `tests/test_download_manager.py:9-11`
|
||||
- Description: `sys.path.insert(0, str(SRC))` is redundant — `pytest.ini` already sets `pythonpath = src`
|
||||
- Suggestion: Remove the sys.path block; tests run via pytest which handles the path
|
||||
- Task: AZ-184
|
||||
|
||||
**F3: Out-of-repo task excluded** (Low / Scope)
|
||||
- Location: AZ-183 task spec
|
||||
- Description: AZ-183 (Resources Table & Update API) targets the admin API repository, not the loader. Excluded from this batch.
|
||||
- Suggestion: Implement in the admin API workspace. A mock /get-update endpoint was added to `e2e/mocks/mock_api/app.py` for loader e2e tests.
|
||||
|
||||
## Cross-Task Consistency
|
||||
|
||||
- AZ-182 and AZ-184 both add loader-side capabilities; no interface conflicts
|
||||
- AZ-187 standalone provisioning script has no coupling issues
|
||||
- Mock /get-update endpoint response format (cdnUrl, sha256, encryptionKey) aligns with AZ-184 download manager expectations
|
||||
|
||||
## Cross-Repo Notes (AZ-183)
|
||||
|
||||
AZ-183 requires implementation in the **admin API repository** (`admin/`):
|
||||
- Resources table migration (resource_name, dev_stage, architecture, version, cdn_url, sha256, encryption_key, size_bytes, created_at)
|
||||
- POST /get-update endpoint: accepts device's current versions + architecture + dev_stage, returns only newer resources
|
||||
- Server-side memory cache invalidated on CI/CD publish
|
||||
- Internal endpoint for CI/CD to publish new resource versions
|
||||
- encryption_key column must be encrypted at rest
|
||||
- Response must include encryption_key only over HTTPS with valid JWT
|
||||
Reference in New Issue
Block a user