# Command-Line Reference The `yatm-cli` executable is the scripted and agent-facing surface. It calls the same [gRPC and HTTP API](../architecture/api.md) the browser does, so a CLI request carries no separate server semantics; [Interface contracts](../architecture/contracts.md) owns the rules shared with those surfaces. ## Agents and Connection The [bundled Skill](../../.agents/skills/yatm/SKILL.md) routes agent work to command domains and defines operating rules. It ships inside the release package beside the [operations guides](library.md) that own the workflows. Agents read the matching installed `--help` down to the selected command for its inventory, arguments and flags, and interpret its JSON output as the operational source of truth. The Skill's domain table does not duplicate the installed command inventory; this document owns the surface rules. Connection settings are global flags or their `YATM_*` environment variables: `--server`, `--timeout` (`0` removes the deadline), `--basic-user` and `--basic-password-file`. Registered Location paths, Volume mount points and Tape devices are paths on the server; `--input`, `--output` and `--basic-password-file` are paths on the machine running the agent. Unary replies print protobuf JSON, and streaming operations print JSON Lines with one item result per line and a final summary. A Files listing is the one stream with no summary: it writes one message per batch as the server produces it, so a reader sees the first rows before the read finishes. Exit status distinguishes intent from execution: `0` succeeded, `1` failed at runtime, and `2` rejected the command as a usage or safety error. An executed stream that settles with failed, unprocessed or publication-pending items exits nonzero even when the request itself completed. A completed `--dryrun` plan succeeds even though its proposed entries remain `UNPROCESSED`; planning failures and pending Library publication still exit nonzero. A read that returns a complete directory takes no page size and no cursor; those flags belong to the query form of the same command, which returns one bounded page and the cursor that continues it. Passing either for a complete read is a usage error rather than a silently ignored argument. Long work belongs to a Job, and a waiting command always has a timeout. Physical decisions such as choosing a device, loading Media, formatting, or continuing writes stay explicit rather than implicit in another command. There is no file or Preview download command; full archived content is obtained through Restore. The CLI has no command for an operation the API does not expose. Instant fields in JSON use decimal strings and names ending in `_ns`, following the [shared time contract](../architecture/contracts.md#time-values). Preserve them as signed 64-bit integers when processing output. Restore `--before` accepts a timestamp with its stated timezone and retains its nanosecond precision; dates outside the supported range are usage errors. `job wait` succeeds for a durable `COMPLETED` Job whose phase is `COMPLETED` or `UNSPECIFIED`, including after a service restart. It keeps waiting while an attached runner reports `QUEUED` for an admitted attempt waiting for shared Media resources, or a finalizing phase. An idle `READY` Job reports `UNSPECIFIED` and requires an explicit Media choice; the [Job lifecycle](../architecture/jobs.md#shared-lifecycle) defines which failures return a Job to `READY` and which are terminal. Waiting returns nonzero with `action_required` for `FAILED` or idle `READY`, including the persisted error when present. `archive creation ID`, `restore creation ID` and `scan creation ID` call their kind's read-only `GetCreation` method once and print its typed response. `request` contains the retained creation inputs; a nonempty `unavailable_reason` explains missing original inputs that require reselection. Reading does not create a Job or change the old Job. Review the selections and options, resolve missing inputs, and explicitly use the normal Create flow with its current validation and pre-submit checks to submit new work. `identical groups` and `identical members` include a disposable `result_id` in their first reply. Later pages require `--result ID` with the returned cursor; an unavailable ID fails and requires a new Find instead of silently repeating the complete search. `identical find` returns the same kind of ID and both row counts for `identical rows --result ID --offset N`; `identical positions` locates File IDs within both hidden-file projections, and `identical close` releases the result. An idle result expires after 30 minutes and does not survive a service restart; run Find again if it is unavailable. Row pages default to 100 and accept at most 200 rows; group and member CLI pages default to 20. `identical rows` and `identical positions` accept the same `--sort-key file-id|name|size` and `--order asc|desc`. The default is File ID ascending; name starts ascending and size starts descending when selected. Row pages and position lookups use the same requested order under the [Library sorting contract](../architecture/library.md#identical-file-tools). These reads never scan or hash physical files. `job log` reads a raw bounded byte page and returns JSON with text in `logs` and the next byte offset as a decimal string in `offset`. Follow that returned offset, not the text's character count; an empty `logs` ends the read. This CLI representation does not Base64-encode the log, although the underlying RPC uses a protobuf bytes field. JSON text can replace invalid UTF-8, including a character split by a byte-page boundary; preserve the original Job bundle's `job.log` when exact bytes are required. `job log-lines` reads a line page from the tail or a returned byte cursor, toward older or newer content, with optional exact `--level` and case-insensitive `--query` filters. The server owns page limits; the CLI supplies no limit. An empty filtered page can still advance its cursor, so use the returned `has_older` and `has_newer` flags to continue. Continuation fragments can appear as context when their line start lies outside the bounded read, even if that fragment does not establish a filter match. `library export` retains streamed delivery. Reported HTTP or transport failures return nonzero; named-file output is published only after the response is received and the file is closed. The CLI does not validate the JSONL end record. In particular, close-delimited HTTP/1.0 can make an interrupted download appear complete; import still rejects the missing end record. Stdout may contain a partial stream on failure, so its exit status must be checked. The separate [standalone export tool](../../cmd/export-library/main.go) opens the configured local Catalog and delegates to the same Library snapshot writer. Its default selection is `file,media,position`; related version groups follow Library selection rules. This selected export omits Location/original groups and Settings, so it is not a complete installation backup. Unknown or empty comma-separated type names fail before opening configuration, Catalog or output. Named regular-file output is staged in the same directory and replaces the resolved target only after export, resource cleanup, file synchronization and close succeed; failures retain the previous backup and remove the owned staging file. Existing permission bits and final symlink targets are preserved; replacement creates a new inode, so other hard links retain the old file. New files keep the normal creation mode and process umask. Stdout and explicitly named device/pipe outputs remain streaming and can contain partial data on failure. Interrupt and termination signals cancel the operation cooperatively. Signal interception is released after the first cancellation, so a later signal can terminate an operation blocked in an OS call. Cooperative cancellation runs normal cleanup; forced termination cannot guarantee it. ## Directory paths `ls` accepts one optional positional directory operand. A bare `ls` selects the Library root. `/Photos/2026`, `Photos/2026`, `./Photos/2026` and `library:///Photos/2026` all select the same logical directory relative to that root. The CLI has no persistent working directory, never uses the client's `PWD` to resolve the operand, and never falls back from a failed lookup to filesystem paths. Positional path syntax applies only to `ls`; `du`, `mv`, `mkdir`, `rm` and other commands retain their existing arguments and flags. `location://NAS/DCIM` selects `DCIM` relative to the registered root of the Location whose full, case-sensitive display name is exactly `NAS`. Both `location://NAS` and `location://NAS/` select that Location's root. `--server` still selects the YATM server; the URI's Location name is not a network host. Missing or duplicate display names fail resolution. Use `--location-id ID --path PATH` to select an ambiguous name by ID. Lookup enumerates existing registrations in bounded pages and compares names exactly, including Unicode names, without a fuzzy text filter, aliases or a display-name uniqueness constraint. The existing ID selectors remain available: `--file-id` selects a Library directory, and `--location-id` with optional `--path` selects a registered Location directory. A positional operand is mutually exclusive with either ID selector and with a nonempty `--path`. More than one directory operand is a usage error. Plain paths are literal and are not percent-decoded; a colon alone does not select a URI, so `library:2026` names an ordinary Library directory. URI components are percent-decoded once; encode literal `%`, `?` and `#` as `%25`, `%3F` and `%23`. Quote operands containing spaces for the shell. A percent-encoded slash in a Location name is rejected. After URI decoding, or directly for a plain path, `.` and `..` segments are normalized within the selected root; traversal above that root is rejected. Library path resolution uses existing API reads and compares each candidate directory name by exact bytes; a fuzzy search match alone never establishes identity. Resolution and the final List or Search share one command context and the global `--timeout` deadline. No API, entity or storage changes are required for path lookup. Ordinary `ls` still reads a complete directory, without `--limit` or `--cursor`. `--query` selects a paged Search within the resolved directory. `--recursive` also selects a paged Search over a Library subtree, even without a query expression. Both search forms accept `--limit` and `--cursor`. An unreadable child remains in List output with `error` and no actionable reference. The command drains the remaining rows, then returns the existing failure exit code with `code: incomplete`; directory or transport failures retain their own errors. Supported filenames are legal UTF-8, including literal backslashes, whitespace and control characters. Non-UTF-8 names are escaped for display only and are not usable path operands. Quote shell operands so the shell preserves their bytes. The [Library guide](library.md#browse-from-the-cli) shows usage. ## Reports and mutations The [shared rule](../architecture/contracts.md#synchronous-operation-reports) owns which commands report under `--dryrun`, what a report contains and which operations are excluded. This surface adds only where the CLI differs: `archive write tape format` keeps `--confirm-format`, because confirming the Tape barcode authorizes a write to a device rather than a dry-run decision, and a report needs no follow-up command — the same command without `--dryrun` performs the resolved work. ## Tape Commands - `settings devices` reads the typed DEVICE group, and `--device-json FILE` replaces the user-owned Device settings without device access. Server-owned `learned_read_parameters` are preserved and incoming values for them are ignored. [Device settings](devices.md#save-device-settings) and [Auto read order](read-order.md) explain the fields and give JSON examples. - `archive write tape format` resolves the selected device's defaults at this Media operation. `--format ltfs_v1|tar_pax_v1`, `--record-mode`, `--record-bytes` and `--hardware-compression true|false` override them as [Tar write options](devices.md#tar-write-options) describe; LTFS rejects tar options. `--barcode` is normalized to six uppercase letters or digits and requires an exact `--confirm-format`. Registered Media and identity conflicts stop before writing, and FORMAT erases the cartridge even when its cartridge memory barcode is empty. - `archive write tape append` uses the registered profile and rejects conflicting explicit options. - `restore run tape`, Tape `verify run` and `scan run` take `--device`, an optional `--media-id` hint and `--refresh-position-map` (default false). Without a hint, the server identifies the Tape by its cartridge memory barcode; a hint never bypasses physical verification. [Load Tape for Restore or Scan](devices.md#load-tape-for-restore-or-scan) describes both options. - No command sets encryption: the configured `encrypt` script owns it, and Media output carries `encryption_key_saved`, never the key. The Device's read order applies to each read attempt, and the Job log records the requested and effective order with any fallback reason. ## Tape Device Diagnostics `tape device list` prints every configured Tape entry with its busy status and detail; listing sends no SCSI commands. `tape device inspect --device DEVICE` explicitly requests passive diagnostics as protobuf JSON. Absent facts stay absent, and inspection never loads, positions or modifies the cartridge. `media inspect tape` is the separate cartridge identification operation; it uses the configured LTFS adapter and can load the cartridge. [Inspect a Device](devices.md#inspect-a-device) explains the reported fields.