Metadata-Version: 2.4
Name: ogxm
Version: 2.1.0
Summary: OGXM, the backgammon match format: codec, streams and signature checks
License-Expression: MIT
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Provides-Extra: verify
Requires-Dist: cryptography>=42; extra == "verify"
Provides-Extra: zstd
Requires-Dist: zstandard; extra == "zstd"

# ogxm

The Python package for **OGXM**, the backgammon match format: read and write
`.ogxm` files, read `.ogxms` streams (many matches in one file, as match
databases publish them), and check the signatures a file carries.

The package is a thin layer over `libogxm`, the reference codec, which the wheel
bundles. Every rule of the format runs in that one codec, so a file this package
accepts is a file every OGXM reader accepts.

```bash
pip install ogxm               # the codec and streams, no dependencies
pip install "ogxm[verify]"     # plus Ed25519 signature checks (cryptography)
pip install "ogxm[zstd]"       # plus zstandard, for .ogxms.zst dumps
```

## Files

```python
import ogxm

data = open("match.ogxm", "rb").read()
doc = ogxm.binary_to_json(data)      # the JSON projection (OGXM_JSON_SPEC.md)
print(doc["player_white"], doc["player_black"], doc.get("event"))

ogxm.v2_problem(doc)                 # None, or the name of the rule a document breaks
assert ogxm.json_to_binary(doc) == data
```

The JSON projection is a view for programs. The binary file is the interchange
format: store and exchange that.

## Streams

A stream is complete OGXM files back to back. A truncated download still reads:
every complete match comes out before `StreamTruncated` is raised.

```python
import zstandard, ogxm

with open("matches-2026-09.ogxms.zst", "rb") as raw:
    reader = zstandard.ZstdDecompressor().stream_reader(raw)
    for member in ogxm.iter_stream(reader):
        doc = ogxm.binary_to_json(member)
```

`iter_stream(f, strict=False, on_skip=...)` skips a corrupt member and resumes at
the next one that verifies. `ogxm.match_digest(member)` is the key an
Analysis-shape file joins its match by. `StreamWriter(f).write(member)` appends a
member, refusing one without a checksum.

## Signatures

```python
ogxm.verify_analysis(data, index=0, keys={publisher_key})
ogxm.verify_match(data, keys={publisher_key})
```

Pass the publisher's raw Ed25519 public keys for the purpose being checked.
Without `keys`, a valid signature proves the file is intact, not who signed it.

## The library

`ogxm.library_version()` is the bundled codec's release; the package version is
the same number. Set `LIBOGXM_PATH` to use another build of the library.
