Skip to content

stacs

Register a STAC catalogue into pgstac and prove it arrived.

stacs works on a catalogue after its items exist. It does not read source data or build items: that belongs to the repository that owns the source. Anything source-agnostic that is done to a published catalogue belongs here.

Status: pre-release. Extracted from stac_dem_bc, where the registration and verification code has run against a 102,460-item collection.

What it does

  • Verify what a STAC API serves against what is published: item id sets compared in both directions, and every item body compared by digest. Never by count: a STAC API omits ids it does not have without reporting an error, so equal counts can hide unequal sets. A body that could not be read is an error, never "unchanged".
  • Register with upserts only, collection before items. Nothing deletes. drift registers only what the API is missing or serves differently, which keeps it stateless: a skipped run is picked up by the next one. Every write is followed by a check that the API now serves the body that was sent.
  • Audit items before they are registered: each names the collection it is going into, and carries the asset keys the catalogue declares (require) and none it has retired (forbid).
  • Validate items with pystac.

Body digests are SHA-256 over RFC 8785 (JCS) canonical JSON, with links removed (the API rewrites them) and null members dropped (pgstac strips them). research/pgstac_round_trip.md records what pgstac changes between load and serve, and why each rule exists.

Install

Managed with uv. Install from git rather than PyPI: the stacs name on PyPI belongs to an unrelated, archived project.

[tool.uv.sources]
stacs = { git = "https://github.com/NewGraphEnvironment/stacs", tag = "v0.2.0" }

Without uv: pip install "stacs @ git+https://github.com/NewGraphEnvironment/stacs@v0.2.0".

Development

uv sync
uv run pytest

The tests run offline: a stub STAC API on loopback, a stub ssh that runs the remote script locally against a fake pypgstac, and a guard that refuses any DNS lookup outside loopback.