Metadata-Version: 2.4
Name: salt-grain
Version: 0.1.0
Summary: Shared deterministic primitives used by SaltyDiff tools
Author: SaltyDiff
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/SaltyDiff/salt-grain
Project-URL: Repository, https://github.com/SaltyDiff/salt-grain
Project-URL: Issues, https://github.com/SaltyDiff/salt-grain/issues
Keywords: saltydiff,canonicalize,digest,structured-diff,receipt,deterministic
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: <3.13,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Dynamic: license-file

# Salt Grain

Shared deterministic primitives used by SaltyDiff tools.

Salt Grain is small infrastructure, not a flagship product. Most developers will
encounter it transitively through SaltyDiff products such as
[MCP Fingerprint](https://github.com/SaltyDiff/mcp-fingerprint) (`pip install mcp-fingerprint`
pulls Salt Grain automatically). You can also install it directly when you need
the primitives yourself.

## Why it exists

SaltyDiff tools need stable, inspectable building blocks for:

- JSON canonicalization
- SHA-256 digests of exact bytes
- structural diff of JSON-compatible values
- content-addressed receipt bind / verify

Salt Grain packages those primitives so a clean Python environment can install
them with ordinary `pip` — no private source checkouts, no internal Factory
tooling, and no manually preinstalled foundation packages.

## Install

```bash
pip install salt-grain
```

Requires Python `>=3.12,<3.13`.

## What it provides

Compatibility modules keep their historical import names:

| Module | Role |
| --- | --- |
| `foundation_json_canonicalize` | Canonicalize JSON-domain values to exact UTF-8 bytes |
| `foundation_bytes_digest` | SHA-256 digest of exact bytes → 64 lowercase hex chars |
| `foundation_structured_diff` | Structural diff of JSON-compatible values |
| `foundation_receipt_bind` | Bind a content-addressed receipt |
| `foundation_receipt_verify` | Verify a bound receipt |
| `scorby_canonicalize` | Compatibility canonicalize used by receipt materialization |

`salt_grain` itself exposes only version and packaging provenance metadata.

## Quickstart

```python
from foundation_json_canonicalize import canonicalize_json
from foundation_bytes_digest import digest_bytes

canon = canonicalize_json(
    {
        "schema_version": "foundation.json_canonicalize.request.v0.1",
        "value": {"id": 1, "score": 0.5},
    }
)
assert canon["ok"] is True

digest = digest_bytes(
    {
        "schema_version": "foundation.bytes_digest.request.v0.1",
        "data": canon["canonical"],
    }
)
assert digest["ok"] is True
print(digest["digest"])
```

Expected result: prints a stable 64-character lowercase hex SHA-256 string for
that input (same string every run).

Structural diff:

```python
from foundation_structured_diff import diff_structures

result = diff_structures(
    {
        "schema_version": "foundation.structured_diff.request.v0.1",
        "before": {"id": 1},
        "after": {"id": 2},
    }
)
assert result["ok"] is True
assert result["equal"] is False
print(len(result["changes"]))  # at least one structured change
```

Receipt bind + verify:

```python
from foundation_receipt_bind import bind_receipt
from foundation_receipt_verify import verify_receipt

bound = bind_receipt(
    {
        "schema_version": "foundation.receipt_bind.request.v0.1",
        "subject": {"type": "example", "id": "1", "attributes": {}},
        "bindings": [],
        "validations": [],
        "outcome": {"status": "SUCCESS", "code": None},
        "previous_receipt_id": None,
    }
)
assert bound["ok"] is True

checked = verify_receipt(
    {
        "schema_version": "foundation.receipt_verify.request.v0.1",
        "receipt": bound["receipt"],
    }
)
assert checked["ok"] is True
assert checked["verified"] is True
print(checked["receipt_id"])  # 64-char hex receipt identity
```

Package identity:

```python
import salt_grain

print(salt_grain.__version__)  # 0.1.0
assert len(salt_grain.PROVENANCE) == 6
```

## Determinism

For a given supported input, each operation returns a deterministic envelope.
The same input produces the same canonical bytes, digests, diffs, and receipt
identities. Unsupported values fail closed with typed error codes.

No network I/O. No clock. No filesystem access in these primitives. Behavior is
inspectable from the returned envelopes — not opaque model judgment.

## Provenance

Salt Grain `0.1.0` vendors exact accepted implementations of shared
deterministic primitives and records that packaging lineage in
`salt_grain.PROVENANCE` (internal capability id, version, accepted commit
SHA, source ref, module name).

That mapping is packaging lineage for inspectability. It is not product
marketing and is not a substitute for historical acceptance records.

## Compatibility

v1 keeps these historical import modules available unchanged:

- `foundation_json_canonicalize`
- `foundation_bytes_digest`
- `foundation_structured_diff`
- `foundation_receipt_bind`
- `foundation_receipt_verify`
- `scorby_canonicalize`

Those module names exist for compatibility with accepted implementations.
They are not separately marketed products, and `scorby_canonicalize` is not
published as its own PyPI distribution.

## License

Apache License 2.0. See [LICENSE](LICENSE).
