# Adac Library — Usage Guide

**Adac** is the core .NET library for working with **ADAC (Archival Digital Asset Container)** files. It provides services for creating, reading, validating, and verifying `.adac` containers — self-describing, ZIP-based archival packages designed for long-term digital preservation.

The library is the foundation used by both the [CLI](../../Adac.Cli/README.md) and the [REST API](../../Adac.Api/README.md).

---

## Table of Contents

- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Service Registration](#service-registration)
- [Services](#services)
  - [IAdacContainerWriter — Creating Containers](#iadaccontainerwriter--creating-containers)
  - [IAdacContainerReader — Reading and Extracting](#iadaccontainerreader--reading-and-extracting)
  - [IAdacValidator — Validating Containers](#iadacvalidator--validating-containers)
  - [IAdacFixityService — Fixity and Checksums](#iadacfixityservice--fixity-and-checksums)
- [Models](#models)
  - [AdacManifest](#adacmanifest)
  - [AdacCoreMetadata](#adaccoremetadata)
  - [AdacPackageDefinition](#adacpackagedefinition)
  - [AdacChecksumManifest and AdacFixityReport](#adacchecksummanifest-and-adacfixityreport)
  - [AdacProvenanceLog](#adacprovenancelog)
  - [AdacRegionAnnotation](#adacregionannotation)
  - [AdacEditPipeline](#adaceditpipeline)
  - [AdacEncryptionDescriptor](#adacencryptiondescriptor)
- [Validation](#validation)
  - [Validation Results](#validation-results)
  - [Validation Options](#validation-options)
- [Constants](#constants)
- [Typical Workflows](#typical-workflows)
- [Project Structure](#project-structure)
- [License](#license)

---

## Prerequisites

- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) or later.

---

## Installation

Add a project reference from your application:

```bash
dotnet add reference path/to/Adac/Adac.csproj
```

Or, if the library is published as a NuGet package:

```bash
dotnet add package Adac
```

---

## Quick Start

```csharp
using Adac.Extensions;
using Adac.Models;
using Adac.Services;
using Adac.Validation;
using Microsoft.Extensions.DependencyInjection;

// 1. Set up DI
ServiceCollection services = new();
services.AddAdacCore();
using ServiceProvider sp = services.BuildServiceProvider();

// 2. Create a container
IAdacContainerWriter writer = sp.GetRequiredService<IAdacContainerWriter>();

AdacPackageDefinition package = new()
{
    Description = "Scanned photograph",
    CreatedBy = "my-app/1.0",
    CoreMetadata = new AdacCoreMetadata
    {
        Title = "Family portrait, 1923",
        Format = "TIFF",
    },
    Masters =
    {
        new AdacMasterSource
        {
            Id = "master_0001",
            SourceFilePath = @"C:\scans\photo.tif",
        },
    },
};

await writer.CreateAsync( @"C:\archives\photo.adac", package );

// 3. Read it back
IAdacContainerReader reader = sp.GetRequiredService<IAdacContainerReader>();

AdacManifest manifest = await reader.ReadManifestAsync( @"C:\archives\photo.adac" );
AdacCoreMetadata metadata = await reader.ReadCoreMetadataAsync( @"C:\archives\photo.adac" );

Console.WriteLine( $"Title: {metadata.Title}" );
Console.WriteLine( $"Masters: {manifest.Masters.Count}" );

// 4. Validate
IAdacValidator validator = sp.GetRequiredService<IAdacValidator>();
AdacValidationResult result = await validator.ValidateAsync( @"C:\archives\photo.adac" );

Console.WriteLine( result.IsValid ? "VALID" : "INVALID" );

// 5. Verify fixity
IAdacFixityService fixity = sp.GetRequiredService<IAdacFixityService>();
AdacFixityReport report = await fixity.VerifyAsync( @"C:\archives\photo.adac" );

Console.WriteLine( report.IsValid ? "Checksums OK" : "Checksum mismatch detected" );
```

---

## Service Registration

All ADAC services are registered with a single extension method call:

```csharp
using Adac.Extensions;

services.AddAdacCore();
```

This registers the following services as **singletons** (all implementations are stateless and thread-safe):

| Interface | Implementation | Purpose |
|---|---|---|
| `IAdacContainerWriter` | `AdacContainerWriter` | Creates new `.adac` containers. |
| `IAdacContainerReader` | `AdacContainerReader` | Reads metadata and extracts files from containers. |
| `IAdacValidator` | `AdacValidator` | Validates containers against the ADAC 1.0 specification. |
| `IAdacFixityService` | `AdacFixityService` | Computes and verifies SHA-256 checksums. |

The method returns the `IServiceCollection` for chaining:

```csharp
builder.Services
    .AddAdacCore()
    .AddLogging();
```

---

## Services

### IAdacContainerWriter — Creating Containers

Creates a new `.adac` container from an `AdacPackageDefinition`. The writer generates the manifest, core metadata, checksum manifest, and the full directory structure automatically.

```csharp
IAdacContainerWriter writer = sp.GetRequiredService<IAdacContainerWriter>();

AdacPackageDefinition package = new()
{
    Description = "Two-page letter",
    CreatedBy = "my-app/1.0",
    CoreMetadata = new AdacCoreMetadata
    {
        Title = "Wartime correspondence, 1945",
        Format = "TIFF",
        Tags = ["correspondence", "wartime"],
        Rights = new AdacRights
        {
            Statement = "Public domain",
            License = "CC0-1.0",
        },
    },
    Masters =
    {
        new AdacMasterSource
        {
            Id = "master_0001",
            SourceFilePath = @"C:\scans\page_001.tif",
        },
        new AdacMasterSource
        {
            Id = "master_0002",
            SourceFilePath = @"C:\scans\page_002.tif",
        },
    },
};

await writer.CreateAsync( @"C:\archives\letter.adac", package );
```

#### Tracking progress

Pass an `IProgress<AdacOperationProgress>` to receive phase-by-phase updates:

```csharp
Progress<AdacOperationProgress> progress = new( p =>
{
    Console.WriteLine( $"[{p.Phase}] ({p.Current}/{p.Total}) {p.CurrentFile}" );
} );

await writer.CreateAsync( @"C:\archives\letter.adac", package, progress );
```

Output:

```
[CopyingMasters] (1/2) page_001.tif
[CopyingMasters] (2/2) page_002.tif
[WritingChecksums] (1/1) provenance/checksums.json
```

#### Cancellation

All async methods accept an optional `CancellationToken`:

```csharp
using CancellationTokenSource cts = new( TimeSpan.FromSeconds( 30 ) );
await writer.CreateAsync( outputPath, package, cancellationToken: cts.Token );
```

---

### IAdacContainerReader — Reading and Extracting

Reads metadata, lists entries, and extracts files from existing containers.

#### Reading metadata

```csharp
IAdacContainerReader reader = sp.GetRequiredService<IAdacContainerReader>();

// Manifest (required)
AdacManifest manifest = await reader.ReadManifestAsync( "archive.adac" );

// Core metadata (required)
AdacCoreMetadata metadata = await reader.ReadCoreMetadataAsync( "archive.adac" );

// Provenance log (optional — may be null)
AdacProvenanceLog? log = await reader.ReadProvenanceLogAsync( "archive.adac" );

// Checksum manifest (optional — may be null)
AdacChecksumManifest? checksums = await reader.ReadChecksumsAsync( "archive.adac" );

// Region annotation (optional — may be null)
AdacRegionAnnotation? regions = await reader.ReadRegionAnnotationAsync(
    "archive.adac", "regions/master_0001.regions.json" );

// Edit pipeline (optional — may be null)
AdacEditPipeline? edits = await reader.ReadEditPipelineAsync(
    "archive.adac", "edits/master_0001.edits.json" );
```

#### Listing entries

```csharp
IReadOnlyList<string> entries = await reader.ListContentsAsync( "archive.adac" );

foreach ( string entry in entries )
{
    Console.WriteLine( entry );
}
// manifest.json
// metadata/core.json
// master/master_0001.tif
// provenance/log.json
// provenance/checksums.json
```

#### Extracting files

```csharp
// Extract a single entry by its container-relative path
await reader.ExtractFileAsync( "archive.adac", "metadata/core.json", @"C:\output\core.json" );

// Extract a single master by ID
await reader.ExtractMasterAsync( "archive.adac", "master_0001", @"C:\output" );

// Extract all masters
await reader.ExtractAllMastersAsync( "archive.adac", @"C:\output\masters" );
```

---

### IAdacValidator — Validating Containers

Validates a container against the ADAC 1.0 specification and returns structured findings.

#### Default validation (structure + checksums)

```csharp
IAdacValidator validator = sp.GetRequiredService<IAdacValidator>();

AdacValidationResult result = await validator.ValidateAsync( "archive.adac" );

if ( result.IsValid )
{
    Console.WriteLine( "Container is valid." );
}
else
{
    foreach ( AdacValidationEntry entry in result.Errors )
    {
        Console.WriteLine( $"[{entry.Code}] {entry.Message}" );
    }
}
```

#### Custom validation options

```csharp
AdacValidationOptions options = new()
{
    VerifyChecksums = false,            // Skip SHA-256 verification (faster)
    WarnOnMissingProvenanceLog = true,  // Warn if provenance/log.json is absent
    WarnOnMissingChecksums = true,      // Warn if provenance/checksums.json is absent
};

AdacValidationResult result = await validator.ValidateAsync( "archive.adac", options );
```

#### Inspecting findings by severity

```csharp
// All findings
foreach ( AdacValidationEntry entry in result.Entries )
{
    Console.WriteLine( $"[{entry.Severity}] {entry.Code}: {entry.Message}" );
}

// Errors only
foreach ( AdacValidationEntry error in result.Errors )
{
    Console.WriteLine( $"ERROR: {error.Code} — {error.Message}" );
}

// Warnings only
foreach ( AdacValidationEntry warning in result.Warnings )
{
    Console.WriteLine( $"WARN: {warning.Code} — {warning.Message}" );
}
```

---

### IAdacFixityService — Fixity and Checksums

Provides SHA-256 checksum computation, manifest generation, and container-level verification.

#### Verifying a container

```csharp
IAdacFixityService fixity = sp.GetRequiredService<IAdacFixityService>();

AdacFixityReport report = await fixity.VerifyAsync( "archive.adac" );

Console.WriteLine( $"Valid:    {report.IsValid}" );
Console.WriteLine( $"Total:   {report.TotalFiles}" );
Console.WriteLine( $"Passed:  {report.VerifiedFiles}" );
Console.WriteLine( $"Failed:  {report.FailedFiles}" );
Console.WriteLine( $"Missing: {report.MissingFiles}" );

foreach ( AdacFixityMismatch m in report.Mismatches )
{
    if ( m.IsMissing )
    {
        Console.WriteLine( $"  MISSING: {m.Path}" );
    }
    else
    {
        Console.WriteLine( $"  MISMATCH: {m.Path}" );
        Console.WriteLine( $"    expected: {m.ExpectedChecksum}" );
        Console.WriteLine( $"    actual:   {m.ActualChecksum}" );
    }
}
```

#### Generating a checksum manifest

```csharp
AdacChecksumManifest manifest = await fixity.GenerateManifestAsync( "archive.adac" );

foreach ( AdacFileChecksum file in manifest.Files )
{
    Console.WriteLine( $"{file.Checksum}  {file.Path}" );
}
```

#### Computing individual checksums

```csharp
// From a file path
string hash = await fixity.ComputeChecksumAsync( @"C:\scans\photo.tif" );

// From a stream
using FileStream stream = File.OpenRead( @"C:\scans\photo.tif" );
string hash2 = await fixity.ComputeChecksumAsync( stream );
```

---

## Models

### AdacManifest

Represents the container manifest (`manifest.json`) — the single source of truth describing the contents and structure of an ADAC container.

| Property | Type | Description |
|---|---|---|
| `AdacVersion` | `string` | Specification version (e.g., `"1.0"`). |
| `Id` | `string` | Globally unique package identifier. |
| `CreatedOn` | `DateTimeOffset` | ISO-8601 creation timestamp. |
| `CreatedBy` | `string` | Software or person that created the container. |
| `Description` | `string` | Human-readable description of the artifact. |
| `Masters` | `List<AdacMasterEntry>` | Master file entries (at least one required). |
| `Derivatives` | `List<AdacDerivativeEntry>?` | Optional derivative file entries. |
| `Metadata` | `AdacMetadataReference` | Paths to metadata files. |
| `ExtensionData` | `Dictionary<string, JsonElement>?` | Unknown fields preserved for forward compatibility. |

#### AdacMasterEntry

| Property | Type | Description |
|---|---|---|
| `Id` | `string` | Unique master identifier (e.g., `"master_0001"`). |
| `File` | `string` | Container-relative path (e.g., `"master/master_0001.tif"`). |
| `Xmp` | `string?` | Optional path to XMP sidecar metadata. |
| `Regions` | `string?` | Optional path to region annotation file. |
| `Edits` | `string?` | Optional path to non-destructive edit pipeline. |
| `Encryption` | `AdacEncryptionDescriptor?` | Encryption metadata if the file is pre-encrypted ciphertext. |

#### AdacDerivativeEntry

| Property | Type | Description |
|---|---|---|
| `Id` | `string` | Unique derivative identifier. |
| `File` | `string` | Container-relative path. |
| `SourceMasterId` | `string` | Identifier of the source master. |
| `Purpose` | `string` | Purpose (e.g., `"access"`, `"thumbnail"`, `"web"`). |
| `Encryption` | `AdacEncryptionDescriptor?` | Encryption metadata if applicable. |

---

### AdacCoreMetadata

Represents the core metadata record (`metadata/core.json`) describing the digital artifact using Dublin Core-inspired fields.

| Property | Type | Description |
|---|---|---|
| `Id` | `string` | Identifier matching the manifest `id`. |
| `Title` | `string?` | Human-readable title. |
| `Creator` | `string?` | Original creator (Dublin Core `dc:creator`). |
| `Subject` | `string?` | Subject keywords (Dublin Core `dc:subject`). |
| `Description` | `string?` | Content description. |
| `DateCreated` | `string?` | Original creation date (ISO-8601). |
| `Source` | `string?` | Source from which the artifact was derived. |
| `Format` | `string?` | Primary format (e.g., `"TIFF"`, `"WAV"`, `"DNG"`). |
| `Language` | `string?` | Content language (IETF BCP 47). |
| `Coverage` | `string?` | Geographic or temporal coverage. |
| `Tags` | `List<string>?` | Classification tags for categorization. |
| `Rights` | `AdacRights?` | Rights and licensing information. |
| `Technical` | `AdacTechnicalMetadata?` | Capture/creation technical details. |
| `Administrative` | `AdacAdministrativeMetadata?` | Lifecycle administrative details. |
| `Preservation` | `AdacPreservationMetadata?` | Preservation statistics. |

#### AdacRights

| Property | Type | Description |
|---|---|---|
| `Statement` | `string?` | General rights statement. |
| `Holder` | `string?` | Rights holder. |
| `License` | `string?` | SPDX identifier or free-text license. |
| `AccessRestrictions` | `string?` | Access restrictions (e.g., `"Embargoed until 2030"`). |

#### AdacTechnicalMetadata

| Property | Type | Description |
|---|---|---|
| `Scanner` | `string?` | Scanning device or capture system. |
| `Dpi` | `int?` | Scanning resolution (dots per inch). |
| `BitDepth` | `int?` | Bit depth (e.g., 8, 16, 24, 48). |
| `ColorSpace` | `string?` | Color space (e.g., `"sRGB"`, `"AdobeRGB"`). |
| `Duration` | `string?` | Audio/video duration. |
| `FrameRate` | `double?` | Video frame rate. |
| `SampleRate` | `int?` | Audio sample rate in Hz. |
| `PageCount` | `int?` | Page count for multi-page documents. |

#### AdacAdministrativeMetadata

| Property | Type | Description |
|---|---|---|
| `Repository` | `string?` | Archival repository name. |
| `AccessionNumber` | `string?` | Repository accession number. |
| `CatalogNumber` | `string?` | Catalog number. |
| `DigitizedBy` | `string?` | Person or system that performed digitization. |
| `DigitizedOn` | `string?` | ISO-8601 digitization timestamp. |

---

### AdacPackageDefinition

Defines everything needed to create a new ADAC container. Pass an instance to `IAdacContainerWriter.CreateAsync`.

| Property | Type | Required | Description |
|---|---|---|---|
| `Id` | `string?` | no | Package identifier. Auto-generated GUID if omitted. |
| `Description` | `string` | no | Human-readable description. |
| `CreatedBy` | `string` | no | Creator identifier. |
| `CoreMetadata` | `AdacCoreMetadata` | yes | Core metadata for the artifact. |
| `Masters` | `List<AdacMasterSource>` | yes | Master files to include (at least one). |
| `Derivatives` | `List<AdacDerivativeSource>?` | no | Optional derivative files. |
| `ProfileSourcePaths` | `List<string>?` | no | Paths to domain-specific profile metadata files on disk. |
| `ProvenanceLog` | `AdacProvenanceLog?` | no | Optional provenance log to include. |
| `RegionAnnotations` | `List<AdacRegionSource>?` | no | Optional region annotations. |
| `EditPipelines` | `List<AdacEditSource>?` | no | Optional edit pipelines. |

#### AdacMasterSource

| Property | Type | Required | Description |
|---|---|---|---|
| `Id` | `string` | yes | Unique identifier (e.g., `"master_0001"`). |
| `SourceFilePath` | `string` | yes | Path to the source file on disk. |
| `ContainerPath` | `string?` | no | Desired container path. Auto-generated if omitted. |
| `XmpSourceFilePath` | `string?` | no | Path to an XMP sidecar file on disk. |
| `Encryption` | `AdacEncryptionDescriptor?` | no | Encryption metadata for pre-encrypted files. |

#### AdacDerivativeSource

| Property | Type | Required | Description |
|---|---|---|---|
| `Id` | `string` | yes | Unique identifier. |
| `SourceFilePath` | `string` | yes | Path to the source file on disk. |
| `ContainerPath` | `string?` | no | Desired container path. Auto-generated if omitted. |
| `SourceMasterId` | `string` | yes | Identifier of the source master. |
| `Purpose` | `string` | yes | Purpose (e.g., `"access"`, `"thumbnail"`). |
| `Encryption` | `AdacEncryptionDescriptor?` | no | Encryption metadata for pre-encrypted files. |

#### AdacOperationProgress

Reported during container creation via `IProgress<AdacOperationProgress>`.

| Property | Type | Description |
|---|---|---|
| `Phase` | `string` | Current phase (e.g., `"CopyingMasters"`, `"WritingChecksums"`). |
| `Current` | `int` | Current item index (1-based). |
| `Total` | `int` | Total items in the current phase. |
| `CurrentFile` | `string?` | Name or path of the file being processed. |

---

### AdacChecksumManifest and AdacFixityReport

#### AdacChecksumManifest

Represents the fixity manifest (`provenance/checksums.json`).

| Property | Type | Description |
|---|---|---|
| `Algorithm` | `string` | Hash algorithm identifier (always `"sha256"` in ADAC 1.0). |
| `Files` | `List<AdacFileChecksum>` | File-checksum pairs. |

Each `AdacFileChecksum` contains:

| Property | Type | Description |
|---|---|---|
| `Path` | `string` | Container-relative file path. |
| `Checksum` | `string` | Lowercase hexadecimal SHA-256 hash. |

#### AdacFixityReport

Returned by `IAdacFixityService.VerifyAsync`.

| Property | Type | Description |
|---|---|---|
| `IsValid` | `bool` | `true` when all checksums match. |
| `TotalFiles` | `int` | Total files in the checksum manifest. |
| `VerifiedFiles` | `int` | Files that passed verification. |
| `FailedFiles` | `int` | Files with mismatched checksums. |
| `MissingFiles` | `int` | Files listed but absent from the container. |
| `Mismatches` | `List<AdacFixityMismatch>` | Details for each failure. |

Each `AdacFixityMismatch` contains:

| Property | Type | Description |
|---|---|---|
| `Path` | `string` | Container-relative path. |
| `ExpectedChecksum` | `string` | Hash from the stored manifest. |
| `ActualChecksum` | `string?` | Computed hash, or `null` if the file is missing. |
| `IsMissing` | `bool` | `true` when the file is absent. |

---

### AdacProvenanceLog

Represents the provenance log (`provenance/log.json`) — a chronological record of actions taken on the artifact.

| Property | Type | Description |
|---|---|---|
| `Events` | `List<AdacProvenanceEvent>` | Chronological list of events. |

Each `AdacProvenanceEvent` contains:

| Property | Type | Description |
|---|---|---|
| `Id` | `string` | Unique event identifier. |
| `Type` | `string` | Event type (e.g., `"scan"`, `"import"`, `"edit"`, `"derivativeCreated"`). |
| `Timestamp` | `DateTimeOffset` | ISO-8601 timestamp. |
| `Actor` | `string` | Person or system that performed the action. |
| `Software` | `string?` | Software used (optional). |
| `Details` | `Dictionary<string, JsonElement>?` | Event-specific properties. |

**Example — creating a container with provenance:**

```csharp
AdacPackageDefinition package = new()
{
    CreatedBy = "my-app/1.0",
    CoreMetadata = new AdacCoreMetadata { Title = "Deed of sale" },
    Masters = { new AdacMasterSource { Id = "master_0001", SourceFilePath = "deed.tif" } },
    ProvenanceLog = new AdacProvenanceLog
    {
        Events =
        {
            new AdacProvenanceEvent
            {
                Id = "evt-001",
                Type = "scan",
                Timestamp = DateTimeOffset.UtcNow,
                Actor = "Digitization Lab",
                Software = "Epson Scan v5.0",
            },
        },
    },
};
```

---

### AdacRegionAnnotation

Represents region annotations (`regions/<master>.regions.json`) that annotate spatial, temporal, or volumetric parts of a master artifact.

| Property | Type | Description |
|---|---|---|
| `MediaId` | `string?` | Master identifier this annotation applies to. |
| `CoordinateSystem` | `string` | Coordinate system: `"pixel"`, `"timecode"`, or `"3d"`. |
| `Width` / `Height` | `int?` | Media dimensions (for pixel-based systems). |
| `Duration` | `string?` | Media duration (for time-based systems). |
| `Frames` | `int?` | Total frame count (for video). |
| `Regions` | `List<AdacRegion>` | Annotated regions. |

Each `AdacRegion` contains:

| Property | Type | Description |
|---|---|---|
| `Id` | `string` | Unique region identifier. |
| `Type` | `string` | Region type (e.g., `"boundingBox"`, `"timeSegment"`, `"point"`). |
| `Bounds` | `AdacRegionBounds?` | Spatial or temporal bounds. |
| `Label` | `string?` | Human-readable label. |
| `LinkedEntities` | `Dictionary<string, JsonElement>?` | Domain-specific linked entities. |

---

### AdacEditPipeline

Represents non-destructive edit instructions (`edits/<master>.edits.json`) stored as transformation parameters rather than altered pixels.

| Property | Type | Description |
|---|---|---|
| `MediaId` | `string?` | Master identifier this pipeline applies to. |
| `PipelineVersion` | `string?` | Pipeline format version. |
| `CoordinateSpace` | `string` | Coordinate space for spatial parameters (default: `"pixel"`). |
| `ReferenceWidth` / `ReferenceHeight` | `int?` | Reference dimensions for scaling. |
| `Operations` | `List<AdacEditOperation>` | Ordered list of edit operations. |

Each `AdacEditOperation` contains:

| Property | Type | Description |
|---|---|---|
| `Id` | `string` | Unique operation identifier. |
| `Type` | `string` | Operation type (e.g., `"crop"`, `"rotate"`, `"colorAdjust"`). |
| `Parameters` | `Dictionary<string, JsonElement>?` | Operation-specific parameters. |
| `AppliesToDerivativeIds` | `List<string>?` | Derivatives produced using this operation. |

---

### AdacEncryptionDescriptor

Describes that a stored file was pre-encrypted before being placed into the container. ADAC never performs encryption or decryption — this is purely informational metadata.

| Property | Type | Description |
|---|---|---|
| `Algorithm` | `string` | Encryption algorithm (e.g., `"AES-256-GCM"`). |
| `KeyId` | `string?` | Opaque key reference for the consumer's key management system. |
| `OriginalMediaType` | `string?` | MIME type of the unencrypted content (e.g., `"image/tiff"`). |

**Example — packaging a pre-encrypted master:**

```csharp
AdacMasterSource encryptedMaster = new()
{
    Id = "master_0001",
    SourceFilePath = @"C:\vault\photo.tif.enc",
    Encryption = new AdacEncryptionDescriptor
    {
        Algorithm = "AES-256-GCM",
        KeyId = "vault://keys/archive-2025",
        OriginalMediaType = "image/tiff",
    },
};
```

SHA-256 fixity checksums cover the ciphertext, which is the correct behavior — the container verifies integrity of what is stored, not the original plaintext.

---

## Validation

### Validation Results

`AdacValidationResult` is returned by `IAdacValidator.ValidateAsync` and contains:

| Property | Type | Description |
|---|---|---|
| `IsValid` | `bool` | `true` when no error-level findings exist. |
| `Entries` | `IReadOnlyList<AdacValidationEntry>` | All findings. |
| `Errors` | `IEnumerable<AdacValidationEntry>` | Error-level findings only. |
| `Warnings` | `IEnumerable<AdacValidationEntry>` | Warning-level findings only. |

Each `AdacValidationEntry` contains:

| Property | Type | Description |
|---|---|---|
| `Severity` | `AdacValidationSeverity` | `Info`, `Warning`, or `Error`. |
| `Code` | `string` | Stable machine-readable rule code (e.g., `"ADAC-010"`). |
| `Message` | `string` | Human-readable description. |
| `Path` | `string?` | Container-relative path involved, or `null`. |

### Validation Options

| Property | Type | Default | Description |
|---|---|---|---|
| `VerifyChecksums` | `bool` | `true` | Verify SHA-256 checksums against actual content. |
| `WarnOnMissingProvenanceLog` | `bool` | `true` | Report missing `provenance/log.json` as a warning. |
| `WarnOnMissingChecksums` | `bool` | `true` | Report missing `provenance/checksums.json` as a warning. |

---

## Constants

The `AdacConstants` class in the `Adac.Constants` namespace exposes all well-known paths and values defined by the ADAC 1.0 specification:

| Constant | Value | Description |
|---|---|---|
| `Version` | `"1.0"` | Current ADAC specification version. |
| `FileExtension` | `".adac"` | Container file extension. |
| `MimeType` | `"application/vnd.adac.container"` | Registered MIME type. |
| `ManifestFileName` | `"manifest.json"` | Root manifest file. |
| `MasterDirectory` | `"master"` | Required master files directory. |
| `MetadataDirectory` | `"metadata"` | Metadata directory. |
| `CoreMetadataPath` | `"metadata/core.json"` | Required core metadata file. |
| `ProvenanceDirectory` | `"provenance"` | Provenance directory. |
| `ProvenanceLogPath` | `"provenance/log.json"` | Optional provenance log. |
| `ChecksumsPath` | `"provenance/checksums.json"` | Optional checksums manifest. |
| `DerivativesDirectory` | `"derivatives"` | Optional derivatives directory. |
| `RegionsDirectory` | `"regions"` | Optional regions directory. |
| `EditsDirectory` | `"edits"` | Optional edits directory. |
| `XmpDirectory` | `"metadata/xmp"` | Optional XMP metadata directory. |
| `ProfilesDirectory` | `"metadata/profiles"` | Optional domain-specific profiles directory. |
| `ChecksumAlgorithm` | `"sha256"` | Checksum algorithm identifier. |

Additionally, `AdacConstants.JsonOptions` provides the shared `JsonSerializerOptions` (camelCase, indented, nulls omitted) used for all ADAC JSON serialization.

---

## Typical Workflows

### Create, validate, and verify

```csharp
// Create
AdacPackageDefinition package = new()
{
    CreatedBy = "my-app/1.0",
    CoreMetadata = new AdacCoreMetadata { Title = "Deed of sale, 1887" },
    Masters = { new AdacMasterSource { Id = "master_0001", SourceFilePath = "deed.tif" } },
};

await writer.CreateAsync( "deed.adac", package );

// Validate structure + checksums
AdacValidationResult result = await validator.ValidateAsync( "deed.adac" );

// Verify fixity independently
AdacFixityReport report = await fixity.VerifyAsync( "deed.adac" );
```

### Read and inspect an existing container

```csharp
AdacManifest manifest = await reader.ReadManifestAsync( "archive.adac" );
AdacCoreMetadata metadata = await reader.ReadCoreMetadataAsync( "archive.adac" );

Console.WriteLine( $"ADAC Version: {manifest.AdacVersion}" );
Console.WriteLine( $"Package ID:   {manifest.Id}" );
Console.WriteLine( $"Created:      {manifest.CreatedOn}" );
Console.WriteLine( $"Title:        {metadata.Title}" );
Console.WriteLine( $"Masters:      {manifest.Masters.Count}" );

foreach ( AdacMasterEntry master in manifest.Masters )
{
    Console.WriteLine( $"  {master.Id} -> {master.File}" );
}
```

### Extract all masters for offline processing

```csharp
await reader.ExtractAllMastersAsync( "archive.adac", @"C:\working-copies" );
```

### Create a container with derivatives and rich metadata

```csharp
AdacPackageDefinition package = new()
{
    CreatedBy = "imaging-pipeline/2.0",
    CoreMetadata = new AdacCoreMetadata
    {
        Title = "Historical photograph",
        Format = "TIFF",
        Technical = new AdacTechnicalMetadata
        {
            Scanner = "Epson Expression 12000XL",
            Dpi = 600,
            BitDepth = 48,
            ColorSpace = "AdobeRGB",
        },
        Administrative = new AdacAdministrativeMetadata
        {
            Repository = "National Archives",
            AccessionNumber = "2025-001-0042",
            DigitizedBy = "Digitization Lab",
            DigitizedOn = "2025-07-15T10:30:00Z",
        },
    },
    Masters =
    {
        new AdacMasterSource
        {
            Id = "master_0001",
            SourceFilePath = @"C:\scans\photo.tif",
            XmpSourceFilePath = @"C:\scans\photo.xmp",
        },
    },
    Derivatives =
    [
        new AdacDerivativeSource
        {
            Id = "deriv_0001",
            SourceFilePath = @"C:\output\photo_access.jpg",
            SourceMasterId = "master_0001",
            Purpose = "access",
        },
    ],
};

await writer.CreateAsync( "photo.adac", package );
```

---

## Project Structure

```
Adac/
├── Adac.csproj                        # Class library (net10.0)
├── Constants/
│   └── AdacConstants.cs               # Well-known paths, versions, JSON options
├── Extensions/
│   └── ServiceCollectionExtensions.cs # AddAdacCore() DI registration
├── Models/
│   ├── AdacManifest.cs                # Manifest and master/derivative entries
│   ├── AdacCoreMetadata.cs            # Core metadata, rights, technical, admin
│   ├── AdacPackageDefinition.cs       # Package definition and source descriptors
│   ├── AdacChecksumManifest.cs        # Checksum manifest and fixity report
│   ├── AdacProvenanceLog.cs           # Provenance log and events
│   ├── AdacRegionAnnotation.cs        # Region annotations and bounds
│   ├── AdacEditPipeline.cs            # Non-destructive edit pipelines
│   └── AdacEncryptionDescriptor.cs    # Pre-encryption metadata
├── Services/
│   ├── IAdacContainerReader.cs        # Reader interface
│   ├── AdacContainerReader.cs         # Reader implementation
│   ├── IAdacContainerWriter.cs        # Writer interface
│   ├── AdacContainerWriter.cs         # Writer implementation
│   ├── IAdacValidator.cs              # Validator interface
│   ├── AdacValidator.cs               # Validator implementation
│   ├── IAdacFixityService.cs          # Fixity service interface
│   ├── AdacFixityService.cs           # Fixity service implementation
│   └── HashingStream.cs              # Internal stream wrapper for hashing
└── Validation/
    └── AdacValidationResult.cs        # Validation results, entries, options
```

---

## License

Copyright © 2026 InnoVadens, LLC. All rights reserved.
