invar-cli

Crates.ioinvar-cli
lib.rsinvar-cli
version1.0.0
created_at2025-11-25 16:35:41.953613+00
updated_at2025-11-25 16:35:41.953613+00
descriptionThe Unchanging Key for Changing Data - Generate immutable structural state roots for cryptographic cache keys
homepagehttps://github.com/swyrknt/invar-cli
repositoryhttps://github.com/swyrknt/invar-cli
max_upload_size
id1950052
size209,290
(swyrknt)

documentation

README

Invar

The Unchanging Key for Changing Data

A high-precision CLI tool for generating deterministic, content-addressable identifiers from any data source. Built for CI/CD pipelines, build systems, and content-addressed storage.

CI License

Overview

Invar generates immutable structural state roots (Invar Keys) from arbitrary input data. Unlike traditional hashing, Invar processes data byte-by-byte through a mathematical synthesis engine, producing keys that represent the exact structural identity of your data.

Key Properties

  • Deterministic: Identical input always produces identical keys
  • Sensitive: A single byte change produces a completely different key
  • Structural: Keys represent the exact structural identity of data
  • Order-independent: Directory hashing produces consistent keys regardless of file system ordering

Installation

Pre-built Binaries

Download from GitHub Releases:

# Linux (x86_64)
curl -LO https://github.com/swyrknt/invar-cli/releases/latest/download/invar-linux-x86_64.tar.gz
tar -xzf invar-linux-x86_64.tar.gz
sudo mv invar /usr/local/bin/

# macOS (Apple Silicon)
curl -LO https://github.com/swyrknt/invar-cli/releases/latest/download/invar-darwin-aarch64.tar.gz
tar -xzf invar-darwin-aarch64.tar.gz
sudo mv invar /usr/local/bin/

Using Cargo

cargo install invar

From Source

git clone https://github.com/swyrknt/invar-cli.git
cd invar-cli
cargo install --path .

Docker

docker pull ghcr.io/swyrknt/invar-cli:latest

# Usage
echo "test" | docker run --rm -i ghcr.io/swyrknt/invar-cli gen
docker run --rm -v $(pwd):/data ghcr.io/swyrknt/invar-cli gen --directory /data/src

Quick Start

# Generate key from a file
invar gen --file package-lock.json

# Generate key from stdin
echo "v1.0.1" | invar gen

# Generate key from inline data
invar gen --data "Hello, World!"

# Generate key from a directory
invar gen --directory ./src

# Verify data matches an expected key
invar check --file package-lock.json --expected <key>

# Watch for changes
invar watch ./src --recursive

Features

File and Data Hashing

# From file
invar gen --file config.json

# From stdin
cat data.bin | invar gen

# From inline string
invar gen --data "my-data-v1.0"

# JSON output with metadata
invar gen --file data.txt --output json

Directory Hashing

Hash entire directories with deterministic, order-independent results:

# Hash a directory recursively
invar gen --directory ./src

# Include hidden files
invar gen --directory ./src --include-hidden

# JSON output with file count and size
invar gen --directory ./src --output json
# Output: {"key": "abc123...", "file_count": 42, "total_bytes": 123456}

Ignore Patterns

Invar respects .gitignore and .invarignore files by default:

# Default behavior (respects .gitignore and .invarignore)
invar gen --directory ./src

# Disable .gitignore
invar gen --directory ./src --no-gitignore

# Disable .invarignore
invar gen --directory ./src --no-invarignore

Create a .invarignore file for invar-specific exclusions:

# .invarignore - patterns specific to invar
*.log
build/
dist/
*.mp4

Streaming Mode

Process large files with constant memory usage:

# Stream large files
invar gen --file large-video.mp4 --stream

# With progress reporting (every N MB)
invar gen --file large-video.mp4 --stream --progress 100

Watch Mode

Monitor files or directories for changes:

# Watch a single file
invar watch config.yaml

# Watch a directory recursively
invar watch ./src --recursive

# Custom debounce interval (milliseconds)
invar watch ./src --recursive --debounce 1000

# JSON output for scripting
invar watch ./src --recursive --output json

Verification

# Generate a key
KEY=$(invar gen --file package-lock.json)

# Later, verify the file hasn't changed
invar check --file package-lock.json --expected "$KEY"

# Verify a directory
invar check --directory ./src --expected "$KEY"

Use Cases

CI/CD Cache Keys

# GitHub Actions
- name: Generate cache key
  run: echo "CACHE_KEY=$(invar gen --directory src)" >> $GITHUB_ENV

- uses: actions/cache@v4
  with:
    key: build-${{ env.CACHE_KEY }}

Build System Integration

# Skip rebuild if source unchanged
CURRENT=$(invar gen --directory src)
CACHED=$(cat .build-cache 2>/dev/null || echo "")

if [ "$CURRENT" = "$CACHED" ]; then
  echo "Source unchanged, skipping build"
else
  make build
  echo "$CURRENT" > .build-cache
fi

Content-Addressable Storage

# Store files by content hash
KEY=$(invar gen --file document.pdf)
cp document.pdf "storage/$KEY.pdf"

Configuration Versioning

# Detect configuration changes
if ! invar check --file config.yaml --expected "$STORED_KEY"; then
  echo "Configuration changed, reloading..."
  reload_configuration
fi

Output Formats

Text (Default)

$ invar gen --data "test"
a94a8fe5ccb19ba61c4c0873d391e987982fbbd3a1e3...

JSON

$ invar gen --data "test" --output json
{
  "key": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3...",
  "length_bytes": 32
}

$ invar gen --directory ./src --output json
{
  "key": "abc123...",
  "file_count": 42,
  "total_bytes": 123456,
  "skipped": []
}

Command Reference

invar gen (alias: g)

Generate an Invar key from input data.

Option Short Description
--file <PATH> -f Read from file
--data <STRING> -d Read from string
--directory <PATH> Hash directory recursively
--stream Use streaming mode (low memory)
--progress <MB> Report progress every N MB
--output <FORMAT> -o Output format: text or json
--include-hidden Include hidden files (directories)
--follow-links Follow symbolic links
--no-gitignore Ignore .gitignore patterns
--no-invarignore Ignore .invarignore patterns

invar check (alias: c)

Verify data produces an expected key.

Option Short Description
--expected <KEY> -e Expected 64-character hex key
(plus all gen options)

Exit codes: 0 = match, 1 = mismatch or error

invar watch (alias: w)

Watch for file changes and report new keys.

Option Short Description
<PATH> File or directory to watch
--recursive -r Watch subdirectories
--debounce <MS> Debounce interval (default: 500)
--output <FORMAT> -o Output format: text or json

How It Works

Invar uses the Distinction Calculus to generate structural identifiers:

  1. Initialize the root Distinction (D0)
  2. Process each byte of input data
  3. Convert each byte to a canonical Distinction via binary path encoding
  4. Synthesize with the running root: D_root = synthesize(D_root, D_byte)
  5. Return the final Distinction ID as a 64-character hex string

This ensures:

  • Byte-level precision: Every byte affects the final key
  • Structural uniqueness: Different data produces different keys
  • Mathematical determinism: Same data always produces same key

Examples

See the examples directory for:

  • GitHub Actions cache workflows
  • GitLab CI pipelines
  • Docker build optimization
  • Monorepo selective testing

Development

# Build
cargo build --release

# Test (87 tests)
cargo test

# Lint
cargo clippy -- -D warnings

# Format
cargo fmt

License

Dual-licensed under MIT or Apache 2.0 at your option.

Acknowledgments

Commit count: 0

cargo fmt