ImageArchive.Cli 0.5.1

dotnet tool install --global ImageArchive.Cli --version 0.5.1
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local ImageArchive.Cli --version 0.5.1
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=ImageArchive.Cli&version=0.5.1
                    
nuke :add-package ImageArchive.Cli --version 0.5.1
                    

ImageArchive

Embed any archive (Git repository, zip, tar, or raw bytes) inside a multi-frame APNG or animated WebP image with a visual header and footer, QR codes, and SHA-256 integrity.

The container format is defined by RFC 1.0.0: docs/ImageArchive-RFC.md.

Example image

Multi-frame APNG of this repository at origin/HEAD, produced by scripts/New-OriginHeadImageArchive.ps1 with dark chrome and the project header banner (encode + decode/extract/diff verified). Header and footer QRs encode the repo root at that commit (https://github.com/<owner>/<repo>/tree/<sha>). Open the file in an APNG-capable viewer to step frames; GitHub may show only the first frame.

Header banner (1024×68):

ImageArchive header banner

Sample archive (dark chrome + banner):

ImageArchive of origin HEAD (dark)

Regenerate:

pwsh -File scripts/New-OriginHeadImageArchive.ps1 -Output docs/images/origin-head.png -Dark

Features

  • Square frames, default 1024×1024 (configurable 512–1440 via frameWidth / --width)
  • Chrome: 67 px header + 67 px footer; data height = frameWidth − 134
  • Data capacity per frame: frameWidth × (frameWidth − 134) × 3 (default 2,734,080 bytes)
  • QR cells 67×67 (65×65 modules, 1 px margin all sides); general payload max 200 chars
  • Header: free-form text, image, or numbered folder round-robin; top-right QR
  • Footer: left QR (frame data SHA-256), Frame N of M + SHA text, right QR (tool commit URL)
  • Required camelCase text metadata (encoderName, jsonManifest, jsonSchema, …) plus streamSha256
  • Archive types: raw (default), git (compressed tar of .git + worktree), zip, tar
  • Default codec path: SkiaSharp 3.119.x raster + real APNG / animated WebP containers
  • Multi-target: net8.0;net9.0;net10.0
  • CLI tool command: imga (init / encode / decode)
  • Tests: xUnit v3 (unit + integration, including flagship E2E)
  • Build: Nuke 10 (build/ project targets net10.0)

Solution layout

ImageArchive.slnx
src/ImageArchive/                 # library
src/ImageArchive.Cli/             # .NET tool (ToolCommandName=imga)
tests/ImageArchive.UnitTests/
tests/ImageArchive.IntegrationTests/
build/                            # Nuke build host
schema/imagearchive-schema.json
examples/example-manifest.json

Versioning

GitVersion drives assembly and NuGet versions (GitVersion.yml, next-version: 0.5.0, tag prefix v). Packable projects use GitVersion.MsBuild; Nuke injects the same version on compile/pack/publish.

./build.ps1 Pack --configuration Release
./build.ps1 Publish --configuration Release   # needs NUGET_API_KEY

Dependencies (product pins)

Package Version Notes
SkiaSharp 3.119.4 Highest 3.x; Skia 4.x deferred (ZXing Skia binding)
ZXing.Net / Bindings.SkiaSharp 0.16.11 / 0.16.22 QR decode path
QRCoder 1.8.0 QR encode
JsonSchema.Net 9.2.2 Manifest schema eval
System.Text.Json 10.0.10 Manifest JSON
System.CommandLine 2.0.10 CLI package (argv currently hand-parsed)
xunit.v3 / runner.visualstudio 3.2.2 / 3.1.5 Test host
Microsoft.NET.Test.Sdk 18.8.1 VSTest
Nuke.Common 10.1.0 Build automation

Build and test

Requires .NET SDK 8, 9, and 10 (Nuke build host needs SDK 10).

dotnet build ImageArchive.slnx
dotnet test ImageArchive.slnx

Nuke build

Bootstrap entrypoints: build.ps1 / build.sh (or dotnet run --project build/_build.csproj --).

Target Purpose
Clean Remove bin/obj and artifacts/
Restore / Compile Restore and build solution
Test Run unit + integration tests
PackLibrary Pack ImageArchive NuGet library to artifacts/packages/
PackCliTool Pack ImageArchive.Cli as a .NET tool (command: imga)
Pack PackLibrary + PackCliTool
PublishLibrary Push library package (needs NuGetApiKey or NUGET_API_KEY)
PublishCliTool Push CLI tool package (same API key)
Ci Clean + Test + Pack
# Local
./build.ps1 Compile
./build.ps1 Test --configuration Release
./build.ps1 Pack --configuration Release

# Publish library to nuget.org
./build.ps1 PublishLibrary --configuration Release --nuget-api-key $env:NUGET_API_KEY

# Optional: also publish CLI tool package
./build.ps1 PublishCliTool --configuration Release --nuget-api-key $env:NUGET_API_KEY

# Install CLI tool from local pack output
dotnet tool install ImageArchive.Cli --add-source ./artifacts/packages --version 1.0.0
# then:
imga encode --manifest path/to/manifest.json
imga decode --input archive.png --output extracted.bin

Optional version override: --package-version 1.2.3. Optional source: --nuget-source https://api.nuget.org/v3/index.json.

Planning package check (docs/schema receipt):

pwsh -File docs/verify-planning-package.ps1

Archive origin HEAD as an image

Encode a git-type ImageArchive of the remote default branch tip (origin/HEAD), then decode, extract to a separate folder, and byte-compare against the clone (same excludes as the encoder: bin, obj, .vs, node_modules):

pwsh -File scripts/New-OriginHeadImageArchive.ps1
# optional:
#   -Output artifacts\my-tip.png
#   -ExtractDir artifacts\origin-head-extract
#   -Format webp
#   -SkipFetch
#   -SkipVerify          # encode only
#   -KeepWorkDir         # keep clone + extract + tar under the stage dir
#   -Framework net10.0

Default output: artifacts/origin-head-<shortSha>.png. Exit is non-zero if the extract tree differs from the clone.

CLI

# Write a blank, schema-valid manifest (default: ./manifest.json)
imga init
imga init --output path/to/manifest.json --force

dotnet run --project src/ImageArchive.Cli -- encode --manifest path/to/manifest.json
dotnet run --project src/ImageArchive.Cli -- encode --manifest path/to/manifest.json --dark --width 1024
dotnet run --project src/ImageArchive.Cli -- decode --input archive.png --output extracted.bin
  • Dark chrome: CLI --dark and/or manifest "dark": true (default false). CLI --dark forces on. Header/footer bands and text invert; QR codes stay black-on-white. Data-region payload colors are unchanged.
  • Frame size: CLI --width <n> and/or manifest "frameWidth": <n> (512–1440, default 1024). Frames stay square. CLI --width overrides the manifest. Capacity per frame is width × (width − 134) × 3 bytes.

imga init (alias: imga manifest) writes a starter manifest with placeholder fields. Edit archive.source, output.path, and related fields before encode. Manifest output.path and output.format (png or webp) control encode output. Optional env IMAGEARCHIVE_TOOL_COMMIT_URL sets the footer right QR. | Exit code | Meaning | |----------:|---------| | 0 | Success | | 1 | Validation / usage | | 2 | Integrity failure | | 3 | I/O or archive source | | 4 | Unexpected internal error |

Library (quick start)

using ImageArchive;
using ImageArchive.Manifest;

var manifest = ManifestJson.Deserialize(File.ReadAllText("manifest.json"));
using var archive = File.OpenRead(manifest.Archive.Source);
using var output = File.Create(manifest.Output.Path);

new ImageArchiveEncoder().Encode(manifest, archive, output, new ImageArchiveEncodeOptions
{
    WorkingDirectory = Environment.CurrentDirectory,
    ToolCommitUrl = "https://github.com/sharpninja/ImageArchive"
});

using var image = File.OpenRead(manifest.Output.Path);
var result = new ImageArchiveDecoder().Decode(image);
// result.ArchiveStream holds the recovered payload

Public entry points include IImageArchiveEncoder / IImageArchiveDecoder, IImageEncoder / IImageDecoder (Skia defaults), IManifestValidator, and FrameGeometry constants.

Documentation

Doc Purpose
docs/ImageArchive-RFC.md Format specification (canonical)
schema/imagearchive-schema.json Manifest JSON Schema (streamSha256)
docs/qr-payload-limits.md QR 65×65 payload limits
docs/plans/ImageArchive-Implementation-Plan.md Implementation plan / public API
docs/receipts-requirements-rfc-1.0.0.md FR/TR/TEST AC coverage receipt
docs/Project/ Exported requirements documents
docs/wiki.yaml Wiki publish manifest

License

GPL-3.0-only

Repository

https://github.com/sharpninja/ImageArchive

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.5.1 160 7/15/2026
0.5.0 128 7/15/2026