Semantic Systems / Language / Glyphs
Developer Platform Blueprint for Embedded Semantics
Report summary
Embedded Semantics already has the right conceptual boundary for a developer platform: stable ConceptCodes are identity; reviewed expressions and provenance are governed evidence; vectors are evidence rather than authority . Its published architecture says that production resolution is currently det
Key topics
- Semantic Systems / Language / Glyphs
- Semantic Systems
- Language
- Glyphs
- AI
- C#
- TypeScript
- Python
- Runtime
Research provenance
For citation, use the report title and canonical URL. Archival presence does not establish authorship or promote report statements into portfolio evidence.
Source availability: 85 citation markers in the source export have no recoverable source links. Those markers are omitted from this reader; any supplied bibliography and ordinary links remain. Check the original sources before relying on the cited claims.
This page renders the archived Markdown as safe, formatted HTML. It is background research and does not become a portfolio claim without evidence review.
Full report
On this page
Executive findings, personas, and product principles
Embedded Semantics already has the right conceptual boundary for a developer platform: stable ConceptCodes are identity; reviewed expressions and provenance are governed evidence; vectors are evidence rather than authority. Its published architecture says that production resolution is currently deterministic reviewed-exact lookup, that the resolver should abstain when exact authority is absent, and that arbitrary-query embedding retrieval remains experimental.
That boundary should become the developer experience.
The platform should not ask ordinary application developers to learn ontologies, embedding geometry, equivalence classes, registry governance mechanics, or semantic-research terminology before they can use a stable identifier. The target mental model should be:
“Give me text. Tell me whether it maps to a known stable ID. Let me use that ID like any other typed identifier.”
There is one immediate prerequisite before a public developer-platform launch. As of August 23, 2026, the live site reports zero published concepts and zero reviewed exact expressions, while its deterministic prerelease bundle manifest contains 11 concepts and 55 expressions. The public concept endpoint likewise returns an empty collection. In other words, the project has a governed bootstrap artifact but not a populated live production registry. That makes a compelling five-minute quickstart impossible today: a correctly written application cannot successfully resolve even the site's own semantic_registry.stable_concept_identity example against the deployed registry. Fixing that deployment gap is more important than adding any sophisticated API feature.
Platform thesis. The ideal product is a small REST terminology service backed by immutable, downloadable registry releases. Remote and offline resolution must implement the same contract. SDKs should turn ConceptCodes into open-world value types, not closed enums. Registry changes should arrive as reviewable dependency upgrades, not silent semantic mutation. Provenance should always be inspectable but usually stay out of the application's happy path.
Developer personas
| Persona | What they are trying to do | What must stay invisible by default | Platform feature that wins them |
|---|---|---|---|
| Application developer | Put stable semantics into records, business rules, APIs, and databases | Ontology modeling, embeddings, governance workflow | resolve(), validate(), typed ConceptCode |
| API/schema designer | Say “this property contains a ConceptCode” and sometimes restrict it to a business-approved subset | Registry internals | JSON Schema annotations, generated ConceptSets |
| Backend/platform engineer | Make semantic lookups reliable and cheap | Manual cache/index implementation | Pinned packages, offline resolver, ETag-aware cache |
| Data engineer | Validate stored ConceptCodes and migrate datasets | Application SDK details | CLI batch validation, registry diff/replay |
| Enterprise architect | Combine public and organization-private concepts | Namespace collision and federation complexity | Namespaced registries, signed packages, federation rules |
| Compliance/audit engineer | Explain why a value resolved as it did | Model internals unless relevant | Provenance/evidence endpoint and immutable digest |
| Library/framework author | Expose ConceptCodes in reusable components without breaking future users | Specific current registry membership | Open-world code types and stable wire contracts |
| Research/semantic engineer | Inspect evidence and eventually experimental candidate retrieval | Nothing; this persona wants depth | Explicit evidence API and separately versioned research surface |
The distinction between ordinary application integration and semantic research is particularly important because Embedded Semantics itself says the production objective is concept resolution rather than generic similarity and explicitly treats “unknown” as a valid outcome.
Core use cases
The public platform should optimize for these operations, in this order:
- Resolve an expression to either one Concept, unknown, or ambiguity.
- Retrieve a Concept by stable ConceptCode.
- Validate a ConceptCode against a particular registry release.
- Pin and download the registry release used by an application.
- Inspect provenance/evidence for a resolution or Concept.
- Update a pinned registry through a reviewable diff.
- Run exactly the same resolution offline.
- Bind ConceptCodes into application schemas and language type systems.
- Test behavior across registry upgrades.
- Combine public and private registries without silently changing identity.
The design should resist expanding beyond those operations until real usage demands it.
API principles
Outcome is different from failure. unknown_expression, ambiguous_expression, and “this ConceptCode is not in this release” are expected semantic outcomes. Network failure, malformed JSON, authentication failure, unsupported API versions, and rate limiting are transport/service errors. Mixing the two makes ordinary application logic painful.
Identity is open-world. A ConceptCode that an SDK released six months ago has never heard of can still be perfectly legitimate. This single rule has major consequences: no global generated enum, no exhaustive registry-membership validation in constructors, and no assumption that a package upgrade requires an SDK upgrade.
Every result is reproducible. Responses should tell the caller exactly which registry identity, human-facing release version, and immutable digest produced the answer.
Offline and hosted behavior are equivalent. A pinned registry package and the hosted resolver must pass the same conformance suite.
Evidence is inspectable, not compulsory. Because Embedded Semantics distinguishes authoritative Concept identity from expressions, scores, vectors, prototypes, and other evidence, the API should preserve that distinction.
No client-defined normalization semantics. The site describes expressions as Unicode-preserving. Unicode defines multiple normalization forms and canonical-versus-compatibility equivalence, so “just lowercase and normalize the string” is not a sufficiently precise interoperability rule. The registry must own and version its matching algorithm, or distribute the precomputed lookup key used by the authoritative resolver.
Language is explicit. Use BCP 47 language tags where language constraints matter; BCP 47 defines the standardized syntax and matching framework for language tags.
Answers to the key architectural questions
| Question | Recommendation |
|---|---|
| Smallest useful API surface? | Four primitives: Resolve, Get Concept, Validate ConceptCode, Get Registry Release. Package download and change diff are the essential operational extension. |
| Error semantics? | Semantic outcomes are typed 200 responses. HTTP/service failures use RFC 9457 Problem Details. |
| Unknown and ambiguous? | Explicit discriminated results: status: "unknown" and status: "ambiguous". Never fabricate a ConceptCode, never encode ambiguity as an exception. |
| Expose evidence? | Yes, but opt-in. A tiny provenance summary belongs in normal responses; full evidence is requested with include=evidence or a subresource. |
| Pin registry versions? | Lock by immutable digest, accompanied by an opaque release label for humans. |
| Update registry versions? | Explicit check → diff → replay tests → update lockfile → review PR. Never silently mutate production semantics. |
| Offline SDK? | Yes. For today's deterministic reviewed-exact model it should be a first-class mode, not an emergency fallback. |
| What should SDKs generate? | Constants/value objects for selected concepts, closed ConceptSets when explicitly requested, JSON Schema fragments, and test fixtures. |
| ConceptCodes in typed languages? | Open-world string-backed value types/newtypes. |
| Generate enums? | Only for an explicitly closed, pinned ConceptSet—not the global registry. JSON Schema enum is by definition a fixed finite set. |
| Testing? | Shared conformance pack, in-memory registries, local server, remote sandbox, upgrade replay tests. |
| Migration tooling? | Registry diff, source/data scan, deprecated-code report, resolution replay, generated-code freshness checks. |
| Private registries? | Namespace them, pin them independently, never let “priority” silently override a public Concept identity, and federate ambiguity explicitly. |
A useful precedent comes from mature terminology systems rather than general-purpose knowledge graphs. FHIR distinguishes a code system defining codes from a ValueSet selecting codes for a particular use context, and exposes a specific $validate-code operation rather than requiring clients to download or reason through the entire terminology system. That separation maps well to Concept registry versus application ConceptSet.
Public REST API, JSON contracts, and protocol semantics
Recommended public surface
Use REST plus JSON as the normative public interface.
Do not launch GraphQL in the MVP. The core workload consists of point reads and small deterministic commands, not arbitrary client-shaped graph traversal. REST gives straightforward HTTP caching, immutable resource URLs, CDN distribution, observability, and high-quality OpenAPI generation. A read-only GraphQL explorer could be justified later if relationship traversal across large concept graphs becomes a dominant workflow; SDK correctness should never depend on it.
The smallest coherent API is:
| Method | Resource | Purpose |
|---|---|---|
POST | /v1/resolve | Resolve one expression |
POST | /v1/validate | Validate one ConceptCode |
GET | /v1/concepts/{conceptCode} | Retrieve authoritative Concept |
GET | /v1/registries/{registryId} | Discover current stable release |
GET | /v1/registries/{registryId}/versions/{version} | Retrieve immutable release metadata |
GET | /v1/registries/{registryId}/versions/{version}/package | Download registry package |
GET | /v1/registries/{registryId}/changes | Diff two releases |
The first four operations are the conceptual API. The package and change endpoints make it operationally trustworthy.
Later, add:
POST /v1/resolve:batch
POST /v1/validate:batch
GET /v1/concepts
GET /v1/concept-sets/{id}
POST /v1/webhook-subscriptions
Do not make browse/search endpoints prerequisites for normal runtime integration.
Resolution request
Zero-configuration development should be extremely small:
POST /v1/resolve
Content-Type: application/json
{
"expression": "stable concept identity",
"language": "en"
}
The server may use the public registry's current stable release when no pin is supplied, but the response must state exactly which release was used.
Production applications can pin explicitly:
{
"expression": "stable concept identity",
"language": "en",
"registry": {
"id": "public",
"version": "2026-08-23.1",
"digest": "sha256:8a42..."
}
}
The example release is illustrative; the currently published Embedded Semantics manifest calls the combined bundle prerelease, while its constituent packs use both 1.0 and 2026-08-22.1. That heterogeneity is a strong reason not to force SemVer interpretation onto registry labels. Treat version as an opaque release identifier and digest as the reproducibility lock.
Resolved response
{
"status": "resolved",
"input": {
"expression": "stable concept identity",
"language": "en"
},
"concept": {
"code": "semantic_registry.stable_concept_identity",
"display": "Stable concept identity",
"definition": "A persistent semantic identity whose meaning is governed independently of language-specific expressions.",
"lifecycle": "active"
},
"match": {
"method": "reviewed_exact",
"evidenceId": "expr_01K4W...",
"reviewed": true
},
"provenance": {
"registryId": "public",
"evidenceCount": 1
},
"registry": {
"id": "public",
"version": "2026-08-23.1",
"digest": "sha256:8a42..."
}
}
The API should not manufacture a confidence: 1.0 for exact matches. Exact governed authority and probabilistic model confidence are different concepts. Embedded Semantics explicitly separates reviewed exact production resolution from future model-backed confidence calibration.
Unknown response
Unknown is not a 404 and not an exception:
{
"status": "unknown",
"reason": "unknown_expression",
"input": {
"expression": "an unseen phrase",
"language": "en"
},
"registry": {
"id": "public",
"version": "2026-08-23.1",
"digest": "sha256:8a42..."
}
}
HTTP status: 200 OK.
This mirrors the project's documented behavior: production should abstain when no governed exact evidence exists.
Do not include “maybe this concept?” candidates in this production response while candidate retrieval remains experimental. Doing so would blur the project's most important semantic boundary. When experimental retrieval is exposed, give it a visibly separate operation or namespace such as:
POST /experimental/v1/candidates
An ordinary resolve() call should remain authoritative and conservative.
Ambiguous response
{
"status": "ambiguous",
"reason": "ambiguous_expression",
"input": {
"expression": "bank",
"language": "en"
},
"candidates": [
{
"conceptCode": "example.financial.bank",
"display": "Financial institution"
},
{
"conceptCode": "example.geography.river_bank",
"display": "River bank"
}
],
"registry": {
"id": "public",
"version": "2026-08-23.1",
"digest": "sha256:8a42..."
}
}
Again: 200 OK.
An exact expression being governed to more than one concept is already a documented Embedded Semantics outcome.
Ambiguity candidates should be ordered deterministically, but not represented as a ranked “winner plus alternatives” unless the registry itself provides authoritative context-specific precedence. In an authoritative resolver, ambiguity means the caller must supply more context or choose explicitly.
A future context-aware request could be:
{
"expression": "bank",
"language": "en",
"context": {
"conceptSet": "finance-v3"
}
}
Do not introduce arbitrary free-form context objects in v1; doing so invites hidden application-specific inference.
Concept retrieval
GET /v1/concepts/semantic_registry.stable_concept_identity
{
"code": "semantic_registry.stable_concept_identity",
"display": "Stable concept identity",
"definition": "...",
"lifecycle": "active",
"expressions": [
{
"language": "en",
"value": "stable concept identity",
"kind": "reviewed_exact"
}
],
"relationships": [],
"provenance": {
"createdAt": "2026-08-20T16:02:10Z",
"modifiedAt": "2026-08-22T10:12:03Z"
},
"registry": {
"id": "public",
"version": "2026-08-23.1",
"digest": "sha256:8a42..."
}
}
The public Concept representation should make code, definition, lifecycle, and registry coordinate immediately visible. Evidence-heavy structures should be behind:
GET /v1/concepts/{conceptCode}?include=evidence
or, once evidence becomes sufficiently large:
GET /v1/concepts/{conceptCode}/evidence
This follows the project's own provenance model, under which definitions, expressions, renderings, relationships, model profiles, prototypes, and evaluation outcomes can change independently without rewriting ConceptCode identity.
Evidence model
Yes, APIs should expose evidence. They should do so with a type system that prevents developers from confusing evidence with authority.
A useful record is:
{
"id": "evidence_01K4...",
"kind": "reviewed_expression",
"role": "authoritative_resolution_evidence",
"expression": {
"value": "stable concept identity",
"language": "en"
},
"review": {
"status": "approved",
"reviewedAt": "2026-08-22T10:10:00Z"
},
"source": {
"registryPack": "embedded-semantics.starter-registry",
"registryPackVersion": "1.0"
}
}
Future model evidence should look visibly different:
{
"id": "evidence_01K5...",
"kind": "embedding_candidate",
"role": "experimental_supporting_evidence",
"modelProfile": "example-model-v4",
"score": 0.84
}
The field role is more important than score: it tells a developer whether this evidence is semantically authoritative, supporting, or experimental. That encodes Embedded Semantics' published distinction between ConceptCode/registry definition as authority and models/scores/prototypes as evidence.
ConceptCode validation
A validation operation should answer a business question, not throw an HTTP error for an ordinary negative result:
POST /v1/validate
{
"conceptCode": "semantic_registry.stable_concept_identity",
"registry": {
"id": "public",
"digest": "sha256:8a42..."
}
}
Valid:
{
"valid": true,
"conceptCode": "semantic_registry.stable_concept_identity",
"lifecycle": "active",
"registry": {
"id": "public",
"version": "2026-08-23.1",
"digest": "sha256:8a42..."
}
}
Unknown code:
{
"valid": false,
"reason": "unknown_concept_code",
"conceptCode": "example.nonexistent",
"registry": {
"id": "public",
"version": "2026-08-23.1",
"digest": "sha256:8a42..."
}
}
Malformed code:
{
"valid": false,
"reason": "invalid_concept_code_syntax",
"conceptCode": "not a valid code"
}
All three are valid POST /validate transactions and should normally be 200.
This design is analogous to terminology APIs such as FHIR's $validate-code: the validation operation returns a Boolean/result rather than requiring the client to interpret retrieval failure itself.
Transport error model
Use application/problem+json and RFC 9457 for protocol/service errors; RFC 9457 exists specifically to provide machine-readable error detail without every API inventing its own envelope.
Example:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://embeddedsemantics.com/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The language property is not a valid BCP 47 language tag.",
"code": "invalid_request",
"requestId": "req_01K4Y7...",
"errors": [
{
"path": "/language",
"code": "invalid_format",
"message": "Expected a BCP 47 language tag."
}
]
}
Recommended status semantics:
| HTTP status | Machine code | Meaning |
|---|---|---|
400 | invalid_request | Malformed JSON or request contract |
401 | authentication_required | Missing/invalid credentials |
403 | forbidden | Authenticated but unauthorized |
404 | resource_not_found | Requested Concept/release endpoint does not exist |
409 | registry_pin_conflict | Requested version and digest identify different releases |
413 | batch_too_large | Batch exceeds service limit |
429 | rate_limited | Request quota exceeded |
503 | service_unavailable | Temporary service failure |
Do not use 404 for unknown_expression. The /resolve resource existed and executed successfully.
SDK exception hierarchies should correspond only to this error plane:
EmbeddedSemanticsError
ApiError
AuthenticationError
AuthorizationError
RateLimitError
RegistryPinConflictError
TransportError
PackageIntegrityError
UnknownResolution and AmbiguousResolution do not belong in that hierarchy.
API and schema versioning
Keep independent version axes:
| Axis | Meaning | Example |
|---|---|---|
| HTTP contract | Breaking wire/API semantics | /v1 |
| Registry release | Human-readable governed release | 2026-08-23.1 |
| Registry digest | Immutable content identity | sha256:... |
| Registry-package schema | File-layout compatibility | registry-package.v1 |
| SDK | Language package evolution | 2.3.0 |
| Concept identity | Stable semantic identifier | semantic_registry... |
| Concept metadata revision | Definition/evidence change | carried through registry release |
A new Concept must not require /v2. A new registry release must not require a new SDK. A new SDK must not silently change the registry used by a pinned application.
JSON Schema strategy
Use JSON Schema Draft 2020-12 for application-schema integration. Its default format vocabulary treats format as an annotation unless the assertion vocabulary is explicitly implemented, so an SDK must not pretend that:
{
"type": "string",
"format": "concept-code"
}
will universally perform semantic registry validation.
The baseline schema should therefore remain structurally interoperable:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"ConceptCode": {
"type": "string",
"x-embedded-semantics": {
"registry": "public",
"lockFile": "./embedded-semantics.lock"
}
}
}
}
Then the Embedded Semantics CLI/schema plugin interprets the annotation:
es schema validate order.json --schema order.schema.json
Do not introduce a custom JSON Schema vocabulary in the MVP. It would require every validator to understand project-specific semantic keywords.
For genuinely closed business domains, generate standard JSON Schema enum:
{
"$defs": {
"ReturnReasonConcept": {
"type": "string",
"enum": [
"acme.returns.damaged",
"acme.returns.wrong_item",
"acme.returns.changed_mind"
],
"x-embedded-semantics": {
"conceptSet": "acme.return-reasons",
"registryDigest": "sha256:..."
}
}
}
}
This is safe specifically because JSON Schema enum is defined to restrict values to a fixed set.
OpenAPI strategy
OpenAPI should be the canonical HTTP contract, not generated after the implementation.
Use OpenAPI 3.2.0 as the source specification. OAS 3.2 is a language-agnostic HTTP API description format and includes first-class webhook descriptions; it also retains JSON Schema-aligned schema capabilities.
Every operation must have:
- a permanent
operationId; - examples for all expected result variants;
- RFC 9457 problems;
- explicit registry-version fields;
- stable discriminator fields such as
status; - auth requirements;
- rate-limit documentation;
- caching semantics.
The resolution response should be a oneOf contract around status:
Resolution:
oneOf:
- $ref: '#/components/schemas/ResolvedResolution'
- $ref: '#/components/schemas/UnknownResolution'
- $ref: '#/components/schemas/AmbiguousResolution'
discriminator:
propertyName: status
Maintain an automatically tested 3.1-compatible derivative only if important generators still lag OAS 3.2 support. Do not weaken the canonical contract to the oldest generator in the ecosystem.
GraphQL decision
Recommendation: no public GraphQL in MVP or early v2.
Add it only if evidence shows developers routinely need queries like:
Concept
-> relationships
-> target Concepts
-> localized expressions
-> evidence
-> provenance
with radically different shapes per consumer.
Even then, make GraphQL a read/exploration surface over the same registry model. Keep resolve, validate, package distribution, pinning, and webhook delivery REST-native.
Registry distribution, offline operation, and change management
Registry distribution is where Embedded Semantics can become unusually easy to adopt. The current architecture is especially amenable to offline operation because the production resolver is governed exact lookup rather than a mandatory remote model call.
Registry lockfile
Introduce one boring, commit-friendly file:
embedded-semantics.lock
{
"lockVersion": 1,
"registries": {
"public": {
"version": "2026-08-23.1",
"digest": "sha256:8a42b8...",
"packageSchema": "embedded-semantics.registry-package.v1"
}
}
}
Developers should rarely hand-edit it.
es registry pin public
writes it.
es registry verify
confirms the local package matches the digest.
es registry status
shows what the application is actually using.
The digest is authoritative for reproducibility. The version label is for humans and release policy.
This design builds naturally on the project's existing manifest, which already publishes SHA-256 values for the prerelease bundle and its source packs.
Registry-package design
The site already advertises a Reviewed Registry Pack v1 JSON Schema and two downloadable packs, so the developer platform should evolve that artifact rather than inventing a separate “SDK database” format.
For the initial scale, a single canonical JSON package is ideal:
{
"schema": "embedded-semantics.registry-package.v1",
"registry": {
"id": "public",
"version": "2026-08-23.1"
},
"createdAt": "2026-08-23T14:00:00Z",
"parentDigest": "sha256:77a...",
"matching": {
"mode": "reviewed_exact",
"algorithm": "reviewed-exact-key-v1"
},
"concepts": [],
"expressions": [],
"ambiguities": [],
"provenance": []
}
The HTTP representation should supply the immutable digest through release metadata and ideally an HTTP Content-Digest as well; RFC 9530 defines HTTP digest fields specifically for representation/content integrity.
At large scale, package v2 can become a manifest plus sharded canonical files:
manifest.json
concepts.ndjson
expressions.ndjson
ambiguities.ndjson
provenance.ndjson
An optional compiled local index may be distributed separately:
index.sqlite
but it should never be the sole authoritative representation. SDK implementations must be able to rebuild it from the canonical registry package.
Matching semantics in the package
The package must define matching behavior explicitly:
{
"matching": {
"mode": "reviewed_exact",
"algorithm": "expression-key-v1",
"unicodePolicy": "registry-computed",
"languageMatching": "exact-bcp47"
}
}
Each expression record can preserve original Unicode and carry a registry-computed key:
{
"id": "expr_123",
"value": "identidad conceptual estable",
"language": "es",
"matchKey": "identidad conceptual estable",
"conceptCode": "semantic_registry.stable_concept_identity",
"authority": "reviewed_exact"
}
That prevents four SDK teams from producing four subtly different normalizers. Unicode normalization is a real specification problem with multiple normalization forms and equivalence rules, so it belongs in the registry contract rather than application folklore.
Offline SDK
There should absolutely be an offline SDK.
The interface should be deliberately isomorphic:
const online = new EmbeddedSemantics({ baseUrl: "..." });
const offline = await EmbeddedSemantics.offline({
lockFile: "./embedded-semantics.lock"
});
await online.resolve("stable concept identity", { language: "en" });
await offline.resolve("stable concept identity", { language: "en" });
For the same registry digest, both must produce equivalent domain results.
An application should also be able to use a hybrid strategy:
const es = await EmbeddedSemantics.create({
registry: {
lockFile: "./embedded-semantics.lock",
prefer: "local",
fallback: "remote"
}
});
But the default for a pinned production application should be deterministic: do not silently switch registry versions or semantic modes because the network happens to be available.
Cache architecture
Use three cache levels.
Immutable package cache
~/.cache/embedded-semantics/
sha256/
8a/
8a42.../
registry.json
verified.json
Content-addressed packages never need invalidation. A new release produces a new digest.
Compiled lookup cache
registry digest
-> language
-> registry matching key
-> 0, 1, or N ConceptCodes
The index may use memory maps, SQLite, a language-native immutable map, IndexedDB in browsers, or another local representation. That is an SDK implementation detail.
Resolution-result cache
A bounded in-memory cache keyed by:
(registry digest, language constraint, expression)
This makes cache correctness trivial: registry changes create a new namespace.
For mutable discovery endpoints such as “current stable public release,” use normal HTTP validators. HTTP caching supports conditional revalidation and reuse after 304 Not Modified; ETags/conditional requests are the right mechanism.
Registry update workflow
Never make production registry updates implicit.
The expected workflow should look like dependency management:
$ es registry check
public 2026-08-23.1 -> 2026-09-04.1 available
$ es registry diff public
+ 18 concepts
+ 74 reviewed expressions
~ 3 definitions
! 2 expressions become ambiguous
! 1 expression reassigned
- 0 concepts removed
$ es migrate replay ./tests/semantic-corpus.jsonl --to 2026-09-04.1
1203 unchanged
14 unknown -> resolved
2 resolved -> ambiguous
0 resolved -> different concept
$ es registry update public --to 2026-09-04.1
Updated embedded-semantics.lock
The lockfile change belongs in version control.
Change taxonomy
Registry diffs should be semantic, not merely JSON diffs.
| Change | Default risk |
|---|---|
| New Concept | Low |
| New expression resolving to existing Concept | Low to medium |
| Definition clarification preserving identity | Medium |
| New ambiguity | High |
| Ambiguity resolved | Medium |
| Expression changes target Concept | Critical |
| Concept deprecated | High |
| Replacement/supersession relation added | High |
| Concept identity withdrawn | Critical |
| Matching algorithm changes | Critical |
The CLI and API should report those classifications.
Example machine-readable diff:
{
"from": {
"version": "2026-08-23.1",
"digest": "sha256:old..."
},
"to": {
"version": "2026-09-04.1",
"digest": "sha256:new..."
},
"changes": [
{
"kind": "expression_resolution_changed",
"risk": "critical",
"expression": "example expression",
"language": "en",
"before": ["example.a"],
"after": ["example.b"]
}
]
}
Migration rules
A stable semantic identity system must make an unusually strong promise: migration tooling never silently rewrites ConceptCodes based on fuzzy similarity.
A deprecation can publish:
{
"code": "acme.old_code",
"lifecycle": "deprecated",
"replacements": [
{
"conceptCode": "acme.new_code",
"relationship": "superseded_by"
}
]
}
Then:
es migrate scan
can say:
src/orders.ts:88
acme.old_code is deprecated
Suggested governed replacement: acme.new_code
No change applied.
The developer approves the identity change.
Change events and webhooks
For teams that need automatic notifications, publish CloudEvents. CloudEvents exists to standardize event metadata across event publishers and delivery systems.
Example:
{
"specversion": "1.0",
"id": "evt_01K5...",
"source": "urn:embedded-semantics:registry:public",
"type": "io.embeddedsemantics.registry.version.published",
"time": "2026-09-04T14:00:00Z",
"subject": "public/2026-09-04.1",
"datacontenttype": "application/json",
"data": {
"registryId": "public",
"version": "2026-09-04.1",
"digest": "sha256:new...",
"previousDigest": "sha256:old...",
"changeSummary": {
"conceptsAdded": 18,
"expressionsAdded": 74,
"ambiguitiesIntroduced": 2
}
}
}
Useful event types:
io.embeddedsemantics.registry.version.published
io.embeddedsemantics.registry.concept.changed
io.embeddedsemantics.registry.expression.changed
io.embeddedsemantics.registry.concept.deprecated
Do not emit one event per tiny record change to ordinary public subscribers unless they opt into that granularity. Most developers want “a new registry dependency is available.”
Webhook signatures can use a simple product HMAC scheme initially; an enterprise-grade option can use HTTP Message Signatures, for which RFC 9421 specifies authentication/signing of selected HTTP components.
OpenAPI 3.2 can document the outbound webhook directly, avoiding a second API-description system in the MVP.
OCI registry packaging
Do not require OCI infrastructure for the public MVP. Plain HTTPS package download is substantially simpler.
For enterprise and v2, however, OCI artifacts are an excellent optional distribution transport because organizations already mirror artifact registries into restricted environments. ORAS is expressly designed to push and pull non-container artifacts through OCI registries and supports packaging arbitrary files.
An enterprise could therefore mirror:
registry.acme.example/semantics/public@sha256:...
registry.acme.example/semantics/acme@sha256:...
and optionally verify signed OCI objects with Cosign; Sigstore documents signing and verification of OCI-hosted non-container objects.
SDKs, typed ConceptCodes, and code generation
The SDK is where semantic identity either becomes mundane—in the best sense—or remains specialist infrastructure.
The SDK should make this ordinary:
const result = await es.resolve(text);
if (result.status === "resolved") {
order.reason = result.concept.code;
}
It should make this difficult:
// Bad architecture:
const vector = await es.embedding(text);
order.reason = vector; // not identity
SDK architecture
Each officially supported SDK should have five layers:
Developer facade
↓
Domain model
↓
Resolver / Registry abstractions
↓
Remote transport OR Local registry store
↓
OpenAPI-generated primitives + HTTP/filesystem implementation
Generated transport: request/response serialization, endpoint signatures, basic HTTP plumbing.
Hand-written domain facade: ConceptCode, resolution unions, lockfile behavior, retry rules, cache policies, offline resolver, helpful diagnostics.
Do not expose raw generated OpenAPI classes as the primary public API. Generator naming and ergonomics change too easily.
Core interfaces conceptually look like:
Resolver
resolve(expression, options) -> Resolution
ConceptRepository
get(ConceptCode) -> Concept
validate(ConceptCode) -> ValidationResult
RegistryStore
open(RegistryPin) -> Registry
current(registryId) -> RegistryRelease
update(...) -> RegistryDiff
ConceptCode type design
A ConceptCode is an open-world semantic identifier, similar in application architecture to a URI, UUID, currency code, or external resource ID—not to a compiler enum whose complete population is known at library build time.
The wire form should stay a simple JSON string:
{
"reason": "semantic_registry.stable_concept_identity"
}
Developers should not have to persist:
{
"reason": {
"registryVersion": "...",
"internalId": "...",
"display": "...",
"evidence": "..."
}
}
unless their business domain genuinely needs those fields.
At runtime, SDKs wrap that string in a lightweight semantic type.
Why a global enum is unsafe
An enum captures a closed known set. JSON Schema explicitly defines enum as a fixed allowed set. C# enums are named constants over an integral underlying type, and Microsoft guidance describes them as appropriate for fixed sets of options.
A registry, by contrast, is designed to grow.
Suppose SDK 1.4 was generated when the registry contained:
concept.a
concept.b
A service then receives:
concept.c
from a newer producer.
With a closed generated enum, deserialization may fail before the application can even choose a forward-compatibility policy. With an open value type, the application safely carries the stable identifier and can validate it against its own pinned registry when appropriate.
Therefore:
Never generate the entire public registry as the canonical ConceptCode enum.
When enum generation is safe
Enums can be generated for a declared ConceptSet whose product semantics are intentionally closed:
conceptSet: acme.return-reasons
registry:
digest: sha256:...
codes:
- acme.returns.damaged
- acme.returns.wrong_item
- acme.returns.changed_mind
policy:
closed: true
That is analogous to selecting a bounded set of terminology codes for a particular application context, an approach also seen in FHIR ValueSets.
Even then, generated code should carry the source registry digest so a maintainer knows what the type means.
What code generation should produce
A project config:
embedded-semantics.codegen.yaml
registry:
lockFile: ./embedded-semantics.lock
generate:
knownConcepts:
- semantic_registry.stable_concept_identity
- semantic_registry.semantic_embedding
conceptSets:
- acme.return-reasons
targets:
- typescript
- csharp
output:
typescript: ./src/generated/semantics.ts
csharp: ./Generated/Semantics.g.cs
should produce:
Open-world constants
export const Concepts = {
stableConceptIdentity:
conceptCode("semantic_registry.stable_concept_identity"),
semanticEmbedding:
conceptCode("semantic_registry.semantic_embedding"),
} as const;
Closed set only when requested
export const ReturnReasons = {
damaged: conceptCode("acme.returns.damaged"),
wrongItem: conceptCode("acme.returns.wrong_item"),
changedMind: conceptCode("acme.returns.changed_mind"),
} as const;
export type ReturnReason =
(typeof ReturnReasons)[keyof typeof ReturnReasons];
JSON Schema fragments, generated test fixtures, and provenance comments:
Generated from registry digest sha256:...
Do not edit manually.
The generator must have deterministic name mapping and collision handling. Once:
foo.bar-baz
becomes:
FooBarBaz
that generated symbol must not arbitrarily change because a second Concept later produces the same normalized language identifier. Persist collision decisions in generated metadata.
C# SDK proposal
C# should use a string-backed value type:
public readonly record struct ConceptCode
{
public string Value { get; }
public ConceptCode(string value)
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("ConceptCode cannot be empty.", nameof(value));
Value = value;
}
public override string ToString() => Value;
}
Do not validate registry membership in that constructor. Construction means “this is intended to be a ConceptCode,” not “the current network registry recognizes it.”
Generated constants:
public static class KnownConcepts
{
public static readonly ConceptCode StableConceptIdentity =
new("semantic_registry.stable_concept_identity");
public static readonly ConceptCode SemanticEmbedding =
new("semantic_registry.semantic_embedding");
}
Resolution should use sealed records:
public abstract record Resolution
{
public sealed record Resolved(
Concept Concept,
MatchInfo Match,
RegistryCoordinate Registry) : Resolution;
public sealed record Unknown(
string Reason,
RegistryCoordinate Registry) : Resolution;
public sealed record Ambiguous(
IReadOnlyList<ConceptCandidate> Candidates,
RegistryCoordinate Registry) : Resolution;
}
Usage:
Resolution result = await semantics.ResolveAsync(
"stable concept identity",
language: "en",
cancellationToken);
switch (result)
{
case Resolution.Resolved resolved:
Console.WriteLine(resolved.Concept.Code);
break;
case Resolution.Unknown:
// Ask user, store raw text, or follow product policy.
break;
case Resolution.Ambiguous ambiguous:
// Present/contextualize candidates.
break;
}
C#'s built-in enums are backed by integral types, making them particularly awkward as the canonical representation for externally defined string ConceptCodes.
Suggested packages:
EmbeddedSemantics
EmbeddedSemantics.Offline
EmbeddedSemantics.AspNetCore
EmbeddedSemantics.Testing
EmbeddedSemantics.CodeGen
Keep EmbeddedSemantics dependency-light. Offline/package support can be an optional package if its storage dependencies become substantial.
TypeScript SDK proposal
TypeScript should use a branded string:
declare const conceptCodeBrand: unique symbol;
export type ConceptCode = string & {
readonly [conceptCodeBrand]: "ConceptCode";
};
export function conceptCode(value: string): ConceptCode {
if (value.length === 0) {
throw new Error("ConceptCode cannot be empty");
}
return value as ConceptCode;
}
Resolution is a discriminated union:
export type Resolution =
| {
status: "resolved";
concept: Concept;
match: MatchInfo;
registry: RegistryCoordinate;
}
| {
status: "unknown";
reason: "unknown_expression";
registry: RegistryCoordinate;
}
| {
status: "ambiguous";
reason: "ambiguous_expression";
candidates: ConceptCandidate[];
registry: RegistryCoordinate;
};
Usage:
const result = await es.resolve("stable concept identity", {
language: "en",
});
switch (result.status) {
case "resolved":
saveConcept(result.concept.code);
break;
case "unknown":
saveUnresolvedText("stable concept identity");
break;
case "ambiguous":
showDisambiguation(result.candidates);
break;
}
For closed generated sets, favor as const objects over TypeScript enum. TypeScript itself describes enums as one of its few constructs that changes JavaScript runtime behavior, and its handbook notes that developers may want to hold off on them unless they are sure they need them; TypeScript explicitly supports enum-like as const object patterns.
Suggested packages:
@embeddedsemantics/sdk
@embeddedsemantics/node
@embeddedsemantics/testkit
@embeddedsemantics/codegen
Browser offline storage can use IndexedDB behind the same RegistryStore abstraction; Node can use a filesystem package cache.
Python SDK proposal
Python can use typing.NewType:
from typing import NewType
ConceptCode = NewType("ConceptCode", str)
Python documents NewType specifically as a low-overhead distinct type for static type checkers while leaving runtime representation essentially unchanged.
Domain models:
from dataclasses import dataclass
from typing import Literal
@dataclass(frozen=True)
class Concept:
code: ConceptCode
display: str | None
definition: str | None
@dataclass(frozen=True)
class Resolved:
status: Literal["resolved"]
concept: Concept
@dataclass(frozen=True)
class Unknown:
status: Literal["unknown"]
reason: Literal["unknown_expression"]
@dataclass(frozen=True)
class Ambiguous:
status: Literal["ambiguous"]
candidates: tuple[Concept, ...]
Usage:
result = client.resolve("stable concept identity", language="en")
match result:
case Resolved(concept=concept):
print(concept.code)
case Unknown():
handle_unknown()
case Ambiguous(candidates=candidates):
choose_candidate(candidates)
Core package:
embedded-semantics
Optional extras:
embedded-semantics[offline]
embedded-semantics[otel]
embedded-semantics[test]
Avoid making Pydantic mandatory for the core client. Provide an optional integration module for organizations already using it.
Rust SDK proposal
Rust is an excellent fit for a newtype:
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[serde(transparent)]
pub struct ConceptCode(String);
Rust's official documentation describes the newtype idiom as providing compile-time guarantees that otherwise similar underlying values are not confused, while Serde's transparent representation allows a one-field newtype to serialize like its wrapped value.
impl ConceptCode {
pub fn new(value: impl Into<String>) -> Result<Self, ConceptCodeError> {
let value = value.into();
if value.is_empty() {
return Err(ConceptCodeError::Empty);
}
Ok(Self(value))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
Resolution:
pub enum Resolution {
Resolved {
concept: Concept,
registry: RegistryCoordinate,
},
Unknown {
reason: UnknownReason,
registry: RegistryCoordinate,
},
Ambiguous {
candidates: Vec<ConceptCandidate>,
registry: RegistryCoordinate,
},
}
Usage:
match client.resolve("stable concept identity", Some("en")).await? {
Resolution::Resolved { concept, .. } => {
println!("{}", concept.code.as_str());
}
Resolution::Unknown { .. } => handle_unknown(),
Resolution::Ambiguous { candidates, .. } => choose(candidates),
}
Do not generate a Rust enum for the entire registry. The newtype preserves arbitrary future codes without a special “Unknown” enum variant that loses the original wire value.
Suggested crates:
embedded-semantics
embedded-semantics-offline
embedded-semantics-testkit
Generated API versus generated registry code
Keep these concepts separate:
OpenAPI generation
changes when HTTP contract changes
Registry code generation
changes when selected registry concepts change
A registry update should never force regeneration of unrelated HTTP transport code. Conversely, a new optional API endpoint should not churn every application's generated Concept constants.
CLI, testing, IDE support, documentation, and local development
A high-quality CLI is not secondary to the SDK. It is the bridge between registry governance and ordinary development workflows.
CLI design
Binary name:
es
Primary commands:
es resolve
es concept
es validate
es registry
es cache
es schema
es generate
es migrate
es test
es serve
es auth
es doctor
Examples:
es resolve "stable concept identity" --lang en
RESOLVED
ConceptCode semantic_registry.stable_concept_identity
Registry public @ 2026-08-23.1
Method reviewed_exact
es resolve "bank" --lang en
AMBIGUOUS
2 governed concepts match the expression.
example.financial.bank Financial institution
example.geography.river_bank River bank
es resolve "unseen wording" --lang en
UNKNOWN
No governed exact expression exists in the pinned registry.
--json must emit the exact machine contract:
es resolve "..." --json | jq '.status'
Other important commands:
es concept get semantic_registry.stable_concept_identity
es concept get ... --include evidence
es validate semantic_registry.stable_concept_identity
es registry list
es registry pull
es registry pin public
es registry verify
es registry check
es registry diff
es registry update
es registry vendor
es cache status
es cache prune
es schema validate payload.json --schema event.schema.json
es generate
es generate --check
es migrate scan .
es migrate replay testdata/semantic-corpus.jsonl --to VERSION
es test
es serve
es doctor
Suggested stable process exit codes:
0 operation successful / expression resolved / ConceptCode valid
1 operational failure
3 expression unknown
4 expression ambiguous
5 ConceptCode invalid
That lets shell users write deterministic pipelines without parsing prose.
Local development mode
es serve should be a major feature:
es serve --lock ./embedded-semantics.lock
Embedded Semantics local server
Registry: public
Version: 2026-08-23.1
Digest: sha256:8a42...
API: http://127.0.0.1:8765/v1
It exposes the same REST API from the downloaded package:
POST /v1/resolve
POST /v1/validate
GET /v1/concepts/{code}
No credentials are required when bound to loopback.
Applications switch via one variable:
ES_BASE_URL=http://127.0.0.1:8765
This is crucial for:
- deterministic tests;
- air-gapped development;
- CI;
- reproducing customer failures;
- development when the hosted API is unavailable;
- comparing old and new registry releases.
Testing toolkit
Publish a language-neutral conformance corpus.
Example:
{
"schema": "embedded-semantics.conformance.v1",
"registryDigest": "sha256:...",
"cases": [
{
"name": "known English expression",
"request": {
"expression": "stable concept identity",
"language": "en"
},
"expect": {
"status": "resolved",
"conceptCode": "semantic_registry.stable_concept_identity"
}
},
{
"name": "unknown expression",
"request": {
"expression": "not in registry",
"language": "en"
},
"expect": {
"status": "unknown"
}
}
]
}
Every hosted release and every SDK must run exactly this corpus.
The corpus needs deliberate cases for:
| Case | Why |
|---|---|
| Reviewed exact match | Happy path |
| Unknown | Abstention is part of correctness |
| Exact ambiguity | Prevent accidental first-wins logic |
| Multiple languages | Core project promise |
| Wrong language constraint | Language handling |
| Unicode composed/decomposed forms | Matching-algorithm compatibility |
| Deprecated ConceptCode | Lifecycle handling |
| Unknown ConceptCode | Forward/open-world semantics |
| Registry digest mismatch | Pin correctness |
| Corrupt package digest | Supply-chain correctness |
HTTP 429 | Retry behavior |
HTTP 503 | Transient failure behavior |
Unicode cases are especially important because canonical-equivalence and normalization behavior can differ unless explicitly standardized.
SDK testkit
Each language package should provide an in-memory registry:
TypeScript:
const registry = TestRegistry.create({
concepts: [
concept("test.shipping.delayed", {
definition: "A shipment is delayed",
}),
],
expressions: [
expression("shipment late", "en", "test.shipping.delayed"),
],
});
const es = EmbeddedSemantics.fromRegistry(registry);
Then:
expect(
await es.resolve("shipment late", { language: "en" })
).toMatchObject({
status: "resolved",
concept: {
code: "test.shipping.delayed",
},
});
No test should require a live public endpoint unless it is explicitly an integration test.
Upgrade replay testing
This is one of the most important platform features.
An application records representative production inputs without requiring telemetry upload:
{"expression":"shipment late","language":"en"}
{"expression":"damaged package","language":"en"}
{"expression":"entrega retrasada","language":"es"}
Then:
es migrate replay semantic-corpus.jsonl \
--from ./old.lock \
--to ./candidate.lock
Result:
50000 inputs compared
49280 unchanged
410 unknown -> resolved
180 unknown -> ambiguous
120 ambiguous -> resolved
8 resolved -> ambiguous
2 resolved -> different ConceptCode
HIGH-RISK CHANGES: 10
This turns semantic-registry upgrades into testable software changes instead of governance guesswork.
Contract testing
Publish a small Docker/OCI test server eventually, but do not require containers.
Simpler MVP:
es serve --fixture ./registry.fixture.json
Then consumer contract tests can target localhost.
The hosted service should also have a non-production sandbox registry:
registry: sandbox
with intentionally stable fixtures for resolved, ambiguous, deprecated, and unknown behavior.
Never make examples depend exclusively on mutable production data.
IDE/editor tooling
The first layer of IDE support comes “for free” from generated language constants.
Typing:
Concepts.sta...
should autocomplete:
stableConceptIdentity
The second layer should be a small language server, es-lsp, reading the lockfile and local package.
Capabilities:
Hover
semantic_registry.stable_concept_identity
Stable concept identity
Persistent semantic identity...
Registry: public @ 2026-08-23.1
Status: active
Diagnostics
Unknown ConceptCode in pinned registry.
Deprecated ConceptCode; governed replacement is available.
Go to definition
Navigate to the local Concept JSON/virtual registry document.
Completion
Inside supported JSON/YAML schema fields:
{
"conceptCode": "semantic_registry.sta..."
}
Code actions
Show provenance
Resolve selected expression
Replace deprecated ConceptCode
Compare against latest registry
Registry upgrade UI
A developer hovering a changed ConceptCode in a lockfile diff should see whether its definition, expression evidence, or lifecycle changed.
Initial editor support should target VS Code through LSP; JetBrains and other editors can reuse the protocol.
Documentation architecture
Documentation should be task-first:
Quickstart
Resolve text
Handle unknown and ambiguity
Use ConceptCodes
Validate stored codes
Use ConceptCodes in JSON Schema
Pin a registry
Work offline
Upgrade a registry safely
Inspect provenance
Test an integration
Enterprise/private registries
API reference
SDK reference
Registry governance
Research
“Ontology,” “embedding geometry,” “cross-lingual alignment,” and research methodology should not appear before a developer has completed the basic resolver flow unless specifically relevant.
Embedded Semantics already labels its FAQ “answer-first documentation” and clearly separates production behavior from research; the developer portal should extend that discipline.
Quick-start experience
The ideal TypeScript quickstart should fit on one screen.
Install:
npm install @embeddedsemantics/sdk
Resolve:
import { EmbeddedSemantics } from "@embeddedsemantics/sdk";
const es = new EmbeddedSemantics();
const result = await es.resolve("stable concept identity", {
language: "en",
});
switch (result.status) {
case "resolved":
console.log(result.concept.code);
break;
case "unknown":
console.log("No governed meaning is known.");
break;
case "ambiguous":
console.log("More context is required.");
break;
}
Then pin:
npx es registry pin public
And go offline:
const es = await EmbeddedSemantics.offline({
lockFile: "./embedded-semantics.lock",
});
The key DX choice is that the first example includes all three domain outcomes. Unknown and ambiguity must not be buried in a later “error handling” chapter because they are part of the semantic contract, not exceptional failures. Embedded Semantics itself explicitly treats unknown/ambiguity as expected abstention states.
Before publishing that tutorial, however, the deployed public registry must actually contain the starter records. The site presently exposes zero published Concepts despite its 11-Concept prerelease bundle.
Enterprise integration, security, rate limits, and observability
Public authentication strategy
Public immutable registry packages and public Concept GETs should be anonymously accessible and CDN-cacheable.
A developer should be able to:
es registry pull public
before obtaining credentials.
For hosted resolution:
- allow a modest anonymous quota;
- support simple project API keys for server-side developer accounts;
- reserve OAuth for organizations needing identity federation, scopes, and machine-to-machine policy.
Do not force an OAuth ceremony on someone completing the first quickstart.
Enterprise authentication
For private registries, standardize on OAuth 2.0-compatible bearer access with current security practices, rather than inventing a custom signed-token protocol. RFC 9700 is the current OAuth 2.0 security Best Current Practice and updates the older OAuth threat guidance.
Typical service-to-service flow:
CI/backend
-> enterprise authorization server
-> scoped access token
-> private Embedded Semantics API
Suggested scopes:
semantics.resolve
semantics.concepts.read
semantics.registry.read
semantics.registry.admin
semantics.webhooks.manage
High-assurance enterprises may require mutual-TLS OAuth. RFC 8705 defines OAuth client authentication and certificate-bound tokens using mTLS.
Private deployments should publish OAuth protected-resource metadata to simplify client discovery where appropriate; RFC 9728 standardizes that metadata model.
Registry namespaces and private identity
Private registries create the most serious identity-design problem: two registries must not independently define the same bare ConceptCode and let runtime configuration determine what it means.
The platform therefore needs a canonical registry coordinate:
{
"registry": "com.acme.semantics",
"code": "orders.return_authorized"
}
or a globally namespace-owned code:
com.acme.orders.return_authorized
For developer simplicity, a single-registry application may usually manipulate just:
ConceptCode
but federation infrastructure must always know the identity namespace.
The recommended rule:
A registry may only author new ConceptCodes in namespaces it owns.
An enterprise may add private expressions for a public Concept without redefining that Concept:
{
"registry": "com.acme.semantics",
"expression": "green-lit for shipment",
"language": "en",
"mapsTo": {
"registry": "public",
"conceptCode": "example.shipping.approved"
}
}
That is an extension of evidence, not an override of identity.
Federation semantics
Configuration:
registries:
- id: public
endpoint: https://api.example
lock: ./public.lock
- id: com.acme.semantics
endpoint: https://semantics.acme.example
lock: ./acme.lock
Do not resolve by “first registry wins.”
Instead:
expression
↓
exact evidence across active registries
↓
all matching canonical ConceptRefs
Outcomes:
no ConceptRefs -> unknown
one ConceptRef -> resolved
multiple evidence records
for same ConceptRef -> resolved + merged provenance
multiple different ConceptRefs -> ambiguous
This preserves the system's explicit abstention philosophy even across enterprise overlays.
Air-gapped operation
Private registries should support complete operation without the public hosted service:
Public release
↓ mirror approval
Enterprise artifact registry
↓
CI / production / developer machines
OCI/ORAS is a good optional v2 transport because ORAS supports non-container artifact push/pull through OCI infrastructure.
The package itself remains the same logical registry package regardless of whether it arrived by public HTTPS, an enterprise artifact manager, a filesystem share, or OCI.
Rate-limit strategy
Exact registry resolution should be cheap enough that the commercial model should encourage local/offline usage rather than monetizing lookup scarcity.
Separate limit buckets:
metadata/read operations
resolve operations
batch operations
administrative operations
webhook configuration
Package downloads should be CDN-friendly rather than charged as resolver calls.
On throttling:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/problem+json
RFC 6585 defines 429 Too Many Requests, while HTTP Retry-After expresses how long the client should wait.
As of August 23, 2026, the proposed standardized RateLimit and RateLimit-Policy headers are still an IETF Internet-Draft rather than a final RFC; the May 2026 draft defines both headers and is scheduled to expire in November 2026. They can be emitted opportunistically, but SDK correctness should not depend on them until standardized.
SDK retry policy:
429 retry, honor Retry-After
502 / 503 / 504 retry with exponential backoff + jitter
400 / 401 / 403 never automatic retry
409 pin conflict never automatic retry
Because resolve is logically read-only, repeating it is safe from an application-state standpoint. Clients still need bounded retry limits.
Package integrity and signing
MVP:
HTTPS
+ published SHA-256 digest
+ local verification
V2/enterprise:
digest
+ publisher signature
+ enterprise trust policy
OCI mirrors can use existing artifact-signing ecosystems. Cosign supports signing and verification of OCI-stored artifacts.
A lockfile should optionally record signer policy:
{
"registry": "com.acme.semantics",
"digest": "sha256:...",
"trust": {
"requireSignature": true,
"issuer": "https://id.acme.example",
"subject": "semantic-registry-publisher"
}
}
Telemetry and observability
SDKs should support OpenTelemetry hooks without taking a mandatory dependency on an exporter. OpenTelemetry defines instrumentation libraries and tracing attributes as the mechanism for attaching useful operation metadata.
Suggested span:
embedded_semantics.resolve
Safe default attributes:
embedded_semantics.operation = "resolve"
embedded_semantics.resolution.status = "resolved" | "unknown" | "ambiguous"
embedded_semantics.registry.id = "public"
embedded_semantics.registry.version = "2026-08-23.1"
embedded_semantics.cache.hit = true
embedded_semantics.mode = "offline"
server.address = ...
http.response.status_code = ...
Do not include by default:
expression
definition
full candidate list
private Concept labels
authorization token
Expressions may contain customer, medical, legal, financial, or otherwise sensitive business text. OpenTelemetry itself emphasizes that instrumenters are responsible for identifying and protecting sensitive data; the observability framework cannot decide what is sensitive for them.
Even hashing raw expressions should not be a default privacy measure: low-entropy or dictionary-like expressions can sometimes be guessed and rehashed. Prefer aggregate outcome counters.
Useful metrics:
semantic_resolutions_total{status,mode}
semantic_resolution_duration
semantic_registry_cache_hits_total
semantic_registry_package_load_duration
semantic_registry_integrity_failures_total
semantic_http_requests_total{status}
Avoid ConceptCode as a metric label in large registries; it can create unbounded/high-cardinality telemetry.
Every hosted response should return a request correlation identifier:
X-Request-Id: req_01K...
and propagate standard distributed tracing context where present.
Enterprise audit logs
Administrative actions—not ordinary expression text—should have strong audit records:
registry release published
registry release promoted
registry package mirrored
private Concept created
Concept deprecated
expression evidence approved/revoked
webhook created/deleted
trust policy changed
Audit records should identify:
actor
action
registry
previous digest
new digest
timestamp
request/trace ID
This provides enterprise governance without turning every semantic lookup into sensitive audit content.
Adoption obstacles, migration strategy, and platform roadmap
Principal adoption obstacles
The live registry is currently empty. This is the immediate blocker. The site advertises stable Concept resolution but currently reports 0/11 governed bootstrap Concept coverage and 0/55 expression coverage. A developer platform should not launch until the canonical quickstart works against production.
Semantic terminology can leak into everyday programming. Developers will disengage if routine integration asks them to understand “semantic residue,” vector calibration, equivalence evidence, or ontology governance. The solution is not better ontology tutorials; it is making those concepts unnecessary for ordinary API use.
Unknown feels like failure unless the SDK teaches otherwise. The API and quickstarts must normalize the three-way result from the beginning. This is aligned with the project's own “Unknown is a valid result” principle.
Registry upgrades can change behavior without changing code. Lockfiles, digests, diffing, and replay testing turn that hidden dependency into an explicit one.
Generated enums create false safety. A compile-time closed registry type looks attractive but harms forward compatibility. Generate open constants by default and closed sets only on explicit request.
A hosted-only resolver creates reliability and procurement friction. Offline packages eliminate network latency, service outages, data-egress concerns, and many enterprise security objections.
Provenance can overwhelm the core API. Keep a compact provenance pointer in normal resolution and full evidence behind opt-in expansion.
Private registries can destroy global identity if “override” semantics are allowed. Namespace ownership and ambiguity-preserving federation are mandatory.
Experimental semantic retrieval can contaminate the stable product. Preserve a hard namespace and contract boundary until retrieval quality/calibration is production-governed. Embedded Semantics currently makes exactly that production/research distinction.
Migration tooling
The full tool suite should include:
es registry diff
Understands semantic changes.
es migrate scan .
Finds ConceptCodes in source, JSON, YAML, and configured data formats, then checks them against a target release.
es migrate replay corpus.jsonl
Compares resolution behavior across releases.
es migrate deprecated
Produces governed replacements without applying them.
es generate --check
Fails CI when generated selected-Concept bindings are stale.
es registry verify
Fails CI when the package no longer matches the lock digest.
A CI job might be:
steps:
- run: es registry verify
- run: es generate --check
- run: es schema validate fixtures/*.json
- run: es test
A registry-update bot can submit a pull request containing:
embedded-semantics.lock
registry-diff.json
registry-diff.md
semantic-replay.json
generated bindings
That is the right interaction model: a semantic upgrade looks like a dependency upgrade with a domain-aware changelog.
Recommended MVP
The MVP should be aggressively constrained.
| Component | MVP recommendation |
|---|---|
| Deployed registry | Required first: publish the existing governed bootstrap so quickstarts resolve |
| REST | /resolve, /validate, /concepts/{code}, registry-release metadata/package |
| Result model | `resolved |
| Error model | RFC 9457 Problem Details |
| Registry identity | registryId + opaque version + SHA-256 digest |
| Package | Existing pack concept evolved into stable Registry Package v1 |
| Lockfile | embedded-semantics.lock |
| Offline | First-class exact resolver |
| CLI | resolve, validate, registry pull/pin/verify/diff, serve, test |
| OpenAPI | Canonical OAS 3.2 contract |
| JSON Schema | Draft 2020-12 annotations; no custom vocabulary |
| TypeScript | GA SDK |
| Python | GA SDK |
| C# | GA SDK |
| Rust | Preview or GA if maintenance capacity exists |
| Testing | Cross-language conformance package and in-memory fixtures |
| Documentation | Quickstart, handling unknown/ambiguity, pinning, offline |
| Auth | Anonymous public reads; API key hosted resolver |
| GraphQL | No |
| Webhooks | Not required for first MVP |
| OCI | Not required |
| IDE LSP | Not required; generated-code autocomplete is enough initially |
| Experimental candidates | Separate from stable API |
The most important MVP acceptance test is:
fresh developer machine
↓
install one SDK
↓
copy 15-line quickstart
↓
resolve a published expression
↓
receive a stable ConceptCode
↓
pin the registry
↓
run the same call offline
If that cannot happen in roughly five minutes without semantic-domain knowledge, the platform is not ready.
Recommended v2 platform
V2 should deepen operations rather than widen semantic theory.
Add:
ConceptSets. First-class closed subsets for business schemas, generated validation, and carefully safe enum-like code generation.
Change subscriptions. CloudEvents webhooks plus subscription management.
Signed packages and OCI mirroring. Especially for regulated and air-gapped enterprises.
Federated public/private registries. With canonical registry coordinates and ambiguity-preserving resolution.
LSP/editor tooling. Registry-aware hover, completion, deprecation diagnostics, and upgrade previews.
Batch resolution/validation. For data pipelines.
Advanced migration replay. Production-corpus comparison, policy thresholds, machine-readable CI gates.
Provenance graph. Richer evidence browsing without bloating normal resolver responses.
Organization governance APIs. Publishing workflow, approval, promotion, audit.
Read-only GraphQL only if evidence supports it. Particularly for graph exploration; not as an SDK dependency.
Production model-backed candidate resolution only after it meets the project's own quality/calibration gates. When that happens, preserve exact-first authority and explicit abstention rather than retrofitting probabilistic guesses into the exact resolver. The current project documentation explicitly says unseen-query retrieval and confidence calibration remain experimental.
Recommended platform architecture
The overall system should converge on:
┌────────────────────────┐
│ Registry governance │
│ review / publish │
└───────────┬────────────┘
│
immutable release
│
┌──────────────────▼──────────────────┐
│ Canonical Registry Package │
│ version + digest + provenance │
└───────┬───────────────┬────────────┘
│ │
public HTTPS enterprise mirror
│ │
┌─────────────▼───┐ ┌──▼──────────────┐
│ Hosted REST API │ │ OCI/filesystem │
└──────┬──────────┘ └──┬──────────────┘
│ │
┌──────────▼─────────────────────▼──────────┐
│ Common SDK domain model │
│ ConceptCode / Resolution / RegistryPin │
└───────┬──────────────────┬───────────────┘
│ │
RemoteResolver OfflineResolver
│ │
└────────┬─────────┘
│
Application
The architectural invariant is:
same registry digest
+ same resolver mode
+ same request
= same semantic result
That invariant should be tested across every language SDK.
Prototype-ready OpenAPI fragment
The following is intentionally compact but sufficient for another team to prototype the core API:
openapi: 3.2.0
info:
title: Embedded Semantics API
version: 1.0.0
servers:
- url: https://api.embeddedsemantics.com
paths:
/v1/resolve:
post:
operationId: resolveExpression
summary: Resolve a governed expression
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ResolveRequest'
responses:
'200':
description: Resolution completed
content:
application/json:
schema:
$ref: '#/components/schemas/Resolution'
'400':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'503':
$ref: '#/components/responses/Problem'
/v1/validate:
post:
operationId: validateConceptCode
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ValidateRequest'
responses:
'200':
description: Validation completed
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationResult'
'400':
$ref: '#/components/responses/Problem'
/v1/concepts/{conceptCode}:
get:
operationId: getConcept
parameters:
- name: conceptCode
in: path
required: true
schema:
type: string
- name: registryVersion
in: query
required: false
schema:
type: string
responses:
'200':
description: Concept found
content:
application/json:
schema:
$ref: '#/components/schemas/Concept'
'404':
$ref: '#/components/responses/Problem'
/v1/registries/{registryId}:
get:
operationId: getRegistry
parameters:
- name: registryId
in: path
required: true
schema:
type: string
responses:
'200':
description: Registry metadata
content:
application/json:
schema:
$ref: '#/components/schemas/RegistryRelease'
components:
schemas:
RegistryPin:
type: object
properties:
id:
type: string
version:
type: string
digest:
type: string
RegistryCoordinate:
type: object
required:
- id
- version
- digest
properties:
id:
type: string
version:
type: string
digest:
type: string
ResolveRequest:
type: object
required:
- expression
properties:
expression:
type: string
minLength: 1
language:
type: string
registry:
$ref: '#/components/schemas/RegistryPin'
include:
type: array
items:
enum:
- provenance
- evidence
Concept:
type: object
required:
- code
- definition
- lifecycle
properties:
code:
type: string
display:
type:
- string
- 'null'
definition:
type: string
lifecycle:
enum:
- active
- deprecated
- retired
MatchInfo:
type: object
required:
- method
properties:
method:
enum:
- reviewed_exact
evidenceId:
type: string
ConceptCandidate:
type: object
required:
- conceptCode
properties:
conceptCode:
type: string
display:
type:
- string
- 'null'
ResolvedResolution:
type: object
required:
- status
- concept
- registry
properties:
status:
const: resolved
concept:
$ref: '#/components/schemas/Concept'
match:
$ref: '#/components/schemas/MatchInfo'
registry:
$ref: '#/components/schemas/RegistryCoordinate'
UnknownResolution:
type: object
required:
- status
- reason
- registry
properties:
status:
const: unknown
reason:
const: unknown_expression
registry:
$ref: '#/components/schemas/RegistryCoordinate'
AmbiguousResolution:
type: object
required:
- status
- reason
- candidates
- registry
properties:
status:
const: ambiguous
reason:
const: ambiguous_expression
candidates:
type: array
minItems: 2
items:
$ref: '#/components/schemas/ConceptCandidate'
registry:
$ref: '#/components/schemas/RegistryCoordinate'
Resolution:
oneOf:
- $ref: '#/components/schemas/ResolvedResolution'
- $ref: '#/components/schemas/UnknownResolution'
- $ref: '#/components/schemas/AmbiguousResolution'
discriminator:
propertyName: status
ValidateRequest:
type: object
required:
- conceptCode
properties:
conceptCode:
type: string
registry:
$ref: '#/components/schemas/RegistryPin'
ValidationResult:
type: object
required:
- valid
properties:
valid:
type: boolean
reason:
enum:
- unknown_concept_code
- invalid_concept_code_syntax
- deprecated_concept
conceptCode:
type: string
registry:
$ref: '#/components/schemas/RegistryCoordinate'
RegistryRelease:
type: object
required:
- id
- version
- digest
properties:
id:
type: string
version:
type: string
digest:
type: string
packageSchema:
type: string
Problem:
type: object
required:
- type
- title
- status
properties:
type:
type: string
format: uri
title:
type: string
status:
type: integer
detail:
type: string
code:
type: string
requestId:
type: string
responses:
Problem:
description: API error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
OpenAPI is explicitly designed to describe HTTP interfaces in a machine-readable, language-neutral form, making this specification appropriate as the source for generated transport models and API documentation.
Prototype server behavior
A prototype implementation can use a tiny in-memory index:
key:
(registryDigest, language, exactMatchKey)
value:
[ConceptCode, ...]
Pseudocode:
resolve(expression, language, pin):
registry = load_registry(pin)
key = registry.matching.compute_key(
expression,
language
)
matches = registry.expression_index[key]
if matches is empty:
return Unknown(
reason = "unknown_expression",
registry = registry.coordinate
)
uniqueConcepts = distinct(matches.conceptCode)
if uniqueConcepts.count > 1:
return Ambiguous(
candidates = lookup(uniqueConcepts),
registry = registry.coordinate
)
concept = registry.concepts[uniqueConcepts[0]]
return Resolved(
concept = concept,
match = reviewed_exact,
registry = registry.coordinate
)
Critically, there is no embedding call in this production algorithm. That matches the project's currently declared production boundary.
Definition of platform success
The public platform succeeds when an ordinary developer can treat semantic identity as:
a stable string ID
+ a resolver
+ a validator
+ a pinned data dependency
and can ignore everything else until they need it.
The final product experience should therefore feel closer to:
const result = await semantics.resolve(userText);
than to:
configure ontology
choose embedding model
select similarity metric
tune threshold
interpret nearest neighbors
decide whether score constitutes identity
That is not merely a DX simplification. It preserves Embedded Semantics' own architectural premise: model representations may change, while governed Concept identity remains stable.
Bibliography
Embedded Semantics. Stable concept identity across languages. Defines registry-backed multilingual identity, stable ConceptCodes, reviewed exact resolution, vectors as evidence rather than authority, and the current production architecture.
Embedded Semantics. Registry & Runtime Status and machine-readable status. Documents the August 23, 2026 deployment state: zero published Concepts, zero reviewed expressions, 0/11 bootstrap Concept coverage, 0/55 expression coverage, exact registry-first resolver mode, and inactive semantic runtime.
Embedded Semantics. Public Registry Bundle Manifest. Publishes the deterministic prerelease bundle containing 11 Concepts and 55 expressions, bundle/source-pack SHA-256 digests, and heterogeneous release labels.
Embedded Semantics. Research & Methodology. Defines exact production resolution, experimental unseen-query retrieval, abstention, evaluation boundaries, and independent provenance tracking.
Embedded Semantics. Frequently Asked Questions. Documents unknown_expression, ambiguous_expression, multilingual ConceptCode identity, and abstention rather than guessing.
Embedded Semantics. llms.txt. Lists the public Concept API, status API, Registry Pack JSON Schema, registry packs, resolver endpoint, and the project's authority/evidence distinction.
IETF, RFC 9457. Problem Details for HTTP APIs. Basis for the proposed transport error format.
OpenAPI Initiative. OpenAPI Specification 3.2.0. Basis for the canonical HTTP contract, generated clients, JSON Schema-aligned models, and webhook description.
JSON Schema. Draft 2020-12 Validation and enum documentation. Basis for schema integration, the distinction between format annotation/assertion, and the recommendation that enums represent only closed ConceptSets.
IETF, RFC 9110 and RFC 9111. HTTP Semantics and HTTP Caching. Basis for validators, conditional requests, cache revalidation, and Retry-After.
IETF, RFC 9530. Digest Fields. Basis for optional HTTP content/representation integrity metadata on registry packages.
IETF, RFC 6585. Additional HTTP Status Codes. Defines 429 Too Many Requests.
IETF HTTPAPI Working Group. RateLimit header fields for HTTP, draft revision 11, May 2026. Defines proposed RateLimit and RateLimit-Policy fields; still an Internet-Draft as of the report date.
IETF, RFC 9700. Best Current Practice for OAuth 2.0 Security. Basis for private/enterprise OAuth deployment recommendations.
IETF, RFC 8705. OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens. Basis for optional high-assurance enterprise authentication.
IETF, RFC 9728. OAuth 2.0 Protected Resource Metadata. Basis for OAuth client discovery around private registries.
IETF, RFC 9421. HTTP Message Signatures. Basis for an interoperable future webhook-signature option.
Cloud Native Computing Foundation. CloudEvents Specification. Basis for registry-update event envelopes.
Unicode Consortium. Unicode Standard Annex #15: Unicode Normalization Forms. Basis for keeping Unicode matching rules in the registry contract rather than allowing each SDK to improvise normalization.
IETF, BCP 47 / RFC 5646. Tags for Identifying Languages. Basis for standardized language-tag representation.
HL7 FHIR R5. Terminology Service, CodeSystem, and ValueSet. Provides mature terminology-system precedents for code validation and separating a code system from contextual sets of allowed codes.
ORAS Project. OCI Registry As Storage. Basis for optional enterprise distribution of registry packages as non-container OCI artifacts.
Sigstore. Cosign signing documentation. Basis for optional signed OCI registry packages.
Microsoft. C# enumeration types. Establishes C# enums as integral named-constant types intended for fixed sets, supporting the recommendation to use string-backed ConceptCode value types instead.
TypeScript. Handbook: Enums and as const enum-like patterns. Supports using branded strings and constant objects rather than global runtime enums.
Python Software Foundation. typing.NewType. Supports a low-overhead, statically distinct ConceptCode string type.
Rust Project and Serde. New Type Idiom and #[serde(transparent)]. Supports an open-world string-backed ConceptCode newtype with wire-compatible serialization.
OpenTelemetry. Overview, tracing, and handling sensitive data. Basis for optional SDK observability and the recommendation not to capture raw expressions in default telemetry.