disks
Disk inventory and lifecycle (hoserva disk).
List disks
Every block device Hoserva knows about.
List external disks
Disks outside the array: Ignore-role or a later USB disk, never a pool or parity member. Registered external disks plus inventory disks that are not the boot device and not in the array. The Unraid USB stick (a FAT filesystem labelled `UNRAID`) is never offered: it is the migration's rollback. Nothing is mounted by this call.
Register a disk as external
Assigns a non-array, non-boot disk the Ignore/external role with a label used as `/mnt/disks/<label>`. Does not mount or format. The boot device is refused, and so is the Unraid USB stick (`unraid_stick`, 409).
Update an external disk
Sets whether this disk's `/mnt/disks/<label>` mount is a local backup destination. Enabling it on the Unraid USB stick is refused with `unraid_stick` (409).
Mount an external disk
Mounts the disk by filesystem UUID at `/mnt/disks/<label>`. Nothing mounts automatically on plug-in. The boot device and array disks are refused, and so is the Unraid USB stick (`unraid_stick`, 409): it is only ever mounted read-only, by the migration scan.
Eject an external disk
Unmounts `/mnt/disks/<label>`, then spins the disk down.
Format an external disk
Formats the disk after the same typed confirmation array setup uses. The boot device is never offered. A wrong or missing confirmation is refused with `confirmation_required` and formats nothing. The Unraid USB stick is refused with `unraid_stick` (409), whatever the confirmation.
List spin-state wake events
Reads persisted spin-state transitions from the central database only — never probes block devices. Returns every recorded transition plus per-device wake counts grouped by UTC day so the wake-events page can show when each disk woke, how long it stayed awake, and how often it woke.
Create the storage array
Queues a Topology job that formats or adopts the assigned disks. The request is the wizard's role assignments, per-disk filesystem (including adopt/keep), pool options, and the same typed confirmation string array setup uses. A wrong or missing confirmation is refused with `confirmation_required` and formats nothing. A device is a whole, non-boot disk, with one exception: the `cache` role may name a spare partition of the boot disk that `listDisks` reports in that disk's `cachePartitions` — Hoserva formats that blank partition and never writes the boot disk's partition table. A partition of the boot disk assigned to `data` or `parity` is refused with `boot_partition_cache_only`; a partition that is not one of the reported spare partitions is refused with `unmanaged_device`. The Unraid USB stick, which is the user's rollback, is refused in every role with `unraid_stick` (409). Refused with 409 `migration_in_progress` while an Unraid import is pending its point of no return (`startMigrationImport`).
Preview adding a data disk
Computes the add plan: the target disk's own identity (model, WWN or serial, size, its existing filesystem if any), its assigned mountpoint and the exact typed confirmation `addDisk` requires. Refuses a disk that would leave a parity disk smaller than the array's largest data disk, and a device already identified as one of the array's own members by WWN or serial, applying the same validation array setup runs over the resulting data set. Read-only: nothing is formatted or persisted.
Add a data disk to the running array
Queues a Topology job that formats or adopts the disk, then regenerates mount units, the pool and `snapraid.conf` from SQLite. The confirmation must be the exact string the matching `planDiskAdd` call returned; a wrong or missing one is refused with `confirmation_required` and formats nothing.
Preview replacing a data disk
Computes the replace plan: the replacement's own identity (model, WWN or serial, size, its existing filesystem if any), the SnapRAID `fix` command that reconstructs the slot's contents after it is formatted, and the exact typed confirmation `replaceDisk` requires. Refuses (`slot_disk_present`) unless the slot's own recorded disk is genuinely gone — not merely unmounted, but absent from a fresh disk inventory by identity (a healthy disk goes through the upgrade flow instead) — and a replacement that would leave a parity disk smaller than the array's largest data disk. Refuses (`disk_leaving_array`, 409) a slot whose disk is still `evacuating` or already `unlisted`: the replacement would inherit that state. A slot that is `evacuated` or `unpooled` is allowed once the slot's own disk is genuinely missing, refused with `slot_disk_present` otherwise like any other slot — replace abandons the removal and rebuilds the disk's recorded files from parity. Read-only: nothing is formatted or persisted.
Replace a data disk
Queues a Topology job that formats or adopts the replacement at the same mountpoint, regenerates mount units, the pool and `snapraid.conf` from SQLite, confirms the mountpoint is genuinely backed by the replacement before touching parity, then runs `snapraid fix` to reconstruct its contents from parity and the remaining disks. Identity is re-checked at format time and the boot disk is always refused. Refuses (`slot_disk_present`) the same way `planDiskReplace` does when the slot's own disk is still mounted or still present by identity, and (`disk_leaving_array`, 409) a slot still `evacuating` or already `unlisted`. A slot that is `evacuated` or `unpooled` is allowed once the slot's own disk is genuinely missing: the job clears the removal state as part of adopting the replacement, so the disk rejoins the array as an ordinary member and its recorded files rebuild from parity. The confirmation must be the exact string the matching `planDiskReplace` call returned; a wrong or missing one is refused with `confirmation_required` and formats nothing.
Preview upgrading a data or parity disk to a larger one
Computes the upgrade plan for the existing array slot at `mountpoint`, whichever role it holds: the replacement's own identity (model, WWN or serial, size, its existing filesystem if any), the copy/verify/remount steps a data-disk upgrade runs or the copy/verify/switch/check steps a parity-disk upgrade runs, and the exact typed confirmation `upgradeDisk` requires. For a data disk, refuses (`invalid_plan`) a replacement that would leave a parity disk smaller than it — offering the parity upgrade flow first — and any of `planDiskReplace`'s own checks. For a parity disk, `newMountpoint` is the fresh `/mnt/parityN` slot the new disk will be formatted, mounted and verified at independently of the old one — never the old disk's own mountpoint. Refuses (`disk_leaving_array`, 409) a data disk in removal (any `removalState`): the new disk would inherit that state. Read-only: nothing is formatted or persisted.
Upgrade a data or parity disk to a larger one
Starts a resumable Topology job (a data-disk or a parity-disk upgrade, resolved from the slot's role). The confirmation must be the exact string the matching `planDiskUpgrade` call returned; a wrong or missing one is refused with `confirmation_required` and formats nothing. A data disk in removal is refused (`disk_leaving_array`, 409) as `planDiskUpgrade` refuses it. A data-disk upgrade follows a fixed state machine: it is admitted only once `stopArray` has completed (otherwise `array_not_stopped`) and while no other data-disk upgrade is pending (otherwise `disk_upgrade_pending`, naming it). It requires a clean `snapraid diff` before formatting, copies and verifies the old disk, mounts the new one at the same mountpoint, requires `snapraid diff` to show no removed or updated files, and only then names the new disk in SQLite; the array stays stopped until the user starts it. A parity-disk upgrade runs with the array started (refused with `maintenance_mode` while it is stopped): it copies the parity file, verifies it byte for byte, switches the configuration and passes `snapraid check` before releasing the old parity disk. The old disk is never written to or released until its verification gate passes.
Preview evacuating a data disk before removal
Computes the evacuation plan for the data disk at `mountpoint` (mechanically a rebalance targeting one specific source disk): every file an evacuation would move from that disk onto the pool's remaining disks, any path-preserving warnings, and the exact typed confirmation `evacuateDisk` requires. Refused (`invalid_plan`) when a share on this disk has no other branch to evacuate onto, when an entry on the disk is something the evacuation copy path cannot move (a symlink, fifo, socket or device node), when the remaining disks do not have room even after each one's own minimum free space is kept, or when the disk holds any top-level entry that is neither a configured share's own branch there nor SnapRAID's own bookkeeping (`lost+found`, `snapraid.content*`) — there is no procedure for moving such content, so evacuation refuses to start rather than leave it behind unreported; that refusal's own 400 body, `EvacuationPlanRefusal`, names every offending path in `nonSharePaths`. Read-only: nothing is copied, synced or deleted, and this preview does not itself put the disk into its `removing`/no-create state — `evacuateDisk`'s own job does that, before its first copy, so the disk keeps taking new writes only until that job starts, never for as long as it runs. Refused (`disk_leaving_array`, 409) when the disk is already `unpooled` or `unlisted` — only `finishDiskRemoval` takes it further — and (`disk_removal_in_progress`) while a different disk is already in removal. An `evacuating` or `evacuated` disk is planned again, as the source of a resumed or repeated evacuation. No other disk in removal is ever a target. Evacuation moves the disk's own already-present files off it, protected through the threshold guard. The no-create switch is applied by `evacuateDisk`'s job, not by this preview, and the mergerfs branch-list removal, SnapRAID removal and unmount are not performed by either.
Evacuate a data disk before removal
Recomputes the evacuation plan for `mountpoint` (never trusting a client-supplied one, as `startRebalance` does) and, once `confirmation` matches the exact phrase the matching `planDiskEvacuation` call returned, queues a resumable evacuation job. Before copying anything — and again on every resume — the job marks the disk `evacuating` (persisted in SQLite) and applies no-create to its own branch in every pool mount, live; a failure to apply that fails the job before any copy. It then runs the plan unchanged: copy and verify every batch, sync through the threshold guard (each such sync naming this disk in the guard's own zero-files exemption, since the batch that finally empties it would otherwise trip that rule), delete the batch's sources, sync again — then, once the whole plan finishes without being interrupted, a post-check confirms the disk's own share branches hold nothing but empty directories and the job marks the disk `evacuated` before reporting success. A wrong or missing confirmation is refused (`confirmation_required`) before anything runs, a disk already `unpooled` or `unlisted` is refused (`disk_leaving_array`, 409) as `planDiskEvacuation` refuses it, a second disk is refused (`disk_removal_in_progress`) while one is already in removal, and any new evacuation is refused (`evacuation_pending`) while another evacuation job is queued, running or interrupted. The removal state belongs to the job that set it: cancelling that job — queued, running or interrupted — clears it and puts the disk back to taking writes, and cancelling any other job never does. A job that fails leaves the disk `evacuating`; evacuating it again takes the state over, and cancelling that run clears it. Success here means the disk's data as this job saw it is safely off it and it is no longer taking new writes, not that it is empty of every file or safe to physically remove: the mergerfs branch-list removal, SnapRAID removal and unmount are `finishDiskRemoval`'s.
Finish removing an evacuated data disk
Queues a Topology job that takes an evacuated data disk out of the array. The confirmation is the same `REMOVE <mountpoint>` phrase `planDiskEvacuation` returned for the disk. Refused synchronously with `disk_slot_not_found` when no data disk occupies `mountpoint`, `disk_not_evacuated` when its removal state is not `evacuated`, `unpooled` or `unlisted`, and `confirmation_required` for a wrong or missing confirmation; `not_configured` when the daemon has no parity engine. Before changing anything the job checks all of that again, that the array without the disk still has a data disk and room for every content-file copy, that the disk is mounted by its own filesystem, and that nothing but empty directories and SnapRAID's own content files is left anywhere on it. It then marks the disk `unpooled` and takes it out of every pool mount, live (a failed live update fails the job); removes the empty directories the evacuation left, since SnapRAID records those too (rmdir only); runs a sync through the threshold guard with only this disk exempt from the zero-files rule, while its data line is still in snapraid.conf, and confirms SnapRAID tracks no file on it; marks it `unlisted`, regenerates snapraid.conf without it and checks SnapRAID accepts the result; then stops its mount unit, removes the unit file and deletes the disk from the array. The job's result names the disk as safe to physically remove; its filesystem is never wiped. A job that fails or is interrupted leaves the disk in the last state it reached, and running this operation again carries on from there — once `unlisted`, it never syncs again. A tripped guard leaves the disk `unpooled`, still listed and mounted, with nothing synced.
Cancel a data disk's removal and return it to normal use
Clears `mountpoint`'s removal state synchronously — no job — and re-applies the pool live so the disk's branches go back to RW. Refused with `disk_slot_not_found` (404) when no data disk occupies `mountpoint`. Refused with `disk_removal_not_cancellable` (409) when the disk is not currently in removal; when it is `unpooled` or `unlisted` — it has already left the pool, so the only way forward is `finishDiskRemoval`, never back; or when it is `evacuating` and its evacuation job is still queued, running or interrupted — that job owns the state, so cancel it (`cancelJob`) instead. An `evacuating` disk whose evacuation job has already ended (no longer queued, running or interrupted — including a job the store no longer has a record of) is cancelled the same as an `evacuated` one: the copy it ran already stopped, and cancelling here is how the disk gets back to ordinary use instead of sitting stuck until it is evacuated again. A live-update failure while re-applying the pool is an internal error, and the removal state is left cleared — the same as the disk-topology jobs' own live-apply failures — rather than silently reporting success with the disk still no-create.