PRERELEASE2026-08-29

Postrust 1.0.0-alpha.1

Both surfaces stop being asserted and start being measured. Postrust answers PostgREST’s REST dialect and Hasura’s GraphQL dialect, and how closely it answers each is now a number produced by replaying the other server’s own test suite against both and diffing the live responses.

Measured, not asserted

Neither harness interprets a test expectation. The reference implementation’s live response is the oracle, so a mistake in the extractor shows up as a case both servers answer the same way rather than as a false failure. Of the 456 Hasura cases counted at that level, 325 agree about data and 131 agree because both servers refuse — a distinction worth keeping, since counting only the first would score a case where Hasura itself raises an error as a failure to match it.

Both numbers carry their provenance. Each harness builds its own candidate, because which features it was built with is part of what is measured and cannot be read off the binary, and records the reference version, the features, the commit and whether the reference was replayed or a recording reused. The generators that put these figures on this page read that record and refuse a run that cannot account for itself. Nothing here is typed by hand.

What the alpha means

The surfaces are measured. The public Rust API has not been lived with by anyone outside the repository, and a prerelease carries no stability promise — expect it to move before 1.0.0.

The HTTP and GraphQL surfaces are the part meant to be stable. If you are pointing a PostgREST or Hasura client at this, that is the contract the conformance reports describe.

Added

Hasura's GraphQL dialect

A client generated against Hasura points at /v1/graphql unchanged: the root fields, the generated _bool_exp filter types, order_by as a list, aggregates, nested writes, on_conflict upserts, update_many, live subscriptions, and Hasura's error envelope — including the path that names a place in the request rather than in the response.

The permission model that comes with it

A schema per role, built from a schema cache already reduced to what that role can see, so nothing in the builders needs to know a permission exists. Row filters in the same language a where is written in. Reading and writing as two column sets, because a role may write a column it cannot read. Presets, ceilings, backend_only and _exists.

Hasura's authentication contract

An admin secret, x-hasura-* headers as session variables a policy can read, and a token that may select among the roles its x-hasura-allowed-roles lists. One deliberate difference: with no secret configured, headers carry no weight at all — a policy reading a value the caller chose is not a policy.

More of PostgREST's surface

Embedding through junction tables, spreads in the parent query, filtering and ordering an embedded list, computed relationships, Prefer: missing=default and max-affected, custom media types, and verbatim database errors in compatibility mode.

Fixed

Every one of these was found by measurement rather than by a report, and each had the same shape: the request succeeded, so nothing looked wrong.

Range headers were ignored unless they began 0-

Every other range silently returned the whole relation — the request succeeded, so nothing looked wrong. Range: 5-9 now means rows 5 to 9, and an inverted range is refused with 416 rather than quietly widened.

OPTIONS never reached the handler

The CORS layer answers every OPTIONS itself and never calls what it wraps, so no response carried Allow and the body was empty because nothing built one.

An expiry was honoured with 30 seconds of slack

exp is now checked to the second. The slack remains on nbf and iat, which describe a token not yet valid rather than one its issuer withdrew.

Two error codes were documented the wrong way round

PGRST301 and PGRST302 had been swapped in the documentation — so anyone branching on them from the docs branched wrongly.

Breaking changes

These are why the version is 1.0.0 rather than 0.4.1. They affect Rust code that depends on the crates; the HTTP and GraphQL surfaces are unaffected.

  • JwtError lost six variants and gained four.
  • Range, QueryResult and Table each gained public fields. None is #[non_exhaustive] yet, so a struct literal downstream needs updating; marking them is planned during the alpha series.

Known gaps

The largest is introspection, and it is not reachable from here: async-graphql builds its own registry and keeps it private, so the directives it installs and the order it lists types in cannot be changed from outside the library. Eight of the sixteen remaining Hasura divergences are that one thing.

Beside it: _stream subscriptions, the cursor-based half of Hasura’s subscription surface; and the OpenAPI document PostgREST serves at /. Actions and Apollo federation are subsystems rather than gaps. The two FINDINGS.md files record the rest, including four faults found in the Hasura harness itself — one of which invalidated eleven runs.

Try it

bash
docker pull ghcr.io/postrust/postrust:v1.0.0-alpha.1

A prerelease is not tagged latest, so it has to be asked for by name.