# ARM64 single-drive dev box This opt-in configuration uses `k3s-m3` for workloads and `builder-m3` for native ARM64 image builds. It reuses the shared DX commands. Other developers retain the existing dedicated-disk, in-cluster-build defaults. From the repository root, select the configuration explicitly: ```sh make -f dx/arm64-singledrive/Makefile devbox-plan ``` The personal settings are in `k3s-m3.env`. Command-line assignments override them. In Pharos DX, select the same target, SSH destination, delivery runner, existing K3s mode, runner builds, native ARM64 provider images and existing storage. The app saves the reviewed settings in this checkout's private `.env`; it does not change repository-wide defaults. After setup, `make urls` connects the public browser routes. Run `make devbox-browser-setup` once on the workstation to approve the dev-box CA and enable trusted short HTTPS addresses. The CA is verified over SSH first; its private key stays on the dev box. `make dx` opens Pharos DX with this checkout's saved settings. Image builds run on `builder-m3`; application takeover still runs on the workstation. The native Waldur Homeport 8.1.4 build pins its shared `NODE_IMAGE_TAG` argument to the recorded Node 24.21.0 image digest for both build stages. This retains Corepack for its frozen Yarn installation. The patch targets the upgraded Dockerfile and is restricted to the pinned Homeport revision; it participates in the image fingerprint. ## Node capacity This profile sets `K3S_MAX_PODS=150`. Apply it explicitly with `make -f dx/arm64-singledrive/Makefile devbox-capacity`, then restart K3s during a maintenance window and check the node's reported pod capacity. The helper is bound to the reviewed `k3s-m3` node UID and writes a dedicated kubelet drop-in; it refuses other nodes, conflicting overrides and changed configuration files. The shared setup and DX application leave existing kubelet capacity unchanged. The setting permits more pods; CPU, memory and pod-network capacity still apply. The live node and kubelet both report 150 after the reviewed K3s restart. The Fedora profile supplies Istio CNI's `spc_t` SELinux context while leaving host SELinux enforcing. Existing-cluster DNS updates preserve the shared ConfigMap's ownership labels and unrelated entries. Existing-cluster mesh validation covers the delivered service and foundation namespaces. Retained legacy namespaces keep their independent lifecycle; dormant resources there do not block this deployment. Standard provisioned dev boxes retain their cluster-wide mesh check. Both hosts have 36 GB of physical RAM. On `k3s-m3`, the AISC Deployments and the old `waldur-onboarding` Deployments/StatefulSets are parked at zero replicas; Waldur's scheduled jobs are suspended. Their configuration and persistent volumes are retained. AI-DLC remains running. Resuming the parked environments requires a new capacity check alongside the dev-box stack. ## Storage `k3s-m3` already has a 64 GiB Ceph file on its internal drive. The configuration reuses it and preserves the object store used by MLflow. It requires compatible `ceph-block`, `ceph-filesystem` and `ceph-bucket` StorageClasses before setup. It neither reinstalls Rook nor changes the cluster's default StorageClass. The explicit `scripts/prepare_existing_ceph.py` operation extends the reviewed cluster with block/ filesystem services and its HTTPS dashboard. It verifies the existing FSID and leaves the OSD and object store in place. It prints a read-only plan by default; `--apply` enables the existing operator's CSI drivers and creates the missing resources. Its operator ConfigMap settings must be retained during future manual Helm upgrades. For a host with no Ceph installation, `new-storage.env.example` selects `STORAGE_MODE=file` with an empty `STORAGE_DEVICE`. The file is preallocated under `/var/lib/pharos-metallo/storage//ceph.img`; setup requires its full size plus 32 GiB of free host space. Btrfs allocation disables copy-on-write before allocating extents. A target-owned udev rule advertises the stable device path to Rook's inventory; a systemd unit reattaches it before K3s starts. Device-directory permissions remain searchable, while the backing file and binding stay root-owned and private. Existing bindings, sizes and udev rules are immutable; changes require a migration. An interrupted allocation without a complete binding requires inspection. File-backed Ceph is a single-node development option with no drive redundancy. An external drive can provide I/O isolation later; it is optional for this target. ## Onboarding prerequisites With API authentication selected, this profile adds a public-CA-only mount to the Waldur provider actor through an optional Kustomize component. It uses the existing `themelio-api-auth-trust` bundle and normal Node TLS validation. The actor's image, provider credentials, proxy private key and the shared dev-box manifests are unchanged. The [authentication installation](../../gitops/manifests/pharos/api-auth/README.md) requires six public GitLab roots. Inspect them after delivery, then explicitly create any missing roots on this personal target: ```sh make -f dx/arm64-singledrive/Makefile devbox-onboarding-plan make -f dx/arm64-singledrive/Makefile devbox-onboarding-prepare ``` The helper checks the recorded `k3s-m3` node identity and delivered ARM64 profile. It reuses the fixed installation allowlist and GitLab's existing Toolbox: `models`, `datasets`, `spaces`, `repos`, `recipes` and `upskillings`. It checks every existing root's identity, visibility and organisation before creating anything; repeated execution preserves the same group IDs. It neither creates test users nor changes policy acceptance. Existing pending account work retries after these prerequisites are restored. The user must check onboarding status and accept the policy themselves. `dx/common/tests/test_arm64_onboarding.py` checks profile and authentication-mode selection, unchanged shared rendering, the public-only trust projection, target guards, idempotency and refusal of conflicting roots before any creation. On 23 October 2026, reconciliation at `0e7e1624b` delivered this component to `k3s-m3`; all eleven Applications were Synced and Healthy. Both of Bob's existing account-setup records reached `ready` through their normal retries. Read-only identity verification returned 200 for GitLab and Waldur, and all six GitLab personal namespaces had Bob as Owner. A repeated root preparation created no additional groups. Waldur API access passed with the existing trust bundle, and the live HTTP/2 public-origin and management-denial checks still passed. The user subsequently confirmed policy acceptance and dashboard arrival for Bob. Alice also reached the dashboard after a status recheck and policy acceptance. These observations do not qualify the full browser/UAT suite; the returning-user and session-policy regression work is recorded in issue #777. ## Workstation takeover The optional API-authentication component keeps its proxy sidecars in the cluster. DX exports only the replaced workloads' allowlisted credentials and public trust; proxy private keys remain in Kubernetes. Temporary, holder-labelled Services let Telepresence identify the application ports behind those proxies. Hand-back removes these Services and restores the previous injection annotations and delivered images. This profile adds the retained frontend's exact `/_next/webpack-hmr` WebSocket route through the public proxy. Authentication and private routes retain their existing policy. Shared configurations have no development route file, so their behaviour stays unchanged. The browser qualification uses a returning test account whose policy has already been accepted by the user: ```sh uv run --project devops --frozen python dx/common/scripts/qualify_takeover.py \ --target dev-vasilis-m3 --ssh k3s-m3 --runtime native --browser ``` The API-owned browser check never accepts a policy or provider invitation. It checks login, identity, catalogue access, secure cookies, local logout and rejection of a revoked session. AAI-wide logout and back-channel revocation remain separate work in issue #777. ## Qualification status SSH and non-interactive sudo work on both hosts. The disposable native ARM64 BuildKit/OCI export check has passed on `builder-m3`: ```sh ssh builder-m3 python3 < dx/arm64-singledrive/scripts/check_runner.py ``` Native Waldur MasterMind and HomePort 8.1.3 builds and runtime smoke checks passed on `builder-m3`. MasterMind includes the upstream tagged-release metadata and reports `8.1.3`. The actual `k3s-m3` block, CephFS and S3 write/read probe passed; its disposable namespace and claims were removed afterwards. Setup completed on 23 October 2026 at commit `d1c293e87f2361406e7d19392aeb2f93c78fe0fd`. All eleven Argo Applications were Synced and Healthy, Ceph reported `HEALTH_OK`, and the block, CephFS and S3 write/read probes passed. The namespace-scoped mesh check and every configured route probe passed. The public-access lane verified CA trust, operator-route denial, connection reuse/reconnection and prevention of inherited reverse SSH forwards. Pharos, its API, Themelio, GitLab and Waldur responded through the workstation connection with explicit CA verification. System/browser trust still requires the workstation approval described above. At idle after deployment, the host had 89 active pods and used about 62% of memory; memory reservations were about 71%. These are observations, not a peak-load capacity guarantee. AISC and the old standalone Waldur must remain parked for this capacity arrangement. On 24 October 2026, reconciliation delivered `0a3f00cd7` and all eleven Argo Applications were Synced and Healthy. Native takeover qualification passed for all seven services, including repeated preparation, source reloads, both frontends' browser HMR without page reload, connection loss, reconnect and Argo hand-back. The returning-user API-authentication check passed login, identity, catalogue access, secure cookies, local logout and revoked-session replay. The installed Pharos DX app also passed an API takeover, guardian-loss shutdown, and retry after deliberately interrupting hand-back in its journalled restoring phase. The app returned to seven dev-box services with an available lease; temporary application Services and injection annotations were removed. Private evidence is under `.local/metallo-workstation/dev-vasilis-m3/` in `qualification-native.json` and `qualification-desktop.json`. `pnpm verify`, the optional-profile regression tests and all required CI checks passed at that commit. CI included PostgreSQL, broker, sessions/browser and the API-login/GitLab/legacy-policy/private-TLS lanes. Docker takeover, Minikube and the full UAT suite were not run; CPU execution remained deferred with Site Runtime paused. AAI-wide logout and back-channel revocation remain issue #777. Fresh file-backed provisioning passed in an isolated Fedora 44 ARM64 guest on `builder-m3`, with one writable Btrfs system disk and a 64 GiB Ceph file. RBD, CephFS and S3 write/read probes passed. Repeating the storage playbook preserved the cluster FSID, OSD UUID, backing inode, private binding and `local-path` default StorageClass. Changed size, file path and physical-device requests were refused without changing the allocation or binding. After a guest reboot, the systemd unit reattached the same backing file and restored its udev link before K3s started. Ceph returned to `HEALTH_OK`; a new read-only probe retrieved the original RBD, CephFS and S3 tokens without rewriting them. All recorded storage identities and the default StorageClass remained unchanged across the reboot. Private evidence and the guarded fixture are in `.local/qualification-ceph/`. This fixture uses QEMU software emulation, a 4 KiB-page guest kernel and permissive guest SELinux. Ceph command/probe deadlines were extended only in that fixture. The rerun uses DX's pinned Helm 3.19; workstation Helm 4 encountered operator field-ownership conflicts and is outside this qualification. The initial S3 probe exceeded its startup deadline; its retry passed after the gateway and OSD rollout settled. These results establish storage functionality, with no native M3 performance or fresh-installation claim for Asahi's 16 KiB kernel. The live `k3s-m3` Ceph installation and both hosts' enforcing SELinux settings are unchanged. `IMAGE_PROFILE=arm64-singledrive` selects native Waldur 8.1.4 builds from the upstream commits pinned in `images.yaml`. Their digests are passed to this target's Argo Application. Other targets retain the shared upstream image pins. MasterMind includes the [opt-in retained-output UID policy](patches/README.md), disabled until a reviewed Site pool is selected. The retained Kubernetes actor downloads `kubectl` for its build architecture and executes it during the image build. This profile selects version 1.35.9 for K3s 1.36 in both that actor and Metallo; their shared version defaults remain unchanged. Its optional GitLab Helm overlay supplies Debian's allocator from the pinned Toolbox image to Webservice and Sidekiq, and selects it directly in Toolbox and migrations. This accommodates Asahi's 16 KiB memory pages; the standard GitLab values remain unchanged. Existing-cluster transport uses private registry and mesh services. This personal profile forwards public browser aliases through the existing Traefik HTTPS entry point to the TLS host edge; other Traefik routes and Forgejo's NodePort stay in place. Foundation installation, native runner builds, exact-digest publication and K3s image pulls have passed. The old `platform` workloads remain scaled to zero. GitLab advertises LFS transfers through its canonical `gitlab.localtest.me` HTTPS origin. This profile admits Source Control to the exact `kube-system` Traefik pod selector on backend TCP port 8443, which the HTTPS Service translates from 443. Delivery checks the existing Service, pod labels and named backend port before changing its owned routes. Run `make reconcile` to apply the policy through Argo. The GitLab URL, sign-in callback, native clients and edge host rules retain their existing configuration. The profile explicitly sets `edge_http2_coalescing_aliases` to the enabled public aliases for Platform, API, Themelio, GitLab and Waldur. These origins may share HTTP/2 connections covered by the dev-box certificate. Management hosts, longer names and laboratory hosts remain outside the list; adding a catalogue entry does not extend it. Shared dev-box defaults use an empty list and retain strict Host/SNI matching. HTTP/1, certificate validation, raw Host checks and path guards remain unchanged. Cross-host connections cannot reach management paths, including Waldur `/admin/`, even on the operator listener. Matching-SNI operator access keeps its existing CIDR policy. See the [edge decision](../../docs/decisions/0038-platform-edge-catalogue.md#profile-specific-http2-connection-sharing-23-october-2026). This replaces the temporary HTTP/1.1 workaround. Existing browser connections may need a browser restart after the edge reload. Both the shared strict policy and the opt-in exception passed the disposable TLS edge suite on `builder-m3`; CI runs both. Run `scripts/platform_edge_smoke.py` through the frozen `devops` Python environment, first without options and then with `--public-coalescing`. On 23 October 2026, a configuration-only reload on `k3s-m3` restored HTTP/2. Live probes passed all 25 public-origin combinations, management and encoded-path denials, conflicting Host rejection and unchanged AAI access. Argo and Ceph operator access still returned 200; cross-host Waldur administration returned 403 on the operator listener. The CA and leaf certificate were unchanged. The preceding HTTP/1.1 configuration is retained at `/opt/pharos-edge/http2-coalescing-before-de0e216ad.conf`. The explicit handover removed the old `waldur` namespace and its two local-path volumes without backups, and uninstalled the old Istio releases and namespace while retaining their CRDs. The `platform` namespace remains: the retained Forgejo gateway and CPU-qualification runtime still reference its stopped legacy services. Other application environments remain in place, with the additional parked environments listed above. The setup inspector refuses unrecorded conflicting namespaces. The Site Runtime remains paused. On this host its journal claim uses the retained `local-path` default, whose first-consumer binding needs the documented explicit [journal initialisation](../../gitops/manifests/pharos-site-runtime/README.md). The first installation completed that operation with the delivered Metallo image and `/repo/services/metallo/.venv/bin/python`. Keep the journal during subsequent deployments; initialisation is an explicit installation action. `scripts/inspect_replacement.py` gathers a read-only dependency and volume inventory without printing Secret or ConfigMap contents. A matching string is evidence of a possible dependency; it does not establish a complete dependency graph. Namespace deletion and provider replacement remain explicit operator work.