pgdesign v0.26.0 /internal/rev
On this page

Package rev computes pgdesign's whole-model canonical form and class-tagged revision identity, and the single JSON envelope serializer for schema output.

#internal/rev

#internal/rev

Package rev is pgdesign's whole-model canonical form, revision identity, and the single JSON envelope serializer (roadmap kernel 1.5, laws L1 + L7).

It sits one level above the per-object encoder (internal/enc): enc maps each resolved model object to canonical bytes; rev concatenates those per-object forms — in sorted manifest-key order, behind a versioned preamble — into ONE canonical whole-model byte string, and takes its SHA-256 as the model's revision. There is exactly one whole-model serializer, so generate json output and serve's /schema response are byte-identical for the same model (L1: one canonical form everywhere).

Two structures are load-bearing:

- Revision (L7): an opaque content identity tagged with its MODEL CLASS. A model built from TOML carries type information (registry-present); an introspected model does not (registry-absent). The class marker lives INSIDE the hashed bytes, so the two classes can never collide on a hash; and the Revision type is deliberately non-comparable with == (it holds a slice), forcing all comparison through Equal, which returns an ERROR on a cross-class comparison rather than silently reporting "not equal".

- The envelope: the JSON artifact {format_version, revision, model, diagnostics?}. The canonical whole-model bytes are embedded VERBATIM as a json.RawMessage and the envelope is emitted with a COMPACT encoder, so the model field bytes are byte-for-byte the bytes that revision hashes. Re-indenting or re-encoding them would break revision == hash(model); the envelope resolves the in-band-stamp circularity (bytes cannot contain their own hash) by putting the hash beside the bytes, not inside them.

rev imports enc, model, and diagnostic only. enc stays pure (it never imports diagnostic); serve and cmd import rev, never the reverse.

#FormatVersion

Go go
const FormatVersion = 1

FormatVersion is the version of the whole-model form and envelope STRUCTURE (the preamble shape, the objects-array framing, the envelope keys). It is independent of enc.CodecVersion, which versions the per-object byte forms: a change to how objects are framed bumps FormatVersion; a change to the bytes of an object bumps the codec. Both travel inside the hashed preamble.

#RegistryPresent

Go go
const RegistryPresent ModelClass = "registry_present"

RegistryPresent is the class of a model built from TOML source, which carries full type information (enums, domains, composites, state machines).

#RegistryAbsent

Go go
const RegistryAbsent ModelClass = "registry_absent"

RegistryAbsent is the class of a model recovered by introspecting a live database, which lacks the type registry.

#ModelClass

Go go
type ModelClass string

ModelClass distinguishes model classes per law L7. A model with type information (built from TOML, carrying a semtype registry) and an introspected model without it are DIFFERENT classes; their revisions are not comparable.

#Revision

Go go
type Revision struct

Revision is the opaque content identity of a whole model: the SHA-256 of its canonical whole-model bytes, tagged with its model class. It is deliberately NOT comparable with (the sum is a slice), so callers cannot silently get a false from a cross-class ; comparison goes through Equal, which errors on a class mismatch (L7).

#Envelope

Go go
type Envelope struct

Envelope is a parsed and revision-VERIFIED envelope. Parse guarantees that Revision equals the class-tagged hash of the embedded Model bytes.

#ParseRevision

Go go
func ParseRevision(s string) (Revision, error)

ParseRevision reconstructs a Revision from its ":" string form (the inverse of String). The empty string yields the zero Revision — a genesis edge's null parent (chain.Edge models it as Revision{}). It is the deserialization counterpart the on-disk chain (roadmap 5.2) needs to reconstruct edge parent/target revisions as GRAPH-NODE IDENTITIES from edge files. The returned Revision carries the class and digest parsed from the string and is used ONLY for identity comparison (String/Equal); it is NOT a certificate that any model hashes to it — store<->chain closure and edge-endpoint consistency are verified separately against the object store (roadmap 5.2's consistency checker). Its class is validated, so a malformed or unknown-class string is a hard error rather than a silently-wrong Revision.

#CanonicalBytes

Go go
func CanonicalBytes(s *model.Schema, class ModelClass) ([]byte, error)

CanonicalBytes returns the canonical whole-model form for s under model class class: the versioned preamble plus every per-object canonical form in sorted manifest-key order. This is the byte string Compute hashes and the bytes the envelope embeds verbatim. It is a pure function of the CANONICALIZED model — the caller is responsible for having built/canonicalized s (Build and introspect both do).

#Compute

Go go
func Compute(s *model.Schema, class ModelClass) (Revision, error)

Compute returns the revision of a whole model: the SHA-256 of its canonical whole-model bytes, tagged with the model class. Because the class marker is inside the hashed bytes, two classes never collide on a hash; because Compute also records the class on the returned Revision, cross-class Equal errors.

#Marshal

Go go
func Marshal(s *model.Schema, class ModelClass, diags []diagnostic.Diagnostic) ([]byte, error)

Marshal is THE single whole-model serializer. It produces the envelope JSON {format_version, revision, model, diagnostics?} for s under model class class, embedding the canonical whole-model bytes verbatim. generate json and serve's /schema response both call this function, so their bodies are byte-identical for the same (schema, class, diagnostics). diags may be nil (the field is then omitted).

#Parse

Go go
func Parse(data []byte) (Envelope, error)

Parse decodes an envelope produced by Marshal and VERIFIES that its revision matches the class-tagged hash of the embedded model bytes — the whole point of embedding the bytes verbatim. A mismatch (re-encoded or tampered model bytes) is a hard error.

Parse performs three independent integrity checks, in order:

1. The outer envelope format_version must equal FormatVersion. An envelope framed by a different serializer generation is rejected before its bytes are trusted, rather than being silently reinterpreted under the current framing. 2. The class named in the revision string must equal the class marker baked INSIDE the embedded whole-model bytes (L7). The two are written together by Marshal, so a divergence means the revision string was forged onto bytes of a different model class — accepting it would let a caller compare, under the wrong class tag, a revision whose bytes belong to another class. This is caught even though the hash already commits to the in-bytes class, because the outer string's class is what tags the returned Revision and thus governs future cross-class Equal checks. 3. The revision (class-tagged hash) must match a fresh hash of the embedded model bytes, catching any tamper of the bytes themselves.

#DecodeModel

Go go
func DecodeModel(canonicalBytes []byte) (*model.Schema, ModelClass, error)

DecodeModel reconstructs the schema from canonical whole-model bytes (the Model field of an envelope, or the output of CanonicalBytes) and returns it alongside its model class. The schema is Canonicalized. This realizes decode∘enc = id at the whole-model level: encoding a canonical model, then DecodeModel, then re-encoding yields byte-identical whole-model bytes.

#Revision.Class

Go go
func (r Revision) Class() ModelClass { return r.class }

Class returns the model class this revision belongs to.

#Revision.IsZero

Go go
func (r Revision) IsZero() bool { return r.class == "" && len(r.sum) == 0 }

IsZero reports whether r is the zero Revision — no class and no digest. The chain package (roadmap 1.4) uses it to represent a GENESIS edge's NULL parent: a genesis edge has no from-revision, modeled as the zero Revision rather than a sentinel value.

#Revision.Hex

Go go
func (r Revision) Hex() string { return hex.EncodeToString(r.sum) }

Hex returns the lowercase hex SHA-256 digest (without a class prefix).

#Revision.String

Go go
func (r Revision) String() string

String renders the revision as ":" so a printed revision names its model class explicitly — a registry-present and a registry-absent revision of otherwise-identical structure are visibly distinct.

#Revision.Equal

Go go
func (r Revision) Equal(other Revision) (bool, error)

Equal reports whether two revisions are equal. Comparing revisions of different model classes is a TYPE ERROR (L7): it returns a non-nil error, not a silent false, so an accidental cross-class comparison cannot be mistaken for a genuine difference.

Search