The Enology Identifier (EID) Contract

Version: 1.0 Status: Published Maintainer: enology.ai (a product of Wine Platform) License: CC-BY 4.0 — see License

This document is the public contract for the Enology Identifier (EID), the canonical, globally unique identifier enology.ai assigns to wines it has verified into its data model. It defines the guarantees third parties — producers, importers, retailers, apps, and other data platforms — can rely on when they store, link to, or resolve an EID. This is a contract, not an implementation note: everything below is a promise about behavior, not a description of internal code.

1. Identifier syntax

An EID has the form:

EID-XXXXXXXXXX-YYYY
  • EID- — fixed literal prefix.
  • XXXXXXXXXX — ten random characters from a legibility-safe base32 alphabet (23456789ABCDEFGHJKLMNPQRSTUVWXYZ — digits 0/1 and letters I/O are excluded to prevent transcription errors on printed labels). Assigned at creation time; not sequential, not derived from any other field, and not guessable from a wine's name, producer, or vintage.
  • -YYYY — a four-digit vintage year, or the literal NV for a non-vintage wine.

Example: EID-3K9L396QP5-2022 (a 2022-vintage wine). A non-vintage wine of the same random suffix would be EID-3K9L396QP5-NV.

Consumers should treat the random segment as an opaque string and, when validating, accept [2-9A-HJ-NP-Z]{10} for the middle segment. (Historical note: early planning documents sketched a six-character segment; the ten-character legibility-safe form above is what has been assigned in production since the identifier's introduction and is the contractual format.)

An EID identifies one specific vintage of one specific wine from one specific producer — it is not a UPC/GTIN (a packaging unit, e.g. one specific bottle size and label run), a LWIN (a producer/wine identity without vintage granularity), or a TTB COLA number (a label approval, which can be reused across vintages or amended). Section 7 covers how EIDs relate to these other schemes.

2. Permanence

Once an EID is assigned to a wine, it is permanent:

  • An EID is never reused. If a wine record is deleted, retired, or found to be a duplicate, its EID is retired with it — it is never reassigned to a different wine.
  • An EID is never deleted from the identifier space, even if the wine it names is removed from public listing. A retired EID still resolves (see Deprecation); it does not silently vanish.
  • Anything that has ever cited an EID — a QR code on a printed label, a shelf-talker, a database foreign key, a scholarly citation — can rely on that EID continuing to mean the same wine for as long as enology.ai operates this service.

3. Merge behavior

Wine data is assembled from many sources (TTB filings, producer submissions, retailer feeds), and enology.ai occasionally discovers that two EIDs describe the same real-world wine (e.g. a producer-submitted record and an independently-ingested TTB record for the same vintage). When that happens, the records are merged:

  • One EID is chosen as the winner (the surviving canonical record); the other becomes a losing EID.
  • The losing EID is never deleted and never reused. It becomes a permanent alias that resolves to the winner, forever.
  • API behavior: looking up a losing EID returns the winner's data, with an additional field merged_into: "<winning EID>" in the response payload, so callers can detect the merge and update any cached references.
  • Public page behavior: the public page for a losing EID issues an HTTP 301 Moved Permanently redirect to the winning EID's canonical URL. Search engines and browsers treat this as the standard, permanent redirect signal.
  • Merges are one-directional and non-reversible by design (if a merge is later found to be wrong, the fix is a new de-merge into fresh EIDs, not un-merging the old one — the alias relationship, once published, is a permanent fact).

4. Deprecation

An EID can be marked deprecated — for example, a wine that is no longer produced, or a record later found to need substantial correction and superseded by a new EID. Deprecation is a status flag, not a deletion:

  • A deprecated EID continues to resolve. It never returns 404 Not Found for a reason related to deprecation. The API response includes status: "deprecated" and, when applicable, a superseded_by field pointing at the replacement EID.
  • 404 is reserved exclusively for EIDs that were never valid (malformed, or never assigned) — never for a real identifier that has simply aged out of current production.
  • This mirrors the permanence guarantee in Section 2: an identifier's job is to keep meaning the same thing, not to reflect whether the thing is still commercially active.

5. Versioning

This contract itself follows semantic-style versioning:

  • Additive changes (new optional response fields, new lookup endpoints, new entries in the cross-scheme mapping registry) are minor version bumps (e.g. 1.0 → 1.1) and ship without advance notice — they cannot break an existing integration, by definition.
  • Breaking changes (removing a field, changing the meaning of an existing field, changing the identifier syntax itself) are major version bumps (e.g. 1.x → 2.0) and require a minimum of 12 months of dual support — both the old and new contract versions remain live and resolvable in parallel — before the old version is retired. Deprecation notice for a major version is published in this document and at the lookup endpoints (see Section 6) at the start of the dual-support window, not at the end of it.
  • EIDs minted under any contract version remain valid under every subsequent version — versioning applies to the contract's rules and endpoints, never to identifiers already issued.

6. Lookup

Primary endpoint (live today):

GET https://enology.ai/api/w/{eid}

Returns the wine/vintage record as JSON, including merged_into and status/superseded_by fields where applicable (Sections 3–4). A public HTML rendering of the same record is served at https://enology.ai/w/{eid}.

Reserved alias:

GET https://enology.ai/api/eid/{eid}

This path is reserved for a scheme-explicit alias of the same lookup (useful once other identifier schemes gain their own /api/{scheme}/{value} lookup routes — see Section 7). It resolves identically to the primary endpoint once activated; reserving the path now means it cannot be repurposed later.

7. Cross-scheme mappings

Wines are frequently already identified by other schemes before or alongside their EID. Rather than forcing a single identifier system on the industry, enology.ai maintains a mapping registry (identifier_mappings) linking each EID to equivalent identifiers in other schemes:

| Scheme | Status | Description | | --- | --- | --- | | ttb_cola | Live | US TTB Certificate of Label Approval (COLA) ID(s) associated with the wine's label filing. A wine may cite more than one COLA (label amendments, size variants). | | permit | Live | The producer's TTB basic permit / winery license number. | | lwin | Reserved | Liv-ex Wine Identification Number — a producer/wine-level (non-vintage-granular) identifier widely used in the fine-wine trade. Mapping is planned; not yet populated. | | gtin | Reserved | GS1 Global Trade Item Number (UPC/EAN) — identifies a specific packaging SKU (bottle size + label run), which can be many-to-one against a single EID. Mapping is planned; not yet populated. |

"Reserved" means the scheme has a defined slot in the registry and a stable scheme key, so integrations can code against it today — but no mapping rows exist yet. "Live" means mapping rows exist and are actively populated as records are ingested or verified.

8. Attestation verification

Certain facts attached to a wine or producer — certifications, verified label facts, producer statements — are backed by signed attestations, not just asserted. This lets any third party independently verify that an attestation was actually issued by enology.ai and has not been altered, without trusting enology.ai's servers at query time.

  • Signing scheme: Ed25519 (via node:crypto), one platform signing key per key generation (key_id is the first 12 hex characters of sha256(public key, DER-encoded)).
  • Public key today: docs/identifier/attestation-public-key.pem in this repository (SPKI PEM format).
  • Public key going forward: the same key will additionally be published at the well-known URL https://enology.ai/identifier once the D2 public identifier page ships, so third parties do not need repository access to verify a signature.
  • Verification procedure, given an attestation record:
    1. Recompute the canonical payload hash: serialize the attestation's logical payload ({accountId, predicate, value, statement} or equivalent claim payload) with all object keys sorted recursively, then take sha256 of that JSON string. This is payload_hash.
    2. Verify payload_hash against the record's signature.sig_base64 using Ed25519 signature verification with the published public key. A true result proves the payload was signed by the holder of the corresponding private key and has not been altered since.
    3. To verify an attestation's position in the transparency log (not just its own signature), recompute chain_hash = sha256(prev_hash + payload_hash) and confirm it matches the record's chain_hash, and that prev_hash matches the previous attestation's chain_hash (or the literal GENESIS for the first record in the log). This proves the record has not been inserted, removed, or reordered relative to its neighbors.
    4. A key rotation is announced at least 12 months in advance for any key still signing active attestations (per the major-version dual-support rule in Section 5, applied to signing keys); an attestation's key_id always identifies which public key verifies it, so old attestations remain verifiable against their original key indefinitely, even after rotation.

Verification encoding

Byte-exact rules for reproducing enology.ai's verification independently:

  • payload_hash: sha256 over the UTF-8 bytes of the canonical payload JSON — object keys sorted recursively (all nesting levels), no inserted whitespace, numbers serialized per ECMAScript JSON.stringify semantics — encoded as lowercase hex. For producer attestations the canonical payload's field set is exactly { accountId, predicate, statement, value }.
  • Signed message: the Ed25519 signature in signature.sig_base64 is computed over the UTF-8 bytes of the lowercase-hex payload_hash string itself — not the raw 32-byte digest. Verifiers must sign/verify the hex string, not Buffer.from(hash, 'hex').
  • chain_hash: sha256 over the UTF-8 bytes of the plain string concatenation prev_hash || payload_hash (no separator between the two), encoded as lowercase hex. The first record in the log uses the literal string GENESIS as prev_hash.
  • Log-walk ordering: to replay the chain from genesis, fetch records ordered by created_at ascending, id ascending — the inverse of the tip-lookup ordering (created_at descending, id descending) used to find the current head.

License

This contract, the EID identifier scheme, and the ability to resolve, link to, and map against EIDs are published under Creative Commons Attribution 4.0 International (CC-BY 4.0). In plain terms: anyone may use, cite, link to, and build on EIDs and this contract, including commercially, provided enology.ai is credited as the source. There is no fee, license, or registration requirement to resolve an EID, to submit a cross-scheme mapping, or to build a product on top of this identifier system — free forever, for resolution and mapping alike. This is a deliberate strategic commitment (see Amendment A): an open identifier layer is the foundation of enology.ai's role as a shared industry utility, not a monetized gate.

Attestation public key

The Ed25519 public key referenced in Section 8 above, published at this well-known URL in SPKI PEM format for independent signature verification.

Get the attestation public key (.pem) →