# 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":"","password":"","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":"","password":""}` - 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.