# ADAC CLI

**adac-cli** is the command-line interface for working with **ADAC (Archival Digital Asset Container)** files. It exposes the core capabilities of the `Adac` library — inspecting, validating, verifying, extracting, and creating `.adac` containers — directly from the terminal.

---

## Table of Contents

- [Prerequisites](#prerequisites)
- [Installation](#installation)
  - [Building from source](#building-from-source)
  - [Publishing a self-contained binary](#publishing-a-self-contained-binary)
- [Quick Start](#quick-start)
- [Usage](#usage)
  - [Global Options](#global-options)
- [Commands](#commands)
  - [`info` — Display Container Metadata](#info--display-container-metadata)
  - [`list` — List Container Entries](#list--list-container-entries)
  - [`validate` — Validate Container Structure](#validate--validate-container-structure)
  - [`verify` — Verify Fixity Checksums](#verify--verify-fixity-checksums)
  - [`extract` — Extract Files from a Container](#extract--extract-files-from-a-container)
  - [`create` — Create a New Container](#create--create-a-new-container)
- [Typical Workflows](#typical-workflows)
- [Exit Codes](#exit-codes)
- [Error Handling](#error-handling)
- [Project Structure](#project-structure)
- [License](#license)

---

## Prerequisites

- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) or later.

---

## Installation

### Building from source

Clone the repository and build the CLI project:

```bash
git clone https://github.com/InnoVadens/Adac.git
cd Adac
dotnet build Adac.Cli/Adac.Cli.csproj
```

Or build the entire solution:

```bash
dotnet build
```

### Publishing a self-contained binary

To produce a single executable that does not require the .NET SDK at runtime:

```bash
dotnet publish Adac.Cli/Adac.Cli.csproj -c Release -r win-x64 --self-contained
```

Replace `win-x64` with your target runtime identifier (e.g. `linux-x64`, `osx-arm64`).

---

## Quick Start

```bash
# Create a container from two master TIFF files
adac-cli create output.adac --master scan_001.tif --master scan_002.tif --title "Letter, 1945"

# Inspect what's inside
adac-cli info output.adac

# List every entry
adac-cli list output.adac

# Validate against the ADAC 1.0 specification
adac-cli validate output.adac

# Verify file integrity checksums
adac-cli verify output.adac

# Extract everything to a directory
adac-cli extract output.adac ./extracted
```

---

## Usage

Run from the project directory:

```bash
dotnet run --project Adac.Cli -- <command> [arguments] [options]
```

After publishing or installing as a tool, invoke directly:

```bash
adac-cli <command> [arguments] [options]
```

### Global Options

| Option | Description |
|---|---|
| `--version` | Show version information. |
| `-h`, `--help` | Show help and usage information. |

Every command also accepts `-h` / `--help` to display its own usage details.

```bash
adac-cli --help
adac-cli create --help
```

---

## Commands

### `info` — Display Container Metadata

Reads and prints the **manifest** (`manifest.json`) and **core metadata** (`metadata/core.json`) from an ADAC container as formatted JSON.

```
adac-cli info <file>
```

| Argument | Description |
|---|---|
| `<file>` | Path to the `.adac` container. |

**Example:**

```bash
adac-cli info archive.adac
```

**Sample output:**

```
=== Manifest ===
{
  "adacVersion": "1.0",
  "id": "b3f1a2c4-...",
  "createdOn": "2025-07-15T12:00:00+00:00",
  "createdBy": "adac-cli/1.0.0",
  "description": "Scanned photograph",
  "masters": [ ... ]
}

=== Core Metadata ===
{
  "id": "b3f1a2c4-...",
  "title": "Family portrait, 1923",
  "format": "TIFF"
}
```

The manifest section shows the ADAC version, unique container ID, creation timestamp, and the list of master files. The core metadata section shows descriptive cataloging information such as title and format.

---

### `list` — List Container Entries

Lists every entry (file path) inside the ADAC container's ZIP archive.

```
adac-cli list <file>
```

| Argument | Description |
|---|---|
| `<file>` | Path to the `.adac` container. |

**Example:**

```bash
adac-cli list archive.adac
```

**Sample output:**

```
Entries (6):
  manifest.json
  metadata/core.json
  master/master_0001.tif
  provenance/log.json
  provenance/checksums.json
  metadata/xmp/master_0001.xmp
```

Use this command to get a quick overview of everything stored in the container before extracting or validating.

---

### `validate` — Validate Container Structure

Validates an ADAC container against the ADAC 1.0 specification. Reports errors, warnings, and informational findings. By default, stored checksums are also verified; use `--skip-checksums` to perform structural validation only.

```
adac-cli validate <file> [--skip-checksums]
```

| Argument / Option | Description |
|---|---|
| `<file>` | Path to the `.adac` container. |
| `--skip-checksums` | Skip SHA-256 checksum verification during validation. |

**Example — full validation (structure + checksums):**

```bash
adac-cli validate archive.adac
```

**Example — structure only (faster):**

```bash
adac-cli validate archive.adac --skip-checksums
```

**Sample output:**

```
  [INFO] ADAC-010: Container created with ADAC version 1.0.
  [WARN] ADAC-040: Provenance log is missing. (provenance/log.json)
  [ERR ] ADAC-020: Master directory is empty. (master/)

Container is INVALID.
```

Each finding includes:

| Field | Meaning |
|---|---|
| Severity | `INFO` (informational), `WARN` (non-fatal issue), or `ERR` (spec violation). |
| Code | A stable identifier (e.g. `ADAC-010`) useful for filtering or scripting. |
| Message | Human-readable description of the finding. |
| Path | The container-relative path involved, if applicable. |

The final line prints **`Container is VALID.`** when no error-level findings exist, or **`Container is INVALID.`** otherwise.

---

### `verify` — Verify Fixity Checksums

Computes SHA-256 hashes for every file in the container and compares them against the stored checksum manifest (`provenance/checksums.json`). This is the integrity check archivists use to confirm that no content has been silently altered or corrupted since the container was created.

```
adac-cli verify <file>
```

| Argument | Description |
|---|---|
| `<file>` | Path to the `.adac` container. |

**Example:**

```bash
adac-cli verify archive.adac
```

**Sample output (all passing):**

```
Total files:    5
Verified:       5
Failed:         0
Missing:        0

Fixity check PASSED.
```

**Sample output (with failures):**

```
Total files:    5
Verified:       3
Failed:         1
Missing:        1

Mismatches:
  MISMATCH master/master_0001.tif
           expected: a1b2c3d4...
           actual:   ff00ff00...
  MISSING  metadata/xmp/master_0001.xmp

Fixity check FAILED.
```

---

### `extract` — Extract Files from a Container

Extracts content from an ADAC container to a directory on disk. Supports extracting everything, only masters, a single master by ID, or a single entry by its container path.

```
adac-cli extract <file> <output> [--entry <path>] [--master <id>] [--masters-only]
```

| Argument / Option | Description |
|---|---|
| `<file>` | Path to the `.adac` container. |
| `<output>` | Destination directory for extracted files. Created if it does not exist. |
| `--entry <path>` | Extract a single entry by its container-relative path (e.g., `metadata/core.json`). |
| `--master <id>` | Extract a single master file by its identifier (e.g., `master_0001`). |
| `--masters-only` | Extract only the master files. |

When none of the filtering options are provided, **all entries** are extracted with their original directory structure preserved.

**Examples:**

```bash
# Extract everything
adac-cli extract archive.adac ./output

# Extract only master files
adac-cli extract archive.adac ./masters --masters-only

# Extract a specific master by ID
adac-cli extract archive.adac ./output --master master_0001

# Extract a single entry by path
adac-cli extract archive.adac ./output --entry metadata/core.json
```

**Sample output (all entries):**

```
  manifest.json
  metadata/core.json
  master/master_0001.tif
  provenance/log.json
  provenance/checksums.json
Extracted 5 entries to C:\output
```

---

### `create` — Create a New ADAC Container

Creates a new `.adac` container from one or more master files on disk. The CLI generates a valid manifest, core metadata, checksum manifest, and the required directory structure automatically.

```
adac-cli create <output> --master <path> [--master <path> ...] [options]
```

| Argument / Option | Description |
|---|---|
| `<output>` | File path for the new `.adac` container. |
| `--master <path>` | **(Required)** Path to a master file to include. Repeat for multiple masters. |
| `--title <text>` | Title of the artifact (written to core metadata). |
| `--description <text>` | Human-readable description (written to the manifest and core metadata). |
| `--created-by <text>` | Software or person creating the container. Defaults to `adac-cli/<version>`. |

**Examples:**

```bash
# Single master
adac-cli create photo.adac --master scan.tif --title "Family portrait, 1923"

# Multiple masters with description
adac-cli create collection.adac \
  --master page_001.tif \
  --master page_002.tif \
  --description "Two-page letter, dated 1945" \
  --title "Wartime correspondence" \
  --created-by "Digitization Lab v2"
```

**Sample output:**

```
  [CopyingMasters] (1/2) page_001.tif
  [CopyingMasters] (2/2) page_002.tif
  [WritingChecksums] (1/1) provenance/checksums.json
Created: C:\archives\collection.adac
```

Master files are assigned sequential identifiers (`master_0001`, `master_0002`, …) and stored uncompressed in the `master/` directory, following the ADAC 1.0 specification's preservation-first principle.

---

## Typical Workflows

### Ingest and verify a new scan

```bash
# Package the master file
adac-cli create scan.adac --master original.tif --title "Deed of sale, 1887"

# Confirm the container is well-formed
adac-cli validate scan.adac

# Later — verify nothing changed in transit
adac-cli verify scan.adac
```

### Inspect an existing container

```bash
# What's inside?
adac-cli list archive.adac

# Show full metadata
adac-cli info archive.adac
```

### Extract masters for processing

```bash
adac-cli extract archive.adac ./working-copies --masters-only
```

### Batch-validate a folder of containers

On Linux / macOS:

```bash
for f in /archive/*.adac; do
  echo "--- $f ---"
  adac-cli validate "$f" --skip-checksums
done
```

On Windows (PowerShell):

```powershell
Get-ChildItem C:\archive\*.adac | ForEach-Object {
    Write-Host "--- $($_.Name) ---"
    adac-cli validate $_.FullName --skip-checksums
}
```

---

## Exit Codes

| Code | Meaning |
|---|---|
| `0` | Command completed successfully. |
| `1` | A runtime error occurred (e.g., file not found, invalid input). |

> **Note:** `validate` and `verify` currently return exit code `0` even when the container is invalid or fixity checks fail. Check the textual output (`Container is VALID.` / `INVALID.`, `Fixity check PASSED.` / `FAILED.`) to determine the result programmatically.

---

## Error Handling

When a command encounters an error, a message is written to **stderr** and the process exits with a non-zero code.

Common error scenarios:

| Scenario | Output |
|---|---|
| Container file does not exist | `File not found: /path/to/file.adac` |
| Master file does not exist (during `create`) | `Master file not found: /path/to/scan.tif` |
| Entry not found in container (during `extract --entry`) | Error message from the underlying library |

All user-facing error messages are prefixed with enough context to identify the problematic file or argument.

---

## Project Structure

```
Adac.Cli/
├── Adac.Cli.csproj           # Console project (net10.0)
├── Program.cs                 # Entry point — wires up the root command
├── CliServiceProvider.cs      # Builds the DI container with ADAC services
└── Commands/
    ├── InfoCommand.cs         # info
    ├── ListCommand.cs         # list
    ├── ValidateCommand.cs     # validate
    ├── VerifyCommand.cs       # verify
    ├── ExtractCommand.cs      # extract
    └── CreateCommand.cs       # create
```

All commands use the `Adac` core library via dependency injection (`AddAdacCore()`). The CLI itself is stateless — each invocation creates a fresh service provider and disposes it on completion.

---

## License

Copyright © 2026 InnoVadens, LLC. All rights reserved.
