<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title></title>
        <description>Open Source API Design and Registry.</description>
        <link></link>
        <atom:link href="https://www.apicur.io/feed.xml" rel="self" type="application/rss+xml" /><item>
            <title>Beyond OpenShift: Making Apicurio Registry Work with Any OAuth Provider via Dex</title><description>&lt;p&gt;In a &lt;a href=&quot;/blog/2026/03/17/openshift-oauth-integration&quot;&gt;previous post&lt;/a&gt;, we showed how to connect Apicurio Registry directly to OpenShift’s built-in OAuth server. It worked for API-level authentication and role-based authorization, but three important features were broken: UI login, principal identity (artifact ownership), and owner-based authorization. And it only worked on OpenShift.&lt;/p&gt;

&lt;p&gt;We wanted to do better. We wanted a solution that works with &lt;strong&gt;any&lt;/strong&gt; OAuth provider — OpenShift, LDAP, GitHub, Azure AD, SAML, you name it — while unlocking the full set of Apicurio Registry features. This post covers how we got there using &lt;a href=&quot;https://dexidp.io/&quot;&gt;Dex&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;where-we-left-off&quot;&gt;Where We Left Off&lt;/h1&gt;

&lt;p&gt;The direct OpenShift integration was a clever hack: we pointed Quarkus OIDC’s userinfo endpoint at the Kubernetes User API, which validated opaque tokens and returned group membership. But OpenShift’s OAuth server is not OIDC-compliant, and that caused three hard problems:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Feature&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Direct Integration&lt;/th&gt;
      &lt;th&gt;Why It Breaks&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;UI login&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;No&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidc-client-ts&lt;/code&gt; requires &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/.well-known/openid-configuration&lt;/code&gt;, which OpenShift doesn’t serve&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Principal identity&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;No&lt;/td&gt;
      &lt;td&gt;Username is nested in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metadata.name&lt;/code&gt;; Quarkus only reads top-level fields from UserInfo&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Owner-based authz&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;No&lt;/td&gt;
      &lt;td&gt;Depends on principal identity&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;These aren’t minor gaps. Without UI login, your users can’t access the Registry through a browser. Without principal identity, every artifact shows &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;owner: &quot;&quot;&lt;/code&gt; — you can’t track who created what. And without owner-based authorization, you can’t enforce policies like “only the creator can modify this artifact.”&lt;/p&gt;

&lt;p&gt;We needed a different approach.&lt;/p&gt;

&lt;h1 id=&quot;enter-dex-a-federated-oidc-bridge&quot;&gt;Enter Dex: A Federated OIDC Bridge&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://dexidp.io/&quot;&gt;Dex&lt;/a&gt; is an identity service that speaks standard OIDC on the front end and connects to upstream identity providers via pluggable &lt;strong&gt;connectors&lt;/strong&gt;. It acts as a protocol translator — your application talks to Dex using standard OIDC, and Dex handles the quirks of whatever identity provider you actually have.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                     OIDC (standard)              Upstream protocol
User --&amp;gt; Apicurio ----------------------&amp;gt; Dex --------------------------&amp;gt; OpenShift / LDAP / GitHub / etc.
              |                            |
              |  /.well-known/...    OK    |  Connector handles
              |  JWT tokens          OK    |  protocol translation
              |  JWKS                OK    |
              |  Flat claims         OK    |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the key insight: &lt;strong&gt;instead of trying to make Apicurio Registry speak every non-standard OAuth dialect, we put a standards-compliant OIDC layer in front of whatever you already have.&lt;/strong&gt; Dex handles the translation, and Apicurio just talks OIDC like it was designed to.&lt;/p&gt;

&lt;h1 id=&quot;what-this-unlocks&quot;&gt;What This Unlocks&lt;/h1&gt;

&lt;p&gt;With Dex in the middle, here’s what changes:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Feature&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Direct OpenShift OAuth&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Via Dex&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Token validation&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Anonymous reads&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Role-based authorization&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;UI login flow&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Principal identity&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Owner-based authorization&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Multi-provider federation&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;No&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Standard OIDC compliance&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;No&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Client credentials grant (M2M)&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;N/A&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;Partial&lt;/strong&gt; — works in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;, no groups in tokens (see below)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Every user-facing feature that was broken now works. And as a bonus, you get multi-provider federation — Dex can authenticate against multiple upstream providers simultaneously.&lt;/p&gt;

&lt;h1 id=&quot;how-to-set-it-up&quot;&gt;How to Set It Up&lt;/h1&gt;

&lt;h2 id=&quot;deploy-dex&quot;&gt;Deploy Dex&lt;/h2&gt;

&lt;p&gt;The fastest way is via Helm:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm repo add dex https://charts.dexidp.io
helm repo update
kubectl create namespace dex
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dex-values.yaml&lt;/code&gt; with your configuration:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://dex.&amp;lt;cluster-domain&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubernetes&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;inCluster&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0:5556&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# CORS: Required so the Apicurio UI (oidc-client-ts) can fetch&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# /.well-known/openid-configuration from a different origin.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;allowedOrigins&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://&amp;lt;registry-ui-route&amp;gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;oauth2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;skipApprovalScreen&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;responseTypes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;id_token&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Two clients are needed:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# - A PUBLIC client for the browser UI (oidc-client-ts cannot hold secrets)&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# - A CONFIDENTIAL client for CLI/API token exchange&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;staticClients&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry-ui&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Apicurio Registry UI&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;redirectURIs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://&amp;lt;registry-ui-route&amp;gt;/&quot;&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://&amp;lt;registry-ui-route&amp;gt;/dashboard&quot;&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Apicurio Registry API&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;GENERATE_A_SECURE_SECRET&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;redirectURIs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://&amp;lt;registry-ui-route&amp;gt;/&quot;&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://&amp;lt;registry-app-route&amp;gt;/&quot;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;connectors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# We&apos;ll configure these next&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dex.&amp;lt;cluster-domain&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;pathType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Prefix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dex dex/dex &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; dex &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; dex-values.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On OpenShift, create a Route instead of using Ingress:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc create route edge dex &lt;span class=&quot;nt&quot;&gt;--service&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dex &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5556 &lt;span class=&quot;nt&quot;&gt;--hostname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dex.&amp;lt;cluster-domain&amp;gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dex
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify OIDC discovery works:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-sk&lt;/span&gt; https://dex.&amp;lt;cluster-domain&amp;gt;/.well-known/openid-configuration | jq .issuer
&lt;span class=&quot;c&quot;&gt;# Expected: &quot;https://dex.&amp;lt;cluster-domain&amp;gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configure-a-connector&quot;&gt;Configure a Connector&lt;/h2&gt;

&lt;p&gt;This is where Dex becomes powerful — you pick the connector that matches your identity provider. Here are the most common options:&lt;/p&gt;

&lt;h3 id=&quot;openshift&quot;&gt;OpenShift&lt;/h3&gt;

&lt;p&gt;First, create an OAuthClient for Dex (not for Apicurio — Dex is the OAuth client now):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;openssl rand &lt;span class=&quot;nt&quot;&gt;-base64&lt;/span&gt; 32 | &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;=&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 32&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; | kubectl apply -f -
apiVersion: oauth.openshift.io/v1
kind: OAuthClient
metadata:
  name: dex
grantMethod: auto
secret: &quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CLIENT_SECRET&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
redirectURIs:
  - &quot;https://dex.&amp;lt;cluster-domain&amp;gt;/callback&quot;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then add the connector:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;connectors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openshift&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openshift&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;OpenShift&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://api.&amp;lt;cluster-domain&amp;gt;:6443&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;clientID&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dex&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;clientSecret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;CLIENT_SECRET&amp;gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;redirectURI&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://dex.&amp;lt;cluster-domain&amp;gt;/callback&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-admins&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-developers&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-readers&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;insecureCA&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;ldap&quot;&gt;LDAP&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;connectors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ldap&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ldap&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;LDAP&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ldap.example.com:636&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;bindDN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cn=admin,dc=example,dc=com&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;bindPW&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;bind-password&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;userSearch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;baseDN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ou=users,dc=example,dc=com&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(objectClass=person)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;uid&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;idAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;uid&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;emailAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mail&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nameAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cn&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groupSearch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;baseDN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ou=groups,dc=example,dc=com&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(objectClass=groupOfNames)&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;userMatchers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;userAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;DN&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;groupAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;member&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nameAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;github&quot;&gt;GitHub&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;connectors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;github&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;github&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GitHub&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;clientID&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;github-oauth-app-client-id&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;clientSecret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;github-oauth-app-client-secret&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;redirectURI&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://dex.&amp;lt;cluster-domain&amp;gt;/callback&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;orgs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Apicurio&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-admins&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-developers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;microsoft--azure-ad&quot;&gt;Microsoft / Azure AD&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;connectors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;microsoft&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;microsoft&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;clientID&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;azure-app-client-id&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;clientSecret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;azure-app-client-secret&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;redirectURI&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://dex.&amp;lt;cluster-domain&amp;gt;/callback&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;tenant&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;azure-tenant-id&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-admins-group-uuid&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-developers-group-uuid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;saml-20&quot;&gt;SAML 2.0&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;connectors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;saml&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;saml&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Corporate SSO&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ssoURL&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://idp.example.com/sso&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ca&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/dex/saml-ca.crt&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;redirectURI&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://dex.&amp;lt;cluster-domain&amp;gt;/callback&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;usernameAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;emailAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;email&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groupsAttr&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;groups&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After adding your connector, upgrade the release:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm upgrade dex dex/dex &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; dex &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; dex-values.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;deploy-apicurio-registry&quot;&gt;Deploy Apicurio Registry&lt;/h2&gt;

&lt;p&gt;Now the Registry CR becomes much simpler — no more OIDC workarounds, just a standard OIDC configuration pointing at Dex:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry.apicur.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ApicurioRegistry3&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry-app-apicurio-registry.apps.&amp;lt;cluster-domain&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# On OpenShift, this annotation enables edge TLS termination on the Route,&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# so the app is served over HTTPS with automatic HTTP -&amp;gt; HTTPS redirect.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;route.openshift.io/termination&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;edge&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Both must point to the PUBLIC Dex client so the backend accepts&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# tokens with aud=apicurio-registry-ui issued by the browser flow.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;appClientId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry-ui&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uiClientId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry-ui&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;authServerUrl&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://dex.&amp;lt;cluster-domain&amp;gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Explicit redirect URI prevents oidc-client-ts from using the current&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# page URL (which may contain stale ?code=...&amp;amp;state=... query params),&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# fixing silent token refresh failures.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;redirectUri&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://&amp;lt;registry-ui-route&amp;gt;/&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;anonymousReadsEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;tlsVerificationType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;none&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;authz&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;readAccessEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ownerOnlyEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# This WORKS now!&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;groupAccessEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-admins&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;developer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-developers&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-readers&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;adminOverride&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;role&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-admins&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_AUTHENTICATION_SCOPES&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;openid,email,profile,groups&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;groups&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_TOKEN_PRINCIPAL_CLAIM&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;email&quot;&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# The UI scope defaults to &quot;openid profile email&quot; which does NOT include&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# groups. Without this, Dex won&apos;t include group membership in the token&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# and RBAC will deny all write operations.&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;APICURIO_UI_AUTH_OIDC_SCOPE&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;openid&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;groups&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry-ui-apicurio-registry.apps.&amp;lt;cluster-domain&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;route.openshift.io/termination&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;edge&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# The operator hardcodes http:// for REGISTRY_API_URL.&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Override it to use HTTPS since we enabled edge TLS on the app route.&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;REGISTRY_API_URL&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://apicurio-registry-app-apicurio-registry.apps.&amp;lt;cluster-domain&amp;gt;/apis/registry/v3&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are a few important details in this CR worth calling out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two-client architecture&lt;/strong&gt; — Both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appClientId&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uiClientId&lt;/code&gt; are set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry-ui&lt;/code&gt; (the public Dex client). The browser-based UI uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidc-client-ts&lt;/code&gt;, which cannot securely hold a client secret. If you use a confidential client for the UI, the token exchange will fail with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Invalid client credentials&lt;/code&gt;. The backend validates JWT signatures via the JWKS endpoint and doesn’t need a client secret. The separate confidential client (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry&lt;/code&gt;) is kept for CLI/API token exchange via curl or scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI scope with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt;&lt;/strong&gt; — The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APICURIO_UI_AUTH_OIDC_SCOPE&lt;/code&gt; env var adds &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; to the scope the UI requests from Dex. Without this, the default scope (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openid profile email&lt;/code&gt;) doesn’t include groups, and Dex won’t put group membership in the token. RBAC then denies all write operations because it can’t determine the user’s role. We verified this on a live cluster with Dex v2.45.1 and the OpenShift connector: tokens include the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; claim with the user’s OpenShift group memberships, and Apicurio successfully resolves roles (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-admins&lt;/code&gt;) for RBAC authorization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirectUri&lt;/code&gt;&lt;/strong&gt; — Without this, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidc-client-ts&lt;/code&gt; uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location.href&lt;/code&gt; as the redirect URI for silent token refresh. After the initial login, the URL may still contain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?code=...&amp;amp;state=...&lt;/code&gt; query parameters, which don’t match any registered redirect URI in Dex, causing a 400 error.&lt;/p&gt;

&lt;p&gt;Notice what’s &lt;strong&gt;not&lt;/strong&gt; here compared to the direct OpenShift integration: no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_DISCOVERY_ENABLED=false&lt;/code&gt;, no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_JWKS_PATH&lt;/code&gt; pointing at the K8s API server, no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_TOKEN_VERIFY_ACCESS_TOKEN_WITH_USER_INFO&lt;/code&gt;. All of those workarounds are gone because Dex is a proper OIDC provider that supports standard discovery.&lt;/p&gt;

&lt;p&gt;And notice what’s &lt;strong&gt;new&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ownerOnlyEnabled: true&lt;/code&gt; — owner-based authorization is enabled because principal identity works. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_TOKEN_PRINCIPAL_CLAIM&lt;/code&gt; is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email&lt;/code&gt;, which Dex populates as a flat top-level claim in the JWT. No more nested JSON issues.&lt;/p&gt;

&lt;h2 id=&quot;verify-everything-works&quot;&gt;Verify Everything Works&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# OIDC discovery&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-sk&lt;/span&gt; https://dex.&amp;lt;cluster-domain&amp;gt;/.well-known/openid-configuration | jq .issuer

&lt;span class=&quot;c&quot;&gt;# Anonymous read&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-sk&lt;/span&gt; https://&amp;lt;registry-app-route&amp;gt;/apis/registry/v3/system/info

&lt;span class=&quot;c&quot;&gt;# UI login — open in browser, click Login, authenticate via your upstream provider&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# https://&amp;lt;registry-ui-route&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Check that artifacts have an owner (principal identity works!)&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-sk&lt;/span&gt; https://&amp;lt;registry-app-route&amp;gt;/apis/registry/v3/groups/my-group &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TOKEN&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | jq .owner
&lt;span class=&quot;c&quot;&gt;# Expected: the user&apos;s email — NOT empty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;machine-to-machine-m2m-access-and-kafka-serdes&quot;&gt;Machine-to-Machine (M2M) Access and Kafka SerDes&lt;/h1&gt;

&lt;p&gt;So far we’ve focused on the browser-based UI flow, but what about CI/CD pipelines, Kafka SerDes, and services that need to interact with the Registry API without a browser?&lt;/p&gt;

&lt;p&gt;This is a critical question because &lt;strong&gt;the Apicurio SerDes libraries only support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; grant&lt;/strong&gt; — they hardcode &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grant_type=client_credentials&lt;/code&gt; with no alternative. Every Kafka producer or consumer using Apicurio SerDes to fetch or register schemas needs this grant type to work.&lt;/p&gt;

&lt;h2 id=&quot;dex-client_credentials-support-unreleased-as-of-v2451&quot;&gt;Dex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; Support (Unreleased as of v2.45.1)&lt;/h2&gt;

&lt;p&gt;Dex added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; support via &lt;a href=&quot;https://github.com/dexidp/dex/pull/4583&quot;&gt;PR #4583&lt;/a&gt; (merged March 3, 2026), but this feature is &lt;strong&gt;not included in Dex v2.45.1&lt;/strong&gt; — the latest stable release at time of writing. It will ship in v2.46.0 or later.&lt;/p&gt;

&lt;p&gt;We tested both versions on a live OpenShift cluster:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Dex v2.45.1&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{&quot;error&quot;:&quot;unsupported_grant_type&quot;}&lt;/code&gt; regardless of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DEX_CLIENT_CREDENTIAL_GRANT_ENABLED_BY_DEFAULT&lt;/code&gt; env var. The feature flag code doesn’t exist in this version.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Dex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; snapshot&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker.io/dexidp/dex:master&lt;/code&gt;): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; &lt;strong&gt;works&lt;/strong&gt;. Tokens are issued, and the grant type appears in the OIDC discovery endpoint. However, tokens &lt;strong&gt;do not include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; claims&lt;/strong&gt; — writes to the Registry API return 403 because RBAC can’t determine the user’s role.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To test before a stable release, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dexidp/dex:master&lt;/code&gt; image from Docker Hub (rebuilt daily by Dex CI). For production, wait for v2.46.0+.&lt;/p&gt;

&lt;p&gt;Enable it by setting an environment variable on the Dex deployment:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# In the Dex Deployment spec (requires Dex master or &amp;gt;= v2.46.0)&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;DEX_CLIENT_CREDENTIAL_GRANT_ENABLED_BY_DEFAULT&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then use the confidential client:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN_RESPONSE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;https://dex.&amp;lt;cluster-domain&amp;gt;/token&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;grant_type=client_credentials&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_id=apicurio-registry&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_secret=&amp;lt;CLIENT_SECRET&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;scope=openid profile&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;ACCESS_TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TOKEN_RESPONSE&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; .access_token&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-catch-no-groups-in-client-credentials-tokens&quot;&gt;The Catch: No Groups in Client Credentials Tokens&lt;/h2&gt;

&lt;p&gt;Even with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; working, the tokens &lt;strong&gt;do not include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; claims&lt;/strong&gt; — even when the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; scope is explicitly requested. The PR description says it “supports &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; scope”, but the scope is merely &lt;em&gt;accepted&lt;/em&gt; (not rejected) — no groups are populated because there’s no upstream connector or user in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; flow. The token is built solely from the static client definition, which has no group membership.&lt;/p&gt;

&lt;p&gt;Since Apicurio Registry uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; for RBAC (determining admin/developer/reader roles), M2M clients authenticating via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; will have &lt;strong&gt;no assigned role&lt;/strong&gt; (principal is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, roles are empty).&lt;/p&gt;

&lt;p&gt;What this means in practice:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Scenario&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Works?&lt;/th&gt;
      &lt;th&gt;Why&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;SerDes &lt;strong&gt;reading&lt;/strong&gt; schemas (with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anonymousReadsEnabled: true&lt;/code&gt;)&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Yes&lt;/td&gt;
      &lt;td&gt;Anonymous reads bypass auth entirely&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SerDes &lt;strong&gt;registering&lt;/strong&gt; schemas&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;No role in token = write denied&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CI/CD creating artifacts&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;No role in token = write denied&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;what-actually-works-for-kafka-serdes&quot;&gt;What Actually Works for Kafka SerDes&lt;/h2&gt;

&lt;p&gt;For the most common Kafka use case — &lt;strong&gt;consumers fetching schemas&lt;/strong&gt; — you don’t need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; at all. Set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anonymousReadsEnabled: true&lt;/code&gt; in the Apicurio CR and the SerDes can read schemas without any token. This is the recommended approach for read-heavy workloads.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;schema registration&lt;/strong&gt; (typically done by developers or CI/CD, not by Kafka producers at runtime), use the authorization code flow with the confidential client and a service user that has the right groups:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Non-interactive token acquisition using OpenShift challenge-based auth&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# (See the full guide for the complete 6-step script)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;TOKEN_RESPONSE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;https://dex.&amp;lt;cluster-domain&amp;gt;/token&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;grant_type=authorization_code&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;code=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEX_CODE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_id=apicurio-registry&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_secret=&amp;lt;CLIENT_SECRET&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-urlencode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redirect_uri=https://&amp;lt;registry-ui-route&amp;gt;/&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# This token WILL have groups because it went through the full user auth flow&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;ACCESS_TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TOKEN_RESPONSE&lt;/span&gt; | jq &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; .access_token&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;upcoming-fix-groups-on-static-clients&quot;&gt;Upcoming Fix: Groups on Static Clients&lt;/h2&gt;

&lt;p&gt;We opened &lt;a href=&quot;https://github.com/dexidp/dex/issues/4690&quot;&gt;dexidp/dex#4690&lt;/a&gt; to report this gap and submitted &lt;a href=&quot;https://github.com/dexidp/dex/pull/4691&quot;&gt;dexidp/dex#4691&lt;/a&gt; with a fix. It adds a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clientCredentialsClaims&lt;/code&gt; sub-struct to static client definitions, keeping identity attributes separate from core client fields:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;staticClients&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;...&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;clientCredentialsClaims&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-admins&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# included in client_credentials tokens&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We deployed a patched build and verified it on a live cluster: tokens now include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;groups&quot;: [&quot;registry-admins&quot;]&lt;/code&gt; and Registry writes succeed (200 instead of 403). Track the PR for when this lands in a stable release.&lt;/p&gt;

&lt;h2 id=&quot;when-to-use-keycloak-instead&quot;&gt;When to Use Keycloak Instead&lt;/h2&gt;

&lt;p&gt;If your deployment requires M2M &lt;strong&gt;write access&lt;/strong&gt; with proper RBAC — for example, if Kafka producers register schemas at runtime, or if you have many CI/CD pipelines that need different permission levels — &lt;strong&gt;use Keycloak (or Auth0, Azure AD) instead of Dex&lt;/strong&gt;. These providers support service account roles natively, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; tokens include all the claims Apicurio needs.&lt;/p&gt;

&lt;p&gt;Dex is the right choice when:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Your upstream IdP is non-OIDC-compliant (OpenShift OAuth, LDAP, SAML)&lt;/li&gt;
  &lt;li&gt;You primarily need user-facing auth (UI login, principal identity, owner-based authz)&lt;/li&gt;
  &lt;li&gt;M2M workloads are read-only (SerDes consumers with anonymous reads)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;group-to-role-mapping&quot;&gt;Group-to-Role Mapping&lt;/h1&gt;

&lt;p&gt;The mapping between upstream provider groups and Apicurio Registry roles flows through two layers:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Upstream Group&lt;/th&gt;
      &lt;th&gt;Dex Passes As&lt;/th&gt;
      &lt;th&gt;CR Role Config&lt;/th&gt;
      &lt;th&gt;Registry Role&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-admins&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-admins&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin: registry-admins&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Admin&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-developers&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-developers&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;developer: registry-developers&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Developer&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-readers&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-readers&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readOnly: registry-readers&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Read-Only&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;For providers that use UUIDs as group identifiers (like Azure AD), the CR supports comma-separated values:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-admins,a1b2c3d4-uuid-of-azure-group&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;production-considerations&quot;&gt;Production Considerations&lt;/h1&gt;

&lt;p&gt;A few things to keep in mind when running this in production:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLS&lt;/strong&gt; — Configure Dex with proper TLS via Ingress termination or its own certificate. Set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tlsVerificationType: &quot;VERIFY_PEER&quot;&lt;/code&gt; in the Apicurio CR and provide a truststore if using a private CA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High availability&lt;/strong&gt; — Run multiple Dex replicas. For better HA, switch Dex’s storage from Kubernetes CRDs to PostgreSQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token lifetimes&lt;/strong&gt; — Configure sensible expiry in Dex:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;expiry&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;idTokens&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1h&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;refreshTokens&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;validIfNotUsedFor&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;168h&quot;&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# 7 days&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;absoluteLifetime&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;720h&quot;&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# 30 days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Network policies&lt;/strong&gt; — Restrict access so only Apicurio Registry pods and the ingress controller can reach Dex.&lt;/p&gt;

&lt;h1 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;“Invalid client credentials” on UI login&lt;/strong&gt; — The UI (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidc-client-ts&lt;/code&gt;) runs in the browser and cannot send a client secret. Use a &lt;strong&gt;public&lt;/strong&gt; Dex client (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public: true&lt;/code&gt;, no secret) for the UI. A confidential client will always fail.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;403 Forbidden on API calls after UI login&lt;/strong&gt; — Two common causes: (1) &lt;strong&gt;Audience mismatch&lt;/strong&gt; — the UI gets tokens with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud: &quot;apicurio-registry-ui&quot;&lt;/code&gt; but the backend expects &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud: &quot;apicurio-registry&quot;&lt;/code&gt;. Fix: set both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appClientId&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uiClientId&lt;/code&gt; to the same public client ID. (2) &lt;strong&gt;Missing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; scope&lt;/strong&gt; — the default UI scope doesn’t include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt;. Fix: set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APICURIO_UI_AUTH_OIDC_SCOPE&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openid profile email groups&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Groups not appearing in the token&lt;/strong&gt; — Verify both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_AUTHENTICATION_SCOPES&lt;/code&gt; (backend) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APICURIO_UI_AUTH_OIDC_SCOPE&lt;/code&gt; (UI) include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt;. Decode the JWT to inspect: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo $TOKEN | cut -d. -f2 | base64 -d | jq .&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;“Unregistered redirect_uri” on page refresh&lt;/strong&gt; — The UI redirects to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dashboard&lt;/code&gt; after login. Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://&amp;lt;registry-ui-route&amp;gt;/dashboard&lt;/code&gt; to the Dex client’s redirect URIs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Silent token refresh returns 400&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidc-client-ts&lt;/code&gt; uses the current page URL (with stale &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?code=...&amp;amp;state=...&lt;/code&gt; params) as the redirect URI. Fix: set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirectUri&lt;/code&gt; explicitly in the Apicurio CR auth config.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;“No end session endpoint” on logout&lt;/strong&gt; — Dex does not implement OIDC RP-Initiated Logout. The local session is still cleared, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;signoutRedirect&lt;/code&gt; fails. This is a known Dex limitation — the error is non-blocking.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;CORS errors in browser console&lt;/strong&gt; — The UI can’t fetch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/.well-known/openid-configuration&lt;/code&gt; from Dex. Add the UI route origin to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.allowedOrigins&lt;/code&gt; in the Dex config.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvalidJwtSignatureException&lt;/code&gt; after Dex restart&lt;/strong&gt; — Dex generates new signing keys on restart. Old tokens become invalid. Users must re-authenticate. For production, configure persistent signing keys.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invalid_client&lt;/code&gt; when exchanging authorization code&lt;/strong&gt; — If the client secret contains special characters (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt;), use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--data-urlencode&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; in curl.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Owner field is still empty&lt;/strong&gt; — Check that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_TOKEN_PRINCIPAL_CLAIM&lt;/code&gt; is set to a claim Dex actually populates (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;UI redirects to Dex but gets an error&lt;/strong&gt; — Check Dex logs (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl logs -n dex deploy/dex&lt;/code&gt;). Usually a redirect URI mismatch or connector misconfiguration.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_credentials&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsupported_grant_type&lt;/code&gt;&lt;/strong&gt; — This grant type was added in Dex PR #4583 (merged March 3, 2026) but is &lt;strong&gt;not included in v2.45.1&lt;/strong&gt; or earlier. You need Dex &amp;gt;= v2.46.0 or a build from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;“OIDC Server is not available”&lt;/strong&gt; — The Registry pod can’t reach Dex. Check DNS resolution, network policies, and TLS trust.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;the-bigger-picture&quot;&gt;The Bigger Picture&lt;/h1&gt;

&lt;p&gt;This approach isn’t just about fixing the three broken features from the direct OpenShift integration. It’s about &lt;strong&gt;decoupling Apicurio Registry from any specific identity provider&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With Dex as an OIDC bridge, your Registry deployment is portable:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Moving from OpenShift to vanilla Kubernetes? Swap the connector, keep everything else.&lt;/li&gt;
  &lt;li&gt;Migrating from LDAP to Azure AD? Same.&lt;/li&gt;
  &lt;li&gt;Need to support multiple identity providers during a transition? Dex handles federation natively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Registry doesn’t need to know or care what’s behind Dex. It just talks OIDC.&lt;/p&gt;

&lt;p&gt;If you’re interested in the direct OpenShift approach (without Dex), check out the &lt;a href=&quot;/blog/2026/03/17/openshift-oauth-integration&quot;&gt;previous post&lt;/a&gt; — it’s still a valid option if you only need API-level auth and role-based authorization. But if you want the full feature set, Dex is the way to go.&lt;/p&gt;

&lt;p&gt;Happy registering!&lt;/p&gt;
</description><category term="registry" /><category term="authentication" /><category term="oauth" /><category term="dex" /><category term="oidc" /><pubDate>Thu, 26 Mar 2026 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2026/03/26/universal-oauth-with-dex</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2026/03/26/universal-oauth-with-dex</guid>
        </item><item>
            <title>Integrating Apicurio Registry with OpenShift&apos;s Built-in OAuth Server</title><description>&lt;p&gt;If you’re running Apicurio Registry on OpenShift, you might be wondering: can I use OpenShift’s built-in OAuth server for authentication instead of deploying a separate identity provider like Keycloak? The answer is &lt;strong&gt;yes&lt;/strong&gt; — with some caveats. In this post, I’ll walk you through how we got it working, what the limitations are, and the technical details behind the integration.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;the-challenge&quot;&gt;The Challenge&lt;/h1&gt;

&lt;p&gt;OpenShift ships with a built-in OAuth server that handles user authentication for the platform. However, it is &lt;strong&gt;not a standard OpenID Connect (OIDC) provider&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; expose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/.well-known/openid-configuration&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;It issues &lt;strong&gt;opaque tokens&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sha256~...&lt;/code&gt;), not JWTs&lt;/li&gt;
  &lt;li&gt;It has &lt;strong&gt;no JWKS endpoint&lt;/strong&gt; and &lt;strong&gt;no RFC 7662 token introspection endpoint&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apicurio Registry uses Quarkus OIDC for authentication, which normally expects a fully OIDC-compliant server. So how do we bridge the gap?&lt;/p&gt;

&lt;h1 id=&quot;the-solution-verify-access-token-with-user-info&quot;&gt;The Solution: verify-access-token-with-user-info&lt;/h1&gt;

&lt;p&gt;The trick is to combine three components:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;JWKS from the Kubernetes API server&lt;/strong&gt; — The K8s API server exposes a JWKS endpoint at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/openid/v1/jwks&lt;/code&gt;. Quarkus OIDC requires either a JWKS or introspection path to initialize. We point it to the K8s JWKS purely to satisfy this startup requirement.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;OpenShift’s User API&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/apis/user.openshift.io/v1/users/~&lt;/code&gt;) — When called with a valid Bearer token, this endpoint returns the authenticated user’s identity including their OpenShift groups. When called with an invalid token, it returns 401.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Quarkus &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;verify-access-token-with-user-info=true&lt;/code&gt;&lt;/strong&gt; — This tells Quarkus to validate opaque access tokens by sending a GET request with the Bearer token to the userinfo endpoint. If the endpoint returns 200, the token is valid; otherwise, it’s rejected.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s the flow:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                          +----------------------+
   Client Request         |  Apicurio Registry   |
   with Bearer token ---&amp;gt; |  (Quarkus OIDC)      |
                          |                      |
                          |  1. Sends GET with    |
                          |     Bearer token to   |
                          |     userinfo endpoint  |
                          +----------+-----------+
                                     |
                                     v
                          +----------------------+
                          |  K8s API Server       |
                          |  /apis/user.openshift |
                          |  .io/v1/users/~       |
                          |                      |
                          |  Valid token -&amp;gt; 200   |
                          |  + user info (with    |
                          |    groups array)      |
                          |                      |
                          |  Invalid token -&amp;gt; 401 |
                          +----------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The User API response includes an OpenShift &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; array at the top level. By creating OpenShift groups that match Apicurio role names, we map group membership directly to Registry roles.&lt;/p&gt;

&lt;h1 id=&quot;step-by-step-setup&quot;&gt;Step-by-Step Setup&lt;/h1&gt;

&lt;h2 id=&quot;1-install-the-apicurio-registry-operator&quot;&gt;1. Install the Apicurio Registry Operator&lt;/h2&gt;

&lt;p&gt;First, deploy the operator to your cluster:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create namespace apicurio-registry-operator
kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; apicurio-registry-operator.yaml &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; apicurio-registry-operator
kubectl &lt;span class=&quot;nb&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;available deployment/apicurio-registry-operator &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; apicurio-registry-operator &lt;span class=&quot;nt&quot;&gt;--timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;300s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-grant-anonymous-access-to-the-jwks-endpoint&quot;&gt;2. Grant Anonymous Access to the JWKS Endpoint&lt;/h2&gt;

&lt;p&gt;The K8s API server JWKS endpoint is restricted by default. Since Quarkus OIDC fetches JWKS without authentication, we need to open it up. The JWKS contains only public keys, so there’s no security risk:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: apicurio-jwks-anonymous-access
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:service-account-issuer-discovery
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:unauthenticated
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-create-an-oauthclient-resource&quot;&gt;3. Create an OAuthClient Resource&lt;/h2&gt;

&lt;p&gt;Register Apicurio Registry as a client of OpenShift’s OAuth server:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;openssl rand &lt;span class=&quot;nt&quot;&gt;-base64&lt;/span&gt; 32 | &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;=&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 32&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Client secret: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CLIENT_SECRET&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;oauth.openshift.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;OAuthClient&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;grantMethod&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;auto&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;CLIENT_SECRET&amp;gt;&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;redirectURIs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://&amp;lt;registry-app-route&amp;gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://&amp;lt;registry-ui-route&amp;gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://&amp;lt;registry-app-route&amp;gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://&amp;lt;registry-ui-route&amp;gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Include both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://&lt;/code&gt; variants in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirectURIs&lt;/code&gt; to avoid 400 errors during OAuth flows.&lt;/p&gt;

&lt;h2 id=&quot;4-create-openshift-groups-for-role-based-authorization&quot;&gt;4. Create OpenShift Groups for Role-Based Authorization&lt;/h2&gt;

&lt;p&gt;Create groups that map to Apicurio Registry roles:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc adm &lt;span class=&quot;nb&quot;&gt;groups &lt;/span&gt;new registry-admins
oc adm &lt;span class=&quot;nb&quot;&gt;groups &lt;/span&gt;new registry-developers
oc adm &lt;span class=&quot;nb&quot;&gt;groups &lt;/span&gt;new registry-readers

oc adm &lt;span class=&quot;nb&quot;&gt;groups &lt;/span&gt;add-users registry-admins &amp;lt;admin-username&amp;gt;
oc adm &lt;span class=&quot;nb&quot;&gt;groups &lt;/span&gt;add-users registry-developers &amp;lt;developer-username&amp;gt;
oc adm &lt;span class=&quot;nb&quot;&gt;groups &lt;/span&gt;add-users registry-readers &amp;lt;reader-username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-deploy-the-registry&quot;&gt;5. Deploy the Registry&lt;/h2&gt;

&lt;p&gt;This is where it all comes together. The CR configures Quarkus OIDC to work with OpenShift’s non-OIDC-compliant OAuth server:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry.apicur.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ApicurioRegistry3&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry-app-apicurio-registry.apps.&amp;lt;cluster-domain&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;appClientId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uiClientId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;authServerUrl&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://oauth-openshift.apps.&amp;lt;cluster-domain&amp;gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;anonymousReadsEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;tlsVerificationType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;none&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;authz&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;readAccessEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ownerOnlyEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;groupAccessEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-admins&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;developer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-developers&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-readers&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;adminOverride&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;role&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry-admins&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Disable OIDC auto-discovery&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_DISCOVERY_ENABLED&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;false&quot;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# OAuth endpoints on the OpenShift OAuth server&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_AUTHORIZATION_PATH&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/oauth/authorize&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_TOKEN_PATH&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/oauth/token&quot;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# K8s API server JWKS (only for Quarkus startup initialization)&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_JWKS_PATH&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://api.&amp;lt;cluster-domain&amp;gt;:6443/openid/v1/jwks&quot;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# OpenShift User API as the userinfo endpoint&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_USER_INFO_PATH&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://api.&amp;lt;cluster-domain&amp;gt;:6443/apis/user.openshift.io/v1/users/~&quot;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# KEY SETTING: validate opaque tokens via the userinfo endpoint&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_TOKEN_VERIFY_ACCESS_TOKEN_WITH_USER_INFO&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# Extract OpenShift groups as roles&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;groups&quot;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# Client credentials&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;QUARKUS_OIDC_CREDENTIALS_SECRET&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;CLIENT_SECRET&amp;gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apicurio-registry-ui-apicurio-registry.apps.&amp;lt;cluster-domain&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls&lt;/code&gt; section under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auth&lt;/code&gt; is required when auth is enabled. Omitting it causes a NullPointerException in the operator.&lt;/p&gt;

&lt;h2 id=&quot;6-verify-it-works&quot;&gt;6. Verify It Works&lt;/h2&gt;

&lt;p&gt;Once the pods are running, you can test the full authorization matrix:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Anonymous read (should work)&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-sk&lt;/span&gt; http://&amp;lt;registry-app-route&amp;gt;/apis/registry/v3/system/info

&lt;span class=&quot;c&quot;&gt;# Unauthenticated write (should return 401)&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-sk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST http://&amp;lt;registry-app-route&amp;gt;/apis/registry/v3/groups &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;groupId&quot;: &quot;test&quot;}&apos;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Admin write (should return 200)&lt;/span&gt;
oc login &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; testadmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; testadmin123
&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc &lt;span class=&quot;nb&quot;&gt;whoami&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--show-token&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-sk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST http://&amp;lt;registry-app-route&amp;gt;/apis/registry/v3/groups &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TOKEN&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;groupId&quot;: &quot;admin-test&quot;}&apos;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Reader write (should return 403)&lt;/span&gt;
oc login &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; testreader &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; testreader123
&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc &lt;span class=&quot;nb&quot;&gt;whoami&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--show-token&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-sk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST http://&amp;lt;registry-app-route&amp;gt;/apis/registry/v3/groups &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TOKEN&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;groupId&quot;: &quot;reader-test&quot;}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;what-works-and-what-doesnt&quot;&gt;What Works and What Doesn’t&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Authentication and role-based authorization work. Owner-based authorization and UI login do not.&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Feature&lt;/th&gt;
      &lt;th&gt;Status&lt;/th&gt;
      &lt;th&gt;Notes&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Token validation&lt;/td&gt;
      &lt;td&gt;Works&lt;/td&gt;
      &lt;td&gt;Valid OpenShift OAuth tokens accepted, invalid tokens rejected&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Anonymous reads&lt;/td&gt;
      &lt;td&gt;Works&lt;/td&gt;
      &lt;td&gt;When &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anonymousReadsEnabled: true&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Auth enforcement&lt;/td&gt;
      &lt;td&gt;Works&lt;/td&gt;
      &lt;td&gt;Unauthenticated writes are blocked with 401&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Role-based authorization&lt;/td&gt;
      &lt;td&gt;Works&lt;/td&gt;
      &lt;td&gt;OpenShift groups mapped to Apicurio roles&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Admin override&lt;/td&gt;
      &lt;td&gt;Works&lt;/td&gt;
      &lt;td&gt;Users in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-admins&lt;/code&gt; get full admin access&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Principal identity&lt;/td&gt;
      &lt;td&gt;Broken&lt;/td&gt;
      &lt;td&gt;Username is in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metadata.name&lt;/code&gt; (nested), but Quarkus only reads top-level fields&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Owner-based authorization&lt;/td&gt;
      &lt;td&gt;Broken&lt;/td&gt;
      &lt;td&gt;Depends on principal identity&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;UI login flow&lt;/td&gt;
      &lt;td&gt;Broken&lt;/td&gt;
      &lt;td&gt;UI requires OIDC discovery, which OpenShift OAuth doesn’t support&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h1 id=&quot;why-principal-identity-doesnt-work&quot;&gt;Why Principal Identity Doesn’t Work&lt;/h1&gt;

&lt;p&gt;This is worth explaining in detail because it reveals an interesting limitation in Quarkus OIDC.&lt;/p&gt;

&lt;p&gt;The K8s User API returns the username nested inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metadata.name&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;kind&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;testadmin&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;groups&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;registry-admins&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;system:authenticated&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For opaque tokens validated via UserInfo, Quarkus extracts the principal with a flat JSON lookup:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;userName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolvedContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;oidcConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;principalClaim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getString(&quot;metadata/name&quot;)&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; — there’s no top-level key with that name. Interestingly, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findClaimValue()&lt;/code&gt; method used for &lt;strong&gt;role&lt;/strong&gt; extraction supports &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt;-separated path traversal, but this method is not used for principal extraction from UserInfo.&lt;/p&gt;

&lt;p&gt;We tested every alternative:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metadata/name&lt;/code&gt; — empty (path not traversed)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metadata.name&lt;/code&gt; — empty (not a valid key)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metadata&lt;/code&gt; — 500 error (JsonObject can’t cast to JsonString)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind&lt;/code&gt; — returns “User” (proves top-level strings work, but useless)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only top-level string fields in the response are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apiVersion&lt;/code&gt;, neither of which contains the username.&lt;/p&gt;

&lt;h1 id=&quot;troubleshooting-tips&quot;&gt;Troubleshooting Tips&lt;/h1&gt;

&lt;p&gt;If you run into issues, here are the most common problems:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;“Either ‘jwks-path’ or ‘introspection-path’ must be set”&lt;/strong&gt; — Add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_JWKS_PATH&lt;/code&gt; env var pointing to the K8s JWKS endpoint.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;“Introspection path and verifyAccessTokenWithUserInfo are mutually exclusive”&lt;/strong&gt; — Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jwks-path&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;introspection-path&lt;/code&gt;. It’s only needed for initialization.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;NullPointerException on TLS&lt;/strong&gt; — Always include the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls&lt;/code&gt; section under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auth&lt;/code&gt; when auth is enabled.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;JWKS 403&lt;/strong&gt; — Create the ClusterRoleBinding for anonymous JWKS access.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;OAuth 400 Bad Request&lt;/strong&gt; — Ensure redirect URIs match exactly, including protocol scheme.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Users don’t get the expected role&lt;/strong&gt; — Verify group membership with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc get groups&lt;/code&gt; and re-authenticate to get a new token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;alternative-oauthinfo-endpoint&quot;&gt;Alternative: /oauth/info Endpoint&lt;/h1&gt;

&lt;p&gt;If you only need pure authentication without role-based authorization, there’s a simpler option. OpenShift’s OAuth server exposes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/oauth/info&lt;/code&gt;, which validates Bearer tokens and returns basic token metadata — but no user identity or groups. Set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_USER_INFO_PATH&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/oauth/info&lt;/code&gt; and skip the K8s User API entirely.&lt;/p&gt;

&lt;h1 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h1&gt;

&lt;p&gt;While this integration has limitations (no principal identity, no UI login, no owner-based authz), it provides a solid foundation for &lt;strong&gt;API-level authentication and role-based authorization&lt;/strong&gt; using only OpenShift’s built-in infrastructure — no external identity provider required. For teams that primarily interact with Apicurio Registry through REST APIs and want to leverage their existing OpenShift user and group management, this is a practical solution.&lt;/p&gt;

&lt;p&gt;We’d love to hear from you if you try this out or have ideas for improving the integration. The Quarkus OIDC limitation around nested principal claims is something we’re looking into upstream as well.&lt;/p&gt;

&lt;p&gt;Happy registering!&lt;/p&gt;
</description><category term="registry" /><category term="openshift" /><category term="oauth" /><category term="authentication" /><category term="authorization" /><pubDate>Tue, 17 Mar 2026 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2026/03/17/openshift-oauth-integration</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2026/03/17/openshift-oauth-integration</guid>
        </item><item>
            <title>Apicurio Registry goes AI-Native: Introducing Agent Registry, MCP Server, and LLM artifact support</title><description>&lt;p&gt;The world of software is undergoing a fundamental shift. AI agents are no longer experimental curiosities—they’re becoming the backbone of modern enterprise workflows. From customer service automation to complex data analysis pipelines, organizations are deploying fleets of specialized AI agents that need to discover each other, communicate reliably, and evolve without breaking.&lt;/p&gt;

&lt;p&gt;Today, we’re excited to announce that &lt;strong&gt;Apicurio Registry is evolving into an AI-native platform&lt;/strong&gt;, becoming the first open-source registry to offer comprehensive support for the AI agent ecosystem.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;why-apicurio-registry-for-ai&quot;&gt;Why Apicurio Registry for AI?&lt;/h1&gt;

&lt;p&gt;For years, Apicurio Registry has been the trusted solution for managing API schemas—OpenAPI, AsyncAPI, Avro, Protobuf, JSON Schema, and more. Organizations rely on it for schema validation, versioning, compatibility enforcement, and governance.&lt;/p&gt;

&lt;p&gt;It turns out these same principles apply directly to AI agents:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Traditional Schema Registry&lt;/th&gt;
      &lt;th&gt;AI Agent Registry&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;API contracts&lt;/td&gt;
      &lt;td&gt;Agent capabilities&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Schema validation&lt;/td&gt;
      &lt;td&gt;Prompt/response validation&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Version compatibility&lt;/td&gt;
      &lt;td&gt;Agent evolution&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Service discovery&lt;/td&gt;
      &lt;td&gt;Agent discovery&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Governance &amp;amp; audit&lt;/td&gt;
      &lt;td&gt;Compliance &amp;amp; traceability&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Rather than building something new, we extended what we already do best. Apicurio Registry now treats AI artifacts—Agent Cards, prompts, and model schemas—as first-class citizens alongside traditional schemas.&lt;/p&gt;

&lt;h1 id=&quot;whats-new-phase-1-complete&quot;&gt;What’s new: Phase 1 complete&lt;/h1&gt;

&lt;p&gt;We’re launching with three major capabilities that are fully implemented and production-ready:&lt;/p&gt;

&lt;h3 id=&quot;1-agent-card-registry-a2a-protocol-support&quot;&gt;1. Agent card Registry (A2A protocol support)&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://a2a-protocol.org/&quot;&gt;A2A (Agent-to-Agent) protocol&lt;/a&gt; defines a standard way for AI agents to describe themselves and discover each other. Apicurio Registry now supports &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AGENT_CARD&lt;/code&gt; as a native artifact type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you can do:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Store and version Agent Cards with full JSON Schema validation&lt;/li&gt;
  &lt;li&gt;Discover agents via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/.well-known/agent.json&lt;/code&gt; endpoints&lt;/li&gt;
  &lt;li&gt;Search agents by capability, skill, or mode&lt;/li&gt;
  &lt;li&gt;Enforce compatibility rules as agents evolve&lt;/li&gt;
  &lt;li&gt;Track changes for compliance and auditing&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Register an agent card&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST http://registry:8080/apis/registry/v3/groups/my-agents/artifacts &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;X-Registry-ArtifactType: AGENT_CARD&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; @my-agent-card.json

&lt;span class=&quot;c&quot;&gt;# Discover agents via A2A protocol&lt;/span&gt;
curl http://registry:8080/.well-known/agent.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-mcp-server-integration&quot;&gt;2. MCP server integration&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://modelcontextprotocol.io/&quot;&gt;Model Context Protocol (MCP)&lt;/a&gt; is becoming the standard for LLMs to interact with external tools and data. Apicurio Registry now includes a built-in MCP Server that lets AI models directly access the registry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What your AI agents can do:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Discover schemas by name, type, or capability&lt;/li&gt;
  &lt;li&gt;Retrieve schema content for validation&lt;/li&gt;
  &lt;li&gt;Create and update schemas programmatically&lt;/li&gt;
  &lt;li&gt;Validate data against registered schemas&lt;/li&gt;
  &lt;li&gt;Manage groups and versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Claude Desktop integration&lt;/strong&gt; is already documented—simply add Apicurio Registry as an MCP tool and your AI assistant can query schemas, validate payloads, and register new artifacts directly through natural language.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;apicurio-registry&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;java&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-jar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;apicurio-registry-mcp.jar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;env&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;REGISTRY_URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-llm-artifact-types&quot;&gt;3. LLM artifact types&lt;/h3&gt;

&lt;p&gt;Managing prompts and model schemas is one of the biggest operational challenges in production AI systems. Apicurio Registry introduces two new artifact types designed specifically for LLM workflows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PROMPT_TEMPLATE&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Store version-controlled prompt templates with `` placeholders&lt;/li&gt;
  &lt;li&gt;Define input variables with types, defaults, and validation rules&lt;/li&gt;
  &lt;li&gt;Render prompts with values via the registry API&lt;/li&gt;
  &lt;li&gt;A/B test prompt variations without code changes&lt;/li&gt;
  &lt;li&gt;Rollback instantly when a prompt underperforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MODEL_SCHEMA&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Define input/output schemas for LLM agents&lt;/li&gt;
  &lt;li&gt;Validate agent responses against expected formats&lt;/li&gt;
  &lt;li&gt;Catch schema mismatches before runtime&lt;/li&gt;
  &lt;li&gt;Track schema evolution with compatibility checking&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://registry.apicur.io/schemas/prompt-template.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;customer-response-generator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;template&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;You are a helpful support agent. The customer said: . Their sentiment is . Generate an empathetic response.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;variables&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sentiment&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;enum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;positive&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;neutral&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;negative&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;gpt-4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;SDKs are ready&lt;/strong&gt; for both Python (LangChain, LlamaIndex) and Java (LangChain4j, Quarkus), making integration straightforward.&lt;/p&gt;

&lt;h1 id=&quot;see-it-in-action-multi-agent-context-chaining&quot;&gt;See it in Action: Multi-Agent context chaining&lt;/h1&gt;

&lt;p&gt;To demonstrate these capabilities working together, we’ve created a comprehensive example: &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/examples/a2a-real-world-integration&quot;&gt;A2A Real-World Integration Demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This demo shows a real-world scenario: processing customer complaints through a pipeline of specialized AI agents:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Customer Complaint
       │
       ▼
┌──────────────────┐
│  Sentiment Agent │  Analyzes emotional tone
│  Input: message  │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  Analyzer Agent  │  Extracts issues, priority
│  Input: message  │
│       + sentiment│
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  Response Agent  │  Generates empathetic reply
│  Input: message  │
│       + sentiment│
│       + analysis │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ Translation Agent│  Translates to Spanish
│  Input: response │
└──────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The key innovation: context chaining.&lt;/strong&gt; Each agent receives not just the original message, but the outputs of all previous agents. The Response Agent knows the customer’s sentiment AND the analyzed issues, enabling truly contextual responses.&lt;/p&gt;

&lt;p&gt;All agents are registered in Apicurio Registry with:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;AGENT_CARD&lt;/strong&gt; artifacts describing their capabilities&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PROMPT_TEMPLATE&lt;/strong&gt; artifacts for their system prompts&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MODEL_SCHEMA&lt;/strong&gt; artifacts validating their inputs and outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running the demo is simple:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;examples/a2a-real-world-integration
docker-compose up &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;
mvn clean compile &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt;:java
open http://localhost:9000  &lt;span class=&quot;c&quot;&gt;# Web UI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll see agents discover each other through the registry, validate their communications against registered schemas, and pass accumulated context through the pipeline.&lt;/p&gt;

&lt;h1 id=&quot;whats-coming-next&quot;&gt;What’s coming next&lt;/h1&gt;

&lt;p&gt;This is just the beginning. Our roadmap includes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Advanced A2A Features&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;DID and Verifiable Credentials for federated trust&lt;/li&gt;
  &lt;li&gt;Auto-generate Agent Cards from OpenAPI &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-agent-*&lt;/code&gt; extensions&lt;/li&gt;
  &lt;li&gt;JSON-LD and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;potentialAction&lt;/code&gt; discovery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 3: Agent Ecosystem Expansion&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Agent lifecycle management (health monitoring, status tracking)&lt;/li&gt;
  &lt;li&gt;MCP Tool Definition artifact type&lt;/li&gt;
  &lt;li&gt;Agent Workflow Definition artifact type&lt;/li&gt;
  &lt;li&gt;Semantic search for natural language agent discovery&lt;/li&gt;
  &lt;li&gt;Agent deployment pipeline integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;get-started-today&quot;&gt;Get started today&lt;/h1&gt;

&lt;p&gt;Apicurio Registry with AI support is available now:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Try the demo&lt;/strong&gt;: Clone the repository and run the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/examples/a2a-real-world-integration&quot;&gt;A2A real-world integration example&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Deploy the registry&lt;/strong&gt;: Use our &lt;a href=&quot;https://quay.io/repository/apicurio/apicurio-registry&quot;&gt;container images&lt;/a&gt; or &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry-operator&quot;&gt;Operator&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Read the docs&lt;/strong&gt;: Check the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/mcp&quot;&gt;MCP Server README&lt;/a&gt; for integration details&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Join the discussion&lt;/strong&gt;: Follow &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues/6991&quot;&gt;the epic&lt;/a&gt; to track progress and contribute ideas&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;a-natural-evolution&quot;&gt;A natural evolution&lt;/h1&gt;

&lt;p&gt;What makes this evolution “natural” is that we didn’t bolt on AI support as an afterthought. The same principles that make Apicurio Registry valuable for API governance—versioning, validation, compatibility, discovery, audit—apply directly to managing AI agents at scale.&lt;/p&gt;

&lt;p&gt;As AI agents become essential infrastructure, they need the same level of governance that APIs have today. Apicurio Registry is ready to provide it.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;The Apicurio team is committed to making schema and artifact management seamless for the AI era. We’d love to hear your feedback and use cases—open an issue or join us in the discussions.&lt;/em&gt;&lt;/p&gt;
</description><category term="apicurio" /><category term="registry" /><category term="ai" /><category term="agents" /><category term="mcp" /><category term="a2a" /><category term="llm" /><pubDate>Thu, 05 Feb 2026 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2026/02/05/apicurio-registry-ai-natural-evolution</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2026/02/05/apicurio-registry-ai-natural-evolution</guid>
        </item><item>
            <title>Externalizing Authentication and Authorization in Apicurio Registry</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;Hey everyone - I wanted to share something we’ve been working on that I think some of you might find
interesting. We’ve created a proof of concept showing how to externalize authentication and authorization
for Apicurio Registry using a reverse proxy pattern with Envoy and Open Policy Agent (OPA).&lt;/p&gt;

&lt;p&gt;If you’ve ever thought “I wish I could handle auth at the gateway layer instead of in the application,”
then read on!&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;why-externalize-auth&quot;&gt;Why externalize auth?&lt;/h1&gt;
&lt;p&gt;Great question! Apicurio Registry currently has built-in authentication and authorization support. It
works, but there are some limitations from an enterprise architecture perspective. The implementation
is tightly coupled to the application code, and while both authentication and authorization can be
disabled, you’re stuck with the patterns we’ve implemented.&lt;/p&gt;

&lt;p&gt;What if you want to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use a standard API gateway for all your services, not just Registry?&lt;/li&gt;
  &lt;li&gt;Centralize authentication and authorization policies across multiple applications?&lt;/li&gt;
  &lt;li&gt;Leverage existing infrastructure like Envoy, Istio, or other service meshes?&lt;/li&gt;
  &lt;li&gt;Keep your application layer focused on business logic, not auth concerns?&lt;/li&gt;
  &lt;li&gt;Implement authorization patterns/rules that Apicurio Registry simply does not support?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s where this example comes in!&lt;/p&gt;

&lt;h1 id=&quot;what-did-we-build&quot;&gt;What did we build?&lt;/h1&gt;
&lt;p&gt;We created a complete Docker Compose environment that demonstrates the reverse proxy pattern for
externalized auth. Here’s what’s in the box:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Envoy Proxy&lt;/strong&gt; - Acts as the Policy Enforcement Point (PEP), handling JWT validation and routing&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Open Policy Agent (OPA)&lt;/strong&gt; - Acts as the Policy Decision Point (PDP), enforcing authorization policies&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keycloak&lt;/strong&gt; - Acts as the Identity Provider (IdP), issuing JWT tokens&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Apicurio Registry&lt;/strong&gt; - Running with auth disabled, trusting identity headers from Envoy&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Apicurio UI&lt;/strong&gt; - Configured to authenticate via Keycloak OIDC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cool part is that Apicurio Registry doesn’t do any authentication or authorization itself. It just
trusts the identity headers that Envoy injects after validating the JWT and checking with OPA.  Of
course, if you were to use this in production you would need to ensure trust between Envoy and Apicurio
Registry (e.g. via network security or mTLS).&lt;/p&gt;

&lt;h1 id=&quot;how-does-it-work&quot;&gt;How does it work?&lt;/h1&gt;
&lt;p&gt;The request flow looks like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Client authenticates with Keycloak and gets a JWT (bearer token)&lt;/li&gt;
  &lt;li&gt;Client sends request to Registry via Envoy with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization: Bearer &amp;lt;JWT&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Envoy validates the JWT against Keycloak’s public keys (JWKS endpoint)&lt;/li&gt;
  &lt;li&gt;Envoy extracts claims from the JWT and injects identity headers:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Forwarded-User&lt;/code&gt; - username from the JWT&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Forwarded-Email&lt;/code&gt; - email from the JWT&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Forwarded-Groups&lt;/code&gt; - roles from the JWT (converted to comma-separated string)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Envoy calls OPA to check if the request should be authorized&lt;/li&gt;
  &lt;li&gt;OPA evaluates its policies based on the user’s roles and the request details&lt;/li&gt;
  &lt;li&gt;If authorized, Envoy forwards the request to Registry with the identity headers&lt;/li&gt;
  &lt;li&gt;Registry processes the request, trusting the headers from Envoy&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                    ┌──────────────────────────────────────────────────────────┐
                    │                                                          │
    ┌────────┐      │    ┌───────────┐      ┌───────────┐      ┌──────────┐    │
    │        │ (1)  │    │           │ (3)  │           │ (4)  │          │    │
    │ Client │──────┼───&amp;gt;│ Keycloak  │&amp;lt;────&amp;gt;│   Envoy   │&amp;lt;────&amp;gt;│   OPA    │    │
    │        │&amp;lt;─────┼────│   (IdP)   │   ┌─&amp;gt;│   (PEP)   │      │  (PDP)   │    │
    └────────┘  JWT │    └───────────┘   │  └─────┬─────┘      └──────────┘    │
         └──────────┼────────────────────┘        │                            │
                    │        (2)                  │ (5)                        │
                    │      Request                │ Forward with               │
                    │       + JWT                 │ identity headers           │
                    │                             ▼                            │
                    │                      ┌─────────────┐                     │
                    │                      │  Apicurio   │                     │
                    │                      │  Registry   │                     │
                    │                      └─────────────┘                     │
                    │                                                          │
                    └──────────────────────────────────────────────────────────┘
                                  Docker Compose Environment

Flow:
 (1) Client authenticates with Keycloak and receives JWT token
 (2) Client sends request to Envoy with Authorization: Bearer &amp;lt;JWT&amp;gt;
 (3) Envoy validates JWT against Keycloak&apos;s JWKS endpoint
 (4) Envoy queries OPA for authorization decision
 (5) Envoy forwards request to Registry with X-Forwarded-* headers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The OPA policies implement role-based access control (RBAC) with three roles:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-admin&lt;/code&gt; - Full access to everything&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-developer&lt;/code&gt; - Read and write access&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-readonly&lt;/code&gt; - Read-only access (GET requests only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also added support for anonymous access to system endpoints like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/apis/registry/v3/system/info&lt;/code&gt;,
which is important for things like health checks and the UI configuration endpoint.&lt;/p&gt;

&lt;p&gt;Essentially this mimics the existing RBAC support found in Registry.  The benefit, of
course, is that the OPA policies can be modified to be whatever you want them to be!&lt;/p&gt;

&lt;h1 id=&quot;show-me-the-code&quot;&gt;Show me the code!&lt;/h1&gt;
&lt;p&gt;The POC is available in the Registry repository at:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://github.com/Apicurio/apicurio-registry/tree/main/distro/docker-compose/in-memory-with-envoy-opa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can try it out right now (if you have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry&lt;/code&gt; cloned locally):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$APICURIO_REGISTRY_HOME&lt;/span&gt;/distro/docker-compose/in-memory-with-envoy-opa
docker compose up &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wait about a minute for everything to start up, then:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Keycloak&lt;/strong&gt;: http://localhost:8080/admin (admin/admin)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Registry API&lt;/strong&gt; (via Envoy): http://localhost:8081/apis/registry/v3&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Registry UI&lt;/strong&gt;: http://localhost:8888 (admin/admin)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve even included an automated test suite (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test.sh&lt;/code&gt;) that validates all the authentication and
authorization scenarios. Running the tests gives you 25+ passing test cases covering everything from
anonymous access to role-based permissions.&lt;/p&gt;

&lt;h1 id=&quot;what-about-cors&quot;&gt;What about CORS?&lt;/h1&gt;
&lt;p&gt;One interesting challenge we ran into was getting the UI to work with this setup. The UI runs on
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost:8888&lt;/code&gt; and makes API calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost:8081&lt;/code&gt;, which triggers the browser’s CORS policy.&lt;/p&gt;

&lt;p&gt;We solved this by adding CORS configuration to Envoy that allows requests from the UI origin and
properly handles preflight requests. The UI is configured to use OIDC authentication directly with
Keycloak, and it includes the JWT token in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt; header for all API requests.&lt;/p&gt;

&lt;p&gt;It all works seamlessly.&lt;/p&gt;

&lt;h1 id=&quot;opa-policies-in-action&quot;&gt;OPA policies in action&lt;/h1&gt;
&lt;p&gt;Here’s what one of the OPA authorization rules looks like:&lt;/p&gt;

&lt;div class=&quot;language-rego highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Admin users can do anything&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;allowed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;has_role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sr-admin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Read-only users can only GET&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;allowed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;has_role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sr-readonly&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;http_method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;GET&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pretty straightforward! The policies are written in Rego (OPA’s policy language) and are stored in a
separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;policy.rego&lt;/code&gt; file. You can modify the policies and restart OPA to test different
authorization scenarios without touching any application code.&lt;/p&gt;

&lt;h1 id=&quot;what-about-owner-based-authorization&quot;&gt;What about owner-based authorization?&lt;/h1&gt;
&lt;p&gt;Good question! We had an interesting discussion about this. OPA does support making HTTP calls to
external services during policy evaluation using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http.send&lt;/code&gt; function. So theoretically, OPA could
query the Registry API to check if a user owns a particular artifact before authorizing a request.&lt;/p&gt;

&lt;p&gt;However, this creates some challenges:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Latency&lt;/strong&gt; - Every request would require an additional HTTP call&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Load&lt;/strong&gt; - Doubles the load on the Registry API&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Complexity&lt;/strong&gt; - Ownership data changes frequently and requires database access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A future evolution of this example will likely explore this area further, assuming we receive any
feedback from interested parties!  Come and open a discussion about this topic if you like:&lt;/p&gt;

&lt;p&gt;https://github.com/Apicurio/apicurio-registry/discussions&lt;/p&gt;

&lt;h1 id=&quot;is-this-production-ready&quot;&gt;Is this production ready?&lt;/h1&gt;
&lt;p&gt;This is a proof of concept, not a production-ready solution (at least not yet). There are a few things
you’d want to add for production use:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;TLS/HTTPS&lt;/strong&gt; - Enable TLS for all external communication&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Network isolation&lt;/strong&gt; - Deploy Registry in a private network, only accessible from Envoy&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;High availability&lt;/strong&gt; - Run multiple instances of each component&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Monitoring&lt;/strong&gt; - Add Prometheus metrics, distributed tracing, and logging&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Secret management&lt;/strong&gt; - Use proper secret management tools for credentials&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Policy management&lt;/strong&gt; - Use GitOps for managing OPA policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the foundation is solid, and the pattern is proven. If you’re interested in deploying Registry
with externalized auth, this POC gives you a great starting point!&lt;/p&gt;

&lt;h1 id=&quot;try-it-out&quot;&gt;Try it out!&lt;/h1&gt;
&lt;p&gt;If this sounds interesting to you, I encourage you to check out the POC in the Registry repository.
The README in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in-memory-with-envoy-opa&lt;/code&gt; directory has detailed instructions on how to run it,
test it, and customize it for your needs.&lt;/p&gt;

&lt;p&gt;And as always, feedback is welcome! If you try this out and run into issues, or if you have ideas
for improvements, please let us know on GitHub or Zulip.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy registering!&lt;/p&gt;
</description><category term="registry" /><category term="authentication" /><category term="security" /><pubDate>Wed, 28 Jan 2026 16:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2026/01/28/registry-external-auth</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2026/01/28/registry-external-auth</guid>
        </item><item>
            <title>OpenTelemetry Distributed Tracing with Apicurio Registry</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;Observability is critical in distributed systems. When you’re running microservices that communicate through Kafka and rely on Apicurio Registry for schema management, understanding the complete request flow becomes essential for debugging and performance optimization. In this post, we introduce comprehensive OpenTelemetry support in Apicurio Registry and provide a complete example demonstrating end-to-end distributed tracing.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;what-is-opentelemetry&quot;&gt;What is OpenTelemetry?&lt;/h1&gt;

&lt;p&gt;OpenTelemetry (OTel) is a vendor-neutral observability framework that provides APIs, SDKs, and tools for collecting telemetry data including traces, metrics, and logs. It has become the industry standard for instrumenting distributed systems.&lt;/p&gt;

&lt;p&gt;Distributed tracing allows you to follow a request as it travels through multiple services, making it invaluable for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Debugging&lt;/strong&gt;: Identify where failures occur in a multi-service flow&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Performance analysis&lt;/strong&gt;: Find bottlenecks across service boundaries&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Dependency mapping&lt;/strong&gt;: Understand how services interact&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Root cause analysis&lt;/strong&gt;: Trace issues back to their origin&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;opentelemetry-in-apicurio-registry&quot;&gt;OpenTelemetry in Apicurio Registry&lt;/h1&gt;

&lt;p&gt;Apicurio Registry is built with all OpenTelemetry signals enabled at build time (traces, metrics, and logs). However, the OTel SDK is disabled by default at runtime to avoid any performance impact for users who don’t need observability.&lt;/p&gt;

&lt;p&gt;To enable OpenTelemetry, simply set:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;QUARKUS_OTEL_SDK_DISABLED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once enabled, Registry operations such as schema registration, lookup, and validation will generate spans that integrate seamlessly with your existing distributed traces.&lt;/p&gt;

&lt;h1 id=&quot;architecture-overview&quot;&gt;Architecture Overview&lt;/h1&gt;

&lt;p&gt;The following diagram shows how traces flow through a typical setup with Kafka and Apicurio Registry:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Quarkus        │────▶│     Kafka       │────▶│    Quarkus      │
│  Producer       │     │   (Strimzi)     │     │    Consumer     │
└────────┬────────┘     └─────────────────┘     └────────┬────────┘
         │                                               │
         │              ┌─────────────────┐              │
         └─────────────▶│   Apicurio      │◀─────────────┘
                        │   Registry      │
                        │  (OTel enabled) │
                        └────────┬────────┘
                                 │
                        ┌────────▼────────┐
                        │   OpenTelemetry │
                        │   Collector     │
                        └────────┬────────┘
                                 │
                        ┌────────▼────────┐
                        │     Jaeger      │
                        │  (Visualization)│
                        └─────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key components are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Producer Application&lt;/strong&gt;: Sends Avro messages to Kafka, registering schemas with Apicurio Registry&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consumer Application&lt;/strong&gt;: Consumes messages from Kafka, looking up schemas from Registry&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Apicurio Registry&lt;/strong&gt;: Provides schema storage and validation with OTel instrumentation&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;OpenTelemetry Collector&lt;/strong&gt;: Aggregates and forwards telemetry data&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Jaeger&lt;/strong&gt;: Visualizes the distributed traces&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;trace-propagation&quot;&gt;Trace Propagation&lt;/h1&gt;

&lt;p&gt;One of the most powerful features of OpenTelemetry is automatic context propagation. When properly configured, trace context flows seamlessly across service boundaries:&lt;/p&gt;

&lt;h2 id=&quot;kafka-message-propagation&quot;&gt;Kafka Message Propagation&lt;/h2&gt;

&lt;p&gt;The OpenTelemetry instrumentation for Kafka automatically:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Producer side&lt;/strong&gt;: Injects trace context (trace ID, span ID) into Kafka message headers&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consumer side&lt;/strong&gt;: Extracts trace context from Kafka message headers and creates child spans&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This creates a continuous trace from producer through Kafka to consumer.&lt;/p&gt;

&lt;h2 id=&quot;registry-correlation&quot;&gt;Registry Correlation&lt;/h2&gt;

&lt;p&gt;When producer or consumer applications communicate with Apicurio Registry:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;HTTP client instrumentation propagates trace context in HTTP headers&lt;/li&gt;
  &lt;li&gt;Registry operations (schema registration, lookup) appear as child spans&lt;/li&gt;
  &lt;li&gt;Schema caching behavior becomes visible in trace timing (first calls are slower, subsequent calls faster due to caching)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;complete-example&quot;&gt;Complete Example&lt;/h1&gt;

&lt;p&gt;We’ve created a comprehensive example demonstrating end-to-end distributed tracing. The example includes Docker Compose and Kubernetes deployments, making it easy to try locally or in a cluster.&lt;/p&gt;

&lt;h2 id=&quot;quick-start-with-docker-compose&quot;&gt;Quick Start with Docker Compose&lt;/h2&gt;

&lt;h3 id=&quot;1-clone-and-build&quot;&gt;1. Clone and build&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# From the apicurio-registry repository&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;examples/otel-tracing

&lt;span class=&quot;c&quot;&gt;# Build producer&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;producer &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mvn clean package &lt;span class=&quot;nt&quot;&gt;-DskipTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..

&lt;span class=&quot;c&quot;&gt;# Build consumer&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;consumer &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mvn clean package &lt;span class=&quot;nt&quot;&gt;-DskipTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-start-the-infrastructure&quot;&gt;2. Start the infrastructure&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker compose up &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-access-the-services&quot;&gt;3. Access the services&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Service&lt;/th&gt;
      &lt;th&gt;URL&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Jaeger UI&lt;/td&gt;
      &lt;td&gt;http://localhost:16686&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Apicurio Registry&lt;/td&gt;
      &lt;td&gt;http://localhost:8083&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Producer API&lt;/td&gt;
      &lt;td&gt;http://localhost:8084&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Consumer API&lt;/td&gt;
      &lt;td&gt;http://localhost:8085&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;4-generate-traces&quot;&gt;4. Generate traces&lt;/h3&gt;

&lt;p&gt;Send a greeting message through the system:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Send a message&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8084/greetings?name=World&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Consume the message&lt;/span&gt;
curl &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8085/consumer/greetings&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;5-view-traces-in-jaeger&quot;&gt;5. View traces in Jaeger&lt;/h3&gt;

&lt;p&gt;Open http://localhost:16686 and:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Select a service: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greeting-producer&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greeting-consumer&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Click “Find Traces”&lt;/li&gt;
  &lt;li&gt;Click on a trace to see the complete flow across all services&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;understanding-trace-visualization-and-performance-analysis&quot;&gt;Understanding Trace Visualization and Performance Analysis&lt;/h1&gt;

&lt;p&gt;When you open a trace in Jaeger, you get a powerful view into exactly how your distributed system behaves. The following screenshot shows a real trace from the batch greeting endpoint:&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/posts/otel/jaeger.png&quot; alt=&quot;Jaeger Trace Visualization&quot; style=&quot;max-width: 100%; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);&quot; /&gt;
&lt;/p&gt;

&lt;h2 id=&quot;reading-the-trace&quot;&gt;Reading the Trace&lt;/h2&gt;

&lt;p&gt;The trace visualization displays a hierarchical timeline where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Each row represents a span&lt;/strong&gt; - a unit of work within a service&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Indentation shows parent-child relationships&lt;/strong&gt; - child spans are nested under their parents&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Bar length indicates duration&lt;/strong&gt; - longer bars mean more time spent&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Bar position shows timing&lt;/strong&gt; - when each operation started relative to the trace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this example trace, you can see the complete flow:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greeting-producer POST /greetings/batch&lt;/code&gt;&lt;/strong&gt; (112.6ms total) - The incoming HTTP request&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greeting-producer greetings publish&lt;/code&gt;&lt;/strong&gt; (80.62ms) - Publishing messages to Kafka&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry POST /apis/registry/v3/groups/{groupId}/artifacts&lt;/code&gt;&lt;/strong&gt; (59.27ms) - Schema registration call to Registry&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Registry span expands to show internal storage operations:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Operation&lt;/th&gt;
      &lt;th&gt;Duration&lt;/th&gt;
      &lt;th&gt;Purpose&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage.isGroupExists&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;2.57ms&lt;/td&gt;
      &lt;td&gt;Check if the artifact group exists&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage.getGlobalRules&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;538µs&lt;/td&gt;
      &lt;td&gt;Retrieve global validation rules&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage.isReadOnly&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;56µs&lt;/td&gt;
      &lt;td&gt;Check if registry is in read-only mode&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage.isReady&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;12µs&lt;/td&gt;
      &lt;td&gt;Health check&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage.createArtifact&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;15.95ms&lt;/td&gt;
      &lt;td&gt;Create the new artifact (most expensive)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage.getArtifactVersionMetaDataByContent&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;4.25ms&lt;/td&gt;
      &lt;td&gt;Retrieve version metadata&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage.getArtifactMetaData&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;2.13ms&lt;/td&gt;
      &lt;td&gt;Get artifact metadata&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;using-traces-for-performance-optimization&quot;&gt;Using Traces for Performance Optimization&lt;/h2&gt;

&lt;p&gt;This level of detail enables powerful performance analysis:&lt;/p&gt;

&lt;h3 id=&quot;identify-bottlenecks&quot;&gt;Identify Bottlenecks&lt;/h3&gt;

&lt;p&gt;The trace immediately reveals where time is spent. In this example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage.createArtifact&lt;/code&gt; takes 15.95ms - the largest portion of the Registry call. If you’re experiencing slow schema registrations, this pinpoints exactly which operation to investigate.&lt;/p&gt;

&lt;h3 id=&quot;measure-latency-breakdown&quot;&gt;Measure Latency Breakdown&lt;/h3&gt;

&lt;p&gt;You can calculate the percentage of time each component contributes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Producer processing: ~20% of total time&lt;/li&gt;
  &lt;li&gt;Network + Kafka: ~10%&lt;/li&gt;
  &lt;li&gt;Registry operations: ~53%&lt;/li&gt;
  &lt;li&gt;Consumer processing: ~17%&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;detect-caching-effects&quot;&gt;Detect Caching Effects&lt;/h3&gt;

&lt;p&gt;Notice how subsequent consumer operations (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greetings process&lt;/code&gt;) take only 480µs compared to the initial 7.69ms. This demonstrates schema caching in action - once a schema is fetched from Registry, it’s cached locally, dramatically reducing latency for subsequent messages.&lt;/p&gt;

&lt;h3 id=&quot;compare-across-requests&quot;&gt;Compare Across Requests&lt;/h3&gt;

&lt;p&gt;Jaeger allows you to compare traces side-by-side. You can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Compare slow vs. fast requests to identify anomalies&lt;/li&gt;
  &lt;li&gt;Track performance trends over time&lt;/li&gt;
  &lt;li&gt;Validate that optimizations have the expected impact&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;set-performance-baselines&quot;&gt;Set Performance Baselines&lt;/h3&gt;

&lt;p&gt;By collecting traces over time, you can establish baseline performance metrics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;P50/P95/P99 latencies&lt;/strong&gt; for Registry operations&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Expected duration&lt;/strong&gt; for schema registration vs. lookup&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Acceptable overhead&lt;/strong&gt; for distributed tracing itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This data helps you set meaningful SLOs and alerts for your schema registry infrastructure.&lt;/p&gt;

&lt;h1 id=&quot;custom-span-creation&quot;&gt;Custom Span Creation&lt;/h1&gt;

&lt;p&gt;The example demonstrates two approaches to creating custom spans for your own applications:&lt;/p&gt;

&lt;h2 id=&quot;declarative-approach-with-withspan&quot;&gt;Declarative Approach with @WithSpan&lt;/h2&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@WithSpan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;create-greeting&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Greeting&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createGreeting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@SpanAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;greeting.recipient&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@SpanAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;greeting.trace_id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;Span&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;greeting.source&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;greeting-producer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Span&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;greeting-object-created&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Business logic here&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;programmatic-approach-with-tracer-api&quot;&gt;Programmatic Approach with Tracer API&lt;/h2&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Span&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processSpan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;spanBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;process-detailed-greeting&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setSpanKind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SpanKind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;INTERNAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;greeting.recipient&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;greeting.priority&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;startSpan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Scope&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processSpan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;makeCurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;processSpan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;validation-started&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Business logic here&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;processSpan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;validation-completed&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;processSpan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;StatusCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;processSpan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;error-tracing&quot;&gt;Error Tracing&lt;/h1&gt;

&lt;p&gt;The example includes dedicated endpoints for demonstrating error tracing:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Test different error types&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8084/greetings/invalid?errorType=validation&quot;&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8084/greetings/invalid?errorType=schema&quot;&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8084/greetings/invalid?errorType=kafka&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Error spans in Jaeger appear with:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Red color indicating error status&lt;/li&gt;
  &lt;li&gt;Exception stack traces in span logs&lt;/li&gt;
  &lt;li&gt;Custom error attributes and events&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;sampling-strategies&quot;&gt;Sampling Strategies&lt;/h1&gt;

&lt;p&gt;The example uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parentbased_always_on&lt;/code&gt; sampling (100% of traces), which is ideal for development. For production environments, consider these alternatives:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Strategy&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
      &lt;th&gt;Use Case&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;always_on&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Sample all traces&lt;/td&gt;
      &lt;td&gt;Development&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parentbased_traceidratio&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Sample a percentage with parent inheritance&lt;/td&gt;
      &lt;td&gt;Production&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;always_off&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Disable tracing&lt;/td&gt;
      &lt;td&gt;When not needed&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Configure sampling via environment variables:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;QUARKUS_OTEL_TRACES_SAMPLER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;parentbased_traceidratio&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;QUARKUS_OTEL_TRACES_SAMPLER_ARG&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;0.1&quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 10% sampling&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;configuration-reference&quot;&gt;Configuration Reference&lt;/h1&gt;

&lt;h2 id=&quot;apicurio-registry-opentelemetry-properties&quot;&gt;Apicurio Registry OpenTelemetry Properties&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Property&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
      &lt;th&gt;Default&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.otel.sdk.disabled&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Disable/enable OTel SDK at runtime&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.otel.service.name&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Service name in traces&lt;/td&gt;
      &lt;td&gt;Application name&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.otel.exporter.otlp.endpoint&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;OTLP collector endpoint&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:4317&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.otel.exporter.otlp.protocol&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;OTLP protocol (grpc or http/protobuf)&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grpc&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.otel.traces.sampler.arg&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Sampler ratio (0.0 to 1.0)&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.1&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Signal properties (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traces.enabled&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metrics.enabled&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logs.enabled&lt;/code&gt;) are build-time properties. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.otel.sdk.disabled&lt;/code&gt; for runtime control.&lt;/p&gt;

&lt;h1 id=&quot;kubernetes-deployment&quot;&gt;Kubernetes Deployment&lt;/h1&gt;

&lt;p&gt;The example also includes complete Kubernetes manifests for deployment with Strimzi. Key steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install Strimzi operator&lt;/li&gt;
  &lt;li&gt;Build and push container images&lt;/li&gt;
  &lt;li&gt;Deploy with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl apply -k k8s/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Port-forward to access services&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See the example README for detailed Kubernetes instructions.&lt;/p&gt;

&lt;h1 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h1&gt;

&lt;h2 id=&quot;no-traces-appearing-in-jaeger&quot;&gt;No traces appearing in Jaeger&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Check OTel Collector logs: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker compose logs otel-collector&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Verify OTLP endpoint configuration&lt;/li&gt;
  &lt;li&gt;Ensure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OTEL_SDK_DISABLED=false&lt;/code&gt; is set for Apicurio Registry&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;missing-trace-context-in-consumer&quot;&gt;Missing trace context in consumer&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Ensure both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus-opentelemetry&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus-kafka-client&lt;/code&gt; dependencies are present&lt;/li&gt;
  &lt;li&gt;Check Kafka message headers for trace context&lt;/li&gt;
  &lt;li&gt;Verify OTel is enabled in both producer and consumer&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;schema-registration-failures&quot;&gt;Schema registration failures&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Check Registry health: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl http://localhost:8083/health&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Verify Registry URL in application configuration&lt;/li&gt;
  &lt;li&gt;Check network connectivity between services&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;OpenTelemetry support in Apicurio Registry enables powerful observability capabilities for your schema registry operations. Combined with the new example demonstrating end-to-end distributed tracing with Kafka, you now have everything needed to implement comprehensive observability in your event-driven architectures.&lt;/p&gt;

&lt;p&gt;The example is available in the Apicurio Registry repository under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;examples/otel-tracing&lt;/code&gt;. We encourage you to try it out and provide feedback!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;For more details, check:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.apicur.io/registry/docs/&quot;&gt;Apicurio Registry Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://opentelemetry.io/docs/&quot;&gt;OpenTelemetry Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://quarkus.io/guides/opentelemetry&quot;&gt;Quarkus OpenTelemetry Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description><category term="registry" /><category term="observability" /><category term="tracing" /><pubDate>Mon, 19 Jan 2026 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2026/01/19/opentelemetry-distributed-tracing</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2026/01/19/opentelemetry-distributed-tracing</guid>
        </item><item>
            <title>Apicurio Registry - JSON Schema and OpenAPI dereference</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;Apicurio Registry allows to manage artifacts with references as shown in the &lt;a href=&quot;https://www.apicur.io/registry/docs/apicurio-registry/3.0.x/getting-started/assembly-managing-registry-artifacts-api.html&quot;&gt;documentation&lt;/a&gt;.
One of the cool features we’ve added on top of this is the possibility of using a dereference parameter for certain API endpoints that optionally allows you to fetch the full content of an artifact with all the references inlined within the same content.
This is especially useful in certain contexts to reduce the number of HTTP requests in the Kafka Serializers and Deserializers, as you will see in this blog.&lt;/p&gt;

&lt;p&gt;The dereference feature is supported for Avro, JSON Schema, Protobuf, OpenAPI, and AsyncAPI artifact types.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;intro&quot;&gt;Intro&lt;/h1&gt;

&lt;p&gt;The code example used for this blog post can be found in the Apicurio Registry &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/examples/serdes-with-references/src/main/java/io/apicurio/registry/examples/references/JsonSerdeReferencesExample.java&quot;&gt;examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the limitations of the JSON Schema format is that you cannot discover the full schema from some data that adheres to the schema. That’s the main reason why, for this serde, the schema being used has to be registered upfront, and that’s what you see
in the code example from line &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/examples/serdes-with-references/src/main/java/io/apicurio/registry/examples/references/JsonSerdeReferencesExample.java#L52-L93&quot;&gt;52 to line 93&lt;/a&gt;.
where the main schema &lt;em&gt;citizen.json&lt;/em&gt; is registered with all the references in the hierarchy. Along those lines, a full hierarchy of schemas is registered in Apicurio Registry. The hierarchy can be described as follows and represented as a tree:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    citizen
       |
      city
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the code example there are
also &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/examples/serdes-with-references/src/main/java/io/apicurio/registry/examples/references/model&quot;&gt;java classes&lt;/a&gt;
that represent each of the objects we have in the structure, so we can use them when producing Kafka messages. There is
one Java class per each JSON Schema file.&lt;/p&gt;

&lt;h1 id=&quot;using-the-rest-api&quot;&gt;Using the REST API&lt;/h1&gt;

&lt;p&gt;Although the example above uses the Apicurio Registry Java client to register the full hierarchy of schemas, it’s also
possible to use the REST API. Note that, in Apicurio Registry v3, when an artifact with references is created, the request body follows a structured format with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;artifactId&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;artifactType&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;firstVersion&lt;/code&gt; containing the content and references. Here’s a set of Curl commands that delivers this result:&lt;/p&gt;

&lt;p&gt;First we must start creating the leaves, in this case, the city schema:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//Creates the city schema
curl -X POST &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts&apos; \
-H &apos;Content-Type: application/json&apos; \
-d &apos;{
  &quot;artifactId&quot;: &quot;city&quot;,
  &quot;artifactType&quot;: &quot;JSON&quot;,
  &quot;firstVersion&quot;: {
    &quot;content&quot;: {
      &quot;content&quot;: &quot;{\&quot;$id\&quot;:\&quot;https://example.com/city.schema.json\&quot;,\&quot;$schema\&quot;:\&quot;http://json-schema.org/draft-07/schema#\&quot;,\&quot;title\&quot;:\&quot;City\&quot;,\&quot;type\&quot;:\&quot;object\&quot;,\&quot;properties\&quot;:{\&quot;name\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;The city&apos;\&apos;&apos;s name.\&quot;},\&quot;zipCode\&quot;:{\&quot;type\&quot;:\&quot;integer\&quot;,\&quot;description\&quot;:\&quot;The zip code.\&quot;,\&quot;minimum\&quot;:0}}}&quot;,
      &quot;contentType&quot;: &quot;application/json&quot;
    }
  }
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the city schema is created, we can create the citizen schema that references it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//Creates the citizen schema with reference to city
curl -X POST &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts&apos; \
-H &apos;Content-Type: application/json&apos; \
-d &apos;{
  &quot;artifactId&quot;: &quot;citizen&quot;,
  &quot;artifactType&quot;: &quot;JSON&quot;,
  &quot;firstVersion&quot;: {
    &quot;content&quot;: {
      &quot;content&quot;: &quot;{\&quot;$id\&quot;:\&quot;https://example.com/citizen.schema.json\&quot;,\&quot;$schema\&quot;:\&quot;http://json-schema.org/draft-07/schema#\&quot;,\&quot;title\&quot;:\&quot;Citizen\&quot;,\&quot;type\&quot;:\&quot;object\&quot;,\&quot;properties\&quot;:{\&quot;firstName\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;The citizen&apos;\&apos;&apos;s first name.\&quot;},\&quot;lastName\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;The citizen&apos;\&apos;&apos;s last name.\&quot;},\&quot;age\&quot;:{\&quot;description\&quot;:\&quot;Age in years which must be equal to or greater than zero.\&quot;,\&quot;type\&quot;:\&quot;integer\&quot;,\&quot;minimum\&quot;:0},\&quot;city\&quot;:{\&quot;$ref\&quot;:\&quot;city.json\&quot;}}}&quot;,
      &quot;contentType&quot;: &quot;application/json&quot;,
      &quot;references&quot;: [
        {
          &quot;name&quot;: &quot;city.json&quot;,
          &quot;groupId&quot;: &quot;default&quot;,
          &quot;artifactId&quot;: &quot;city&quot;,
          &quot;version&quot;: &quot;1&quot;
        }
      ]
    }
  }
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;json-schema-dereference&quot;&gt;JSON Schema dereference&lt;/h1&gt;

&lt;p&gt;By default, the endpoints that return the content of a particular artifact fetch just the main content, and then you must get the links to the references and,
one by one, do a full scan of the hierarchy to fetch any other sub-schema needed. For instance, the content of the &lt;em&gt;citizen.json&lt;/em&gt; schema is the following:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.schema.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without dereferencing in order to be able to produce Kafka messages to adhere to this schema, that is already registered in Apicurio Registry, we would need to fetch all the nested references with multiple HTTP requests and then parse all of them while, when dereferencing is used, this is what you get:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zipCode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The zip code.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The city&apos;s name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.schema.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see, that’s the content of the full hierarchy, not just the schema of the &lt;em&gt;citizen&lt;/em&gt; element alone. In Apicurio Registry v3, you can get the content dereferenced using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;references=DEREFERENCE&lt;/code&gt; query parameter with the following curl commands:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &apos;http://localhost:8080/apis/registry/v3/ids/globalIds/2?references=DEREFERENCE&apos;
curl &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts/citizen/versions/1/content?references=DEREFERENCE&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;
&lt;p&gt;OpenAPI dereference
===&lt;/p&gt;

&lt;p&gt;The dereference feature also works for OpenAPI specifications. OpenAPI specs commonly use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$ref&lt;/code&gt; to reference external schema components, and the dereference parameter allows you to inline those references.&lt;/p&gt;

&lt;p&gt;Here’s an example of how to register OpenAPI specs with references:&lt;/p&gt;

&lt;p&gt;First, create a shared schema component:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//Creates the address types OpenAPI component
curl -X POST &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts&apos; \
-H &apos;Content-Type: application/json&apos; \
-d &apos;{
  &quot;artifactId&quot;: &quot;address-types&quot;,
  &quot;artifactType&quot;: &quot;OPENAPI&quot;,
  &quot;firstVersion&quot;: {
    &quot;content&quot;: {
      &quot;content&quot;: &quot;{\&quot;openapi\&quot;:\&quot;3.0.3\&quot;,\&quot;info\&quot;:{\&quot;title\&quot;:\&quot;Address Types\&quot;,\&quot;version\&quot;:\&quot;1.0.0\&quot;},\&quot;paths\&quot;:{},\&quot;components\&quot;:{\&quot;schemas\&quot;:{\&quot;Address\&quot;:{\&quot;title\&quot;:\&quot;Address\&quot;,\&quot;type\&quot;:\&quot;object\&quot;,\&quot;properties\&quot;:{\&quot;street\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;Street address\&quot;},\&quot;city\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;City name\&quot;},\&quot;zipCode\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;Postal code\&quot;}}}}}}&quot;,
      &quot;contentType&quot;: &quot;application/json&quot;
    }
  }
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then create an API that references the address component:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//Creates the User API with reference to address
curl -X POST &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts&apos; \
-H &apos;Content-Type: application/json&apos; \
-d &apos;{
  &quot;artifactId&quot;: &quot;user-api&quot;,
  &quot;artifactType&quot;: &quot;OPENAPI&quot;,
  &quot;firstVersion&quot;: {
    &quot;content&quot;: {
      &quot;content&quot;: &quot;{\&quot;openapi\&quot;:\&quot;3.0.3\&quot;,\&quot;info\&quot;:{\&quot;title\&quot;:\&quot;User API\&quot;,\&quot;version\&quot;:\&quot;1.0.0\&quot;},\&quot;paths\&quot;:{\&quot;/users\&quot;:{\&quot;get\&quot;:{\&quot;summary\&quot;:\&quot;List users\&quot;,\&quot;operationId\&quot;:\&quot;getUsers\&quot;,\&quot;responses\&quot;:{\&quot;200\&quot;:{\&quot;description\&quot;:\&quot;Success\&quot;,\&quot;content\&quot;:{\&quot;application/json\&quot;:{\&quot;schema\&quot;:{\&quot;type\&quot;:\&quot;array\&quot;,\&quot;items\&quot;:{\&quot;$ref\&quot;:\&quot;#/components/schemas/User\&quot;}}}}}}}}},\&quot;components\&quot;:{\&quot;schemas\&quot;:{\&quot;User\&quot;:{\&quot;title\&quot;:\&quot;User\&quot;,\&quot;type\&quot;:\&quot;object\&quot;,\&quot;properties\&quot;:{\&quot;name\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;},\&quot;email\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;},\&quot;address\&quot;:{\&quot;$ref\&quot;:\&quot;#/components/schemas/Address\&quot;}}},\&quot;Address\&quot;:{\&quot;$ref\&quot;:\&quot;address-types.json#/components/schemas/Address\&quot;}}}}&quot;,
      &quot;contentType&quot;: &quot;application/json&quot;,
      &quot;references&quot;: [
        {
          &quot;name&quot;: &quot;address-types.json#/components/schemas/Address&quot;,
          &quot;groupId&quot;: &quot;default&quot;,
          &quot;artifactId&quot;: &quot;address-types&quot;,
          &quot;version&quot;: &quot;1&quot;
        }
      ]
    }
  }
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can then fetch the dereferenced OpenAPI specification:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts/user-api/versions/1/content?references=DEREFERENCE&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that OpenAPI dereferencing works by inlining the referenced schemas into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;components/schemas&lt;/code&gt; section of the specification.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;Referencing options
===&lt;/p&gt;

&lt;p&gt;Note that, even though referencing a whole schema definition is the most common use case, it’s also possible to reference a single object definition defined in a json file with multiple schemas. For example, we might have the following schema definition:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/types/all-types.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;definitions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The city&apos;s name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zipCode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The zip code.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen identifier.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With definitions for both an identifier and the city objects. Then we might decide that we want to have a separate schema that points to an entire definition, like the one below:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;types/all-types.json#/definitions/City&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;types/all-types.json#/definitions/Identifier&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or we might decide to just reference a single property inside a schema definition, like the schema below does:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;types/all-types.json#/definitions/City/properties/name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;types/all-types.json#/definitions/Identifier&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The three options work exactly the same way when it comes to registering those schemas using the REST API as if we were registering a schema that references another. First we must register the schema that will be referenced, in this case, the one with the definitions:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts&apos; \
-H &apos;Content-Type: application/json&apos; \
-d &apos;{
  &quot;artifactId&quot;: &quot;all-types&quot;,
  &quot;artifactType&quot;: &quot;JSON&quot;,
  &quot;firstVersion&quot;: {
    &quot;content&quot;: {
      &quot;content&quot;: &quot;{\&quot;$id\&quot;:\&quot;https://example.com/types/all-types.json\&quot;,\&quot;$schema\&quot;:\&quot;http://json-schema.org/draft-07/schema#\&quot;,\&quot;definitions\&quot;:{\&quot;City\&quot;:{\&quot;title\&quot;:\&quot;City\&quot;,\&quot;type\&quot;:\&quot;object\&quot;,\&quot;properties\&quot;:{\&quot;name\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;The city&apos;\&apos;&apos;s name.\&quot;},\&quot;zipCode\&quot;:{\&quot;type\&quot;:\&quot;integer\&quot;,\&quot;description\&quot;:\&quot;The zip code.\&quot;,\&quot;minimum\&quot;:0}}},\&quot;Identifier\&quot;:{\&quot;title\&quot;:\&quot;Identifier\&quot;,\&quot;type\&quot;:\&quot;object\&quot;,\&quot;properties\&quot;:{\&quot;identifier\&quot;:{\&quot;type\&quot;:\&quot;integer\&quot;,\&quot;description\&quot;:\&quot;The citizen identifier.\&quot;,\&quot;minimum\&quot;:0}}}}}&quot;,
      &quot;contentType&quot;: &quot;application/json&quot;
    }
  }
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once this schema has been registered, we can now decide to register two separate schemas, the first one referencing the entire city object:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts&apos; \
-H &apos;Content-Type: application/json&apos; \
-d &apos;{
  &quot;artifactId&quot;: &quot;citizen-object-level&quot;,
  &quot;artifactType&quot;: &quot;JSON&quot;,
  &quot;firstVersion&quot;: {
    &quot;content&quot;: {
      &quot;content&quot;: &quot;{\&quot;$id\&quot;:\&quot;https://example.com/citizen.json\&quot;,\&quot;$schema\&quot;:\&quot;http://json-schema.org/draft-07/schema#\&quot;,\&quot;title\&quot;:\&quot;Citizen\&quot;,\&quot;type\&quot;:\&quot;object\&quot;,\&quot;properties\&quot;:{\&quot;firstName\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;The citizen&apos;\&apos;&apos;s first name.\&quot;},\&quot;lastName\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;The citizen&apos;\&apos;&apos;s last name.\&quot;},\&quot;age\&quot;:{\&quot;description\&quot;:\&quot;Age in years which must be equal to or greater than zero.\&quot;,\&quot;type\&quot;:\&quot;integer\&quot;,\&quot;minimum\&quot;:0},\&quot;city\&quot;:{\&quot;$ref\&quot;:\&quot;types/all-types.json#/definitions/City\&quot;},\&quot;identifier\&quot;:{\&quot;$ref\&quot;:\&quot;types/all-types.json#/definitions/Identifier\&quot;}},\&quot;required\&quot;:[\&quot;city\&quot;]}&quot;,
      &quot;contentType&quot;: &quot;application/json&quot;,
      &quot;references&quot;: [
        {
          &quot;name&quot;: &quot;types/all-types.json#/definitions/City&quot;,
          &quot;groupId&quot;: &quot;default&quot;,
          &quot;artifactId&quot;: &quot;all-types&quot;,
          &quot;version&quot;: &quot;1&quot;
        },
        {
          &quot;name&quot;: &quot;types/all-types.json#/definitions/Identifier&quot;,
          &quot;groupId&quot;: &quot;default&quot;,
          &quot;artifactId&quot;: &quot;all-types&quot;,
          &quot;version&quot;: &quot;1&quot;
        }
      ]
    }
  }
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then register the second schema, where just the city name property is referenced:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST &apos;http://localhost:8080/apis/registry/v3/groups/default/artifacts&apos; \
-H &apos;Content-Type: application/json&apos; \
-d &apos;{
  &quot;artifactId&quot;: &quot;citizen-property-level&quot;,
  &quot;artifactType&quot;: &quot;JSON&quot;,
  &quot;firstVersion&quot;: {
    &quot;content&quot;: {
      &quot;content&quot;: &quot;{\&quot;$id\&quot;:\&quot;https://example.com/citizen.json\&quot;,\&quot;$schema\&quot;:\&quot;http://json-schema.org/draft-07/schema#\&quot;,\&quot;title\&quot;:\&quot;Citizen\&quot;,\&quot;type\&quot;:\&quot;object\&quot;,\&quot;properties\&quot;:{\&quot;firstName\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;The citizen&apos;\&apos;&apos;s first name.\&quot;},\&quot;lastName\&quot;:{\&quot;type\&quot;:\&quot;string\&quot;,\&quot;description\&quot;:\&quot;The citizen&apos;\&apos;&apos;s last name.\&quot;},\&quot;age\&quot;:{\&quot;description\&quot;:\&quot;Age in years which must be equal to or greater than zero.\&quot;,\&quot;type\&quot;:\&quot;integer\&quot;,\&quot;minimum\&quot;:0},\&quot;city\&quot;:{\&quot;$ref\&quot;:\&quot;types/all-types.json#/definitions/City/properties/name\&quot;},\&quot;identifier\&quot;:{\&quot;$ref\&quot;:\&quot;types/all-types.json#/definitions/Identifier\&quot;}},\&quot;required\&quot;:[\&quot;city\&quot;]}&quot;,
      &quot;contentType&quot;: &quot;application/json&quot;,
      &quot;references&quot;: [
        {
          &quot;name&quot;: &quot;types/all-types.json#/definitions/City/properties/name&quot;,
          &quot;groupId&quot;: &quot;default&quot;,
          &quot;artifactId&quot;: &quot;all-types&quot;,
          &quot;version&quot;: &quot;1&quot;
        },
        {
          &quot;name&quot;: &quot;types/all-types.json#/definitions/Identifier&quot;,
          &quot;groupId&quot;: &quot;default&quot;,
          &quot;artifactId&quot;: &quot;all-types&quot;,
          &quot;version&quot;: &quot;1&quot;
        }
      ]
    }
  }
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The most relevant part here is the name of the reference (apart from the content itself, of course). The name of the reference is what’s going to determine the part of the schema to be used to fill the reference in the main content. In the first case, in the object scenario, the reference name is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;types/all-types.json#/definitions/City&lt;/code&gt; creating a reference to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all-types.json&lt;/code&gt; file in general, and to the city definition in particular. In the second scenario, the property path is used for the reference name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;types/all-types.json#/definitions/City/properties/name&lt;/code&gt;, creating a reference to the all-types.json file in general, and to the city name in particular.&lt;/p&gt;

&lt;p&gt;This is especially important for dereferencing content, since the final result will be different for each schema. In the object scenario, the city object will be inlined within the main schema as follows:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen identifier.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zipCode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The zip code.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The city&apos;s name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whereas in the second scenario, the property one, just the city name will be inlined:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen identifier.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The city&apos;s name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;json-schema-kafka-serde-dereference&quot;&gt;JSON Schema Kafka Serde dereference&lt;/h1&gt;

&lt;p&gt;Now, in order to make good use of all of this, we can use a Kafka producer. For this example, in the Kafka Producer there is one extremely important property we must set, the schema resolver configuration option &lt;em&gt;DEREFERENCE_SCHEMA&lt;/em&gt;. This is the option that will instruct the Schema Resolver to use the dereference parameter when fetching the schema from the registry.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Configure kafka settings&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;BOOTSTRAP_SERVERS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SERVERS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;CLIENT_ID_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Producer-&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACKS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;all&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;KEY_SERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringSerializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;VALUE_SERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;JsonSchemaKafkaSerializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ARTIFACT_RESOLVER_STRATEGY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleTopicIdStrategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EXPLICIT_ARTIFACT_GROUP_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//This is the most important configuration for this particular example.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DEREFERENCE_SCHEMA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;// Create the Kafka producer&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaProducer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this producer we can then send messages to Kafka that adhere to the &lt;em&gt;citizen&lt;/em&gt; schema as follows:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Create the message to send&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;citizen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;City&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;city&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;City&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setZipCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45676&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setCity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setFirstName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setLastName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Send/produce the message on the Kafka Producer&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;producedRecord&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topicName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SUBJECT_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producedRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And consume them with a consumer that is created as follows, instructing the deserializer to use the schema dereferenced with the following configuration property &lt;em&gt;DEREFERENCE_SCHEMA&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;BOOTSTRAP_SERVERS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SERVERS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GROUP_ID_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Consumer-&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ENABLE_AUTO_COMMIT_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTO_COMMIT_INTERVAL_MS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTO_OFFSET_RESET_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;earliest&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;KEY_DESERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringDeserializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;VALUE_DESERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;JsonSchemaKafkaDeserializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ARTIFACT_RESOLVER_STRATEGY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleTopicIdStrategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EXPLICIT_ARTIFACT_GROUP_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;VALIDATION_ENABLED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//This is the most important configuration for this example, that instructs the deserializer to fetch the dereferenced schema.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DEREFERENCE_SCHEMA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Configure Service Registry location&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// No other configuration needed for the deserializer, because the globalId of the schema&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// the deserializer should use is sent as part of the payload.  So the deserializer simply&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// extracts that globalId and uses it to look up the Schema from the registry.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Create the Kafka Consumer&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;KafkaConsumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaConsumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then consume the messages with the consumer configured above like this:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;messageCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsumerRecords&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ofSeconds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;messageCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Do nothing - no messages waiting.&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;No messages waiting...&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Consumed a message: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; @ &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getCity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of doing an HTTP request to get the &lt;em&gt;citizen schema&lt;/em&gt;, another request to fetch the pointers to the references, and additional requests to discover the full hierarchy, a single HTTP request gets the full content of the hierarchy and then uses it to produce/consume messages.&lt;/p&gt;

</description><category term="registry" /><category term="openapi" /><category term="jsonschema" /><category term="asyncapi" /><category term="avro" /><pubDate>Tue, 25 Nov 2025 16:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/11/25/registry-dereference-v3</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/11/25/registry-dereference-v3</guid>
        </item><item>
            <title>Extend Apicurio Registry with Custom Artifact Types at Deployment Time</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;One of the most exciting new features in &lt;strong&gt;Apicurio Registry 3.1.0&lt;/strong&gt; is the ability to extend the registry
with custom artifact types at deployment time, without rebuilding the project. This capability opens up
Registry to support any schema or API definition format your organization uses, even if it’s not natively
supported out of the box.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;the-challenge-supporting-custom-schema-formats&quot;&gt;The Challenge: Supporting Custom Schema Formats&lt;/h1&gt;

&lt;p&gt;Apicurio Registry natively supports many popular schema and API definition formats:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;OpenAPI&lt;/li&gt;
  &lt;li&gt;AsyncAPI&lt;/li&gt;
  &lt;li&gt;Apache Avro&lt;/li&gt;
  &lt;li&gt;JSON Schema&lt;/li&gt;
  &lt;li&gt;Google Protocol Buffers&lt;/li&gt;
  &lt;li&gt;GraphQL&lt;/li&gt;
  &lt;li&gt;WSDL&lt;/li&gt;
  &lt;li&gt;XML Schema (XSD)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what if your organization uses other formats like RAML, Schematron, WADL, API Blueprint, or a
completely custom schema format? Previously, you would have to fork the Registry project, implement
support for your format, and maintain your own custom build.&lt;/p&gt;

&lt;p&gt;Starting with Registry 3.1.0, that’s no longer necessary.&lt;/p&gt;

&lt;h1 id=&quot;the-solution-custom-artifact-types-configuration&quot;&gt;The Solution: Custom Artifact Types Configuration&lt;/h1&gt;

&lt;p&gt;The new custom artifact types feature allows you to configure Registry to support additional artifact
types by providing a JSON configuration file at deployment time. You can implement the (optional) type-specific
logic in three different ways:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Java classes&lt;/strong&gt; - Implement Java interfaces for maximum performance (&lt;strong&gt;requires a custom build of Registry&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;JavaScript/TypeScript&lt;/strong&gt; - Write scripts for easier development and portability&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Webhooks&lt;/strong&gt; - Deploy HTTP endpoints for ultimate flexibility and language independence&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s explore how this works.&lt;/p&gt;

&lt;h1 id=&quot;configuration-overview&quot;&gt;Configuration Overview&lt;/h1&gt;

&lt;p&gt;To add a custom artifact type, you create a JSON configuration file and point Registry to it using the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.artifact-types.config-file&lt;/code&gt; configuration property.&lt;/p&gt;

&lt;p&gt;Here’s the structure of the configuration file:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;includeStandardArtifactTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;artifactTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;artifactType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RAML&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RAML&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The simplest way to model APIs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;application/x-yaml&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scriptLocation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;custom-artifact-types/dist/raml-at.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentAccepter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentValidator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;compatibilityChecker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentCanonicalizer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentDereferencer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;referenceFinder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configuration-properties&quot;&gt;Configuration Properties&lt;/h2&gt;

&lt;p&gt;Let’s break down what each property does:&lt;/p&gt;

&lt;h3 id=&quot;top-level-properties&quot;&gt;Top-Level Properties&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;includeStandardArtifactTypes&lt;/strong&gt;: Set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; to include all built-in types (OpenAPI, Avro, etc.)
alongside your custom types, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; to use only your custom types&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;artifact-type-properties&quot;&gt;Artifact Type Properties&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;artifactType&lt;/strong&gt;: The unique identifier for your artifact type (e.g., “RAML”, “SCHEMATRON”)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;name&lt;/strong&gt;: Display name shown in e.g. logs&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;description&lt;/strong&gt;: Human-readable description of the artifact type&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;contentTypes&lt;/strong&gt;: List of MIME types for this artifact type’s content (e.g., “application/x-yaml”)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;provider-components&quot;&gt;Provider Components&lt;/h3&gt;

&lt;p&gt;Each artifact type can implement these optional components to customize Registry behavior:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;contentAccepter&lt;/strong&gt;: Detects if content belongs to this artifact type (enables auto-detection)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;contentValidator&lt;/strong&gt;: Validates content syntax and structure&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;compatibilityChecker&lt;/strong&gt;: Determines if new versions are compatible with existing ones&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;contentCanonicalizer&lt;/strong&gt;: Normalizes content for consistent comparison&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;contentDereferencer&lt;/strong&gt;: Resolves references to other artifacts&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;referenceFinder&lt;/strong&gt;: Discovers external references in content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each provider component specifies a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; which can be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webhook&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;implementation-approach-1-javascripttypescript&quot;&gt;Implementation Approach 1: JavaScript/TypeScript&lt;/h1&gt;

&lt;p&gt;The JavaScript approach provides a good balance between ease of development and performance. You write
TypeScript (or JavaScript) code that exports specific functions.&lt;/p&gt;

&lt;p&gt;Registry executes JavaScript using the QuickJS engine via the &lt;a href=&quot;https://github.com/Apicurio/quickjs4j&quot;&gt;quickjs4j&lt;/a&gt;
project. Scripts run in a sandboxed environment for security, which means they have limited access to system
resources and cannot perform potentially dangerous operations like file system access or network requests.&lt;/p&gt;

&lt;h2 id=&quot;using-typescript-types-for-better-development-experience&quot;&gt;Using TypeScript Types for Better Development Experience&lt;/h2&gt;

&lt;p&gt;For TypeScript and JavaScript developers, Apicurio provides an npm package with complete type definitions for all
custom artifact type interfaces. This significantly improves the development experience by providing:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Full type safety&lt;/strong&gt; - Catch errors at compile time rather than runtime&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;IDE autocomplete&lt;/strong&gt; - Get intelligent code completion for request and response objects&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Better documentation&lt;/strong&gt; - Types serve as inline documentation for available properties&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Compatibility assurance&lt;/strong&gt; - Ensure your implementation matches Registry’s expectations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&gt;

&lt;p&gt;Install the TypeScript definitions package:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @apicurio/artifact-type-builtins
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;using-the-types&quot;&gt;Using the Types&lt;/h3&gt;

&lt;p&gt;Import the type definitions at the top of your TypeScript file:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ContentAccepterRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ContentValidatorRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ContentValidatorResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;CompatibilityCheckerRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;CompatibilityCheckerResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ContentCanonicalizerRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ContentCanonicalizerResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ContentDereferencerRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ContentDereferencerResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ReferenceFinderRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ReferenceFinderResponse&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@apicurio/artifact-type-builtins&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then apply these types to your function signatures:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Content Validator with types&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ContentValidatorRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ContentValidatorResponse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;violations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Your validation logic here&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ruleViolations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;violations&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Content Accepter with types&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;acceptsContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ContentAccepterRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;typedContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#%RAML 1.0&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Compatibility Checker with types&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;testCompatibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CompatibilityCheckerRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CompatibilityCheckerResponse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Your compatibility checking logic here&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;incompatibleDifferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The typed interfaces provide IDE autocomplete for all properties, making it much easier to discover what data is
available in requests and what structure is expected in responses.&lt;/p&gt;

&lt;h2 id=&quot;example-raml-content-validator&quot;&gt;Example: RAML Content Validator&lt;/h2&gt;

&lt;p&gt;Here’s a simple example that validates RAML content:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;violations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;application/x-yaml&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;violations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Incorrect content type. Expected &apos;application/x-yaml&apos; but found &apos;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#%RAML 1.0&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;violations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Missing &apos;#%RAML 1.0&apos; content header.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ruleViolations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;violations&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configuration-for-javascript&quot;&gt;Configuration for JavaScript&lt;/h2&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;artifactType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RAML&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scriptLocation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;custom-types/raml-artifact-type.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentValidator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The script file is loaded from the classpath, so you can bundle it with your deployment or mount it as
a volume in containerized environments.&lt;/p&gt;

&lt;h1 id=&quot;implementation-approach-2-java-classes&quot;&gt;Implementation Approach 2: Java Classes&lt;/h1&gt;

&lt;p&gt;For maximum performance and full access to the Java ecosystem, you can implement custom artifact types
using Java classes.&lt;/p&gt;

&lt;h2 id=&quot;configuration-for-java&quot;&gt;Configuration for Java&lt;/h2&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;artifactType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RAML&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentValidator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;java&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;classname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;com.example.raml.RamlContentValidator&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Your Java class would implement the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentValidator&lt;/code&gt; interface:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.raml&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io.apicurio.registry.rules.validity.ContentValidator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io.apicurio.registry.content.TypedContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RamlContentValidator&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ContentValidator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ValidityLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvedReferences&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contentStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contentStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#%RAML 1.0&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuleViolationException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Missing &apos;#%RAML 1.0&apos; content header&quot;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Additional validation logic here&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Java class needs to be available on the Registry classpath, typically by creating your own build of Apicurio
Registry (out of scope for this blog post).&lt;/p&gt;

&lt;h1 id=&quot;implementation-approach-3-webhooks&quot;&gt;Implementation Approach 3: Webhooks&lt;/h1&gt;

&lt;p&gt;The webhook approach provides ultimate flexibility by allowing you to implement the logic in any
language and deploy it as a separate service.&lt;/p&gt;

&lt;h2 id=&quot;configuration-for-webhooks&quot;&gt;Configuration for Webhooks&lt;/h2&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;artifactType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RAML&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentValidator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;webhook&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://custom-validator-service:8080/validate&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;webhook-api-contract&quot;&gt;Webhook API Contract&lt;/h2&gt;

&lt;p&gt;Registry will POST a JSON request to your webhook endpoint and expect a JSON response. For example, the
content validator webhook receives:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SYNTAX_ONLY&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;function&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;validate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;THE_ACTUAL_RAML_CONTENT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;application/x-yaml&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resolvedReferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And should return:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ruleViolations&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Missing &apos;#%RAML 1.0&apos; header&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The complete webhook API specification is defined in an OpenAPI document included with Registry at
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;META-INF/artifact-type-webhooks.json&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;use-cases&quot;&gt;Use Cases&lt;/h1&gt;

&lt;p&gt;This feature is particularly valuable for organizations that:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Use proprietary schema formats&lt;/strong&gt; - Internal schema definition languages can now be managed in
Registry&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Need RAML support&lt;/strong&gt; - RAML users can add full Registry support for their API definitions&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Work with legacy formats&lt;/strong&gt; - Support for older standards like WADL or WSDL 1.1&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Have domain-specific schemas&lt;/strong&gt; - Schematron for healthcare, custom XML schemas for finance, etc.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Want to experiment&lt;/strong&gt; - Try out emerging schema formats without waiting for official Registry
support&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;component-reference&quot;&gt;Component Reference&lt;/h1&gt;

&lt;p&gt;Each custom artifact type can implement the following optional components. Below are the contracts for each
implementation approach.&lt;/p&gt;

&lt;h2 id=&quot;content-accepter&quot;&gt;Content Accepter&lt;/h2&gt;

&lt;p&gt;Auto-detects if content belongs to this artifact type (enables automatic type detection when no explicit
type is provided).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;acceptsContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.typedContent.content - the actual content&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.typedContent.contentType - the MIME type&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Returns: true if this artifact type accepts the content&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;typedContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#%RAML 1.0&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Webhook:&lt;/strong&gt; POST to configured URL&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;typedContent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;application/x-yaml&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;accepted&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt; Implement &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/schema-util/common/src/main/java/io/apicurio/registry/content/ContentAccepter.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentAccepter&lt;/code&gt;&lt;/a&gt; interface&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;accepts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;content-validator&quot;&gt;Content Validator&lt;/h2&gt;

&lt;p&gt;Validates content syntax and structure according to the artifact type’s rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.level - validation level (e.g., &quot;SYNTAX_ONLY&quot;, &quot;FULL&quot;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.content - TypedContent with content and contentType&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.resolvedReferences - array of resolved references&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Returns: object with ruleViolations array&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ruleViolations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Error message&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/path/to/error&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Webhook:&lt;/strong&gt; POST to configured URL&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SYNTAX_ONLY&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;function&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;validate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resolvedReferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ruleViolations&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Error message&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/path/to/error&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt; Implement &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/schema-util/common/src/main/java/io/apicurio/registry/rules/validity/ContentValidator.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentValidator&lt;/code&gt;&lt;/a&gt; interface&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ValidityLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvedReferences&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuleViolationException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;compatibility-checker&quot;&gt;Compatibility Checker&lt;/h2&gt;

&lt;p&gt;Determines if a new version is compatible with existing versions according to the configured compatibility
level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;testCompatibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.level - compatibility level (e.g., &quot;BACKWARD&quot;, &quot;FORWARD&quot;, &quot;FULL&quot;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.existingArtifacts - array of existing TypedContent&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.proposedArtifact - new TypedContent to check&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.resolvedReferences - array of resolved references&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Returns: object with incompatibleDifferences array&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;incompatibleDifferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Incompatibility description&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Webhook:&lt;/strong&gt; POST to configured URL&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;BACKWARD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;existingArtifacts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;proposedArtifact&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resolvedReferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;incompatibleDifferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Incompatibility&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt; Implement &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/schema-util/common/src/main/java/io/apicurio/registry/rules/compatibility/CompatibilityChecker.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CompatibilityChecker&lt;/code&gt;&lt;/a&gt; interface&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;CompatibilityExecutionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;testCompatibility&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CompatibilityLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;existingArtifacts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proposedArtifact&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvedReferences&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;content-canonicalizer&quot;&gt;Content Canonicalizer&lt;/h2&gt;

&lt;p&gt;Normalizes content to a canonical form for consistent comparison and duplicate detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canonicalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.content - TypedContent with content and contentType&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.resolvedReferences - array of resolved references&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Returns: object with canonicalized typedContent&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canonical&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;normalizeWhitespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;typedContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canonical&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Webhook:&lt;/strong&gt; POST to configured URL&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resolvedReferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;typedContent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;canonical content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt; Implement &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/schema-util/common/src/main/java/io/apicurio/registry/content/canon/ContentCanonicalizer.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentCanonicalizer&lt;/code&gt;&lt;/a&gt; interface&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;canonicalize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvedReferences&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;content-dereferencer&quot;&gt;Content Dereferencer&lt;/h2&gt;

&lt;p&gt;Resolves references to other artifacts, either by inlining referenced content (dereference) or rewriting
references to URLs (rewriteReferences).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dereference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.content - TypedContent with content and contentType&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.resolvedReferences - array of { name, content, contentType }&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.function - &quot;dereference&quot; or &quot;rewriteReferences&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.resolvedReferenceUrls - array of { name, url } (for rewrite)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Returns: object with dereferenced typedContent&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dereferenced&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;inlineReferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                          &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resolvedReferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;typedContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dereferenced&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rewriteReferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Same as dereference but rewrites refs to URLs instead of inlining&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rewritten&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;replaceWithUrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resolvedReferenceUrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;typedContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rewritten&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Webhook:&lt;/strong&gt; POST to configured URL&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;(dereference)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;function&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dereference&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resolvedReferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ref.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;(rewriteReferences)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;function&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;rewriteReferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resolvedReferenceUrls&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ref.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://registry/...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;typedContent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dereferenced content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt; Implement &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/schema-util/common/src/main/java/io/apicurio/registry/content/dereference/ContentDereferencer.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentDereferencer&lt;/code&gt;&lt;/a&gt; interface&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dereference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvedReferences&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rewriteReferences&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvedReferenceUrls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;reference-finder&quot;&gt;Reference Finder&lt;/h2&gt;

&lt;p&gt;Discovers external references within content (used by tooling like the Maven plugin for automatic reference
resolution).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;findExternalReferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// request.typedContent - TypedContent with content and contentType&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Returns: object with externalReferences array&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;refs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parseReferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;typedContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;externalReferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;fullReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;schemas/ref.json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;schemas/ref.json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Webhook:&lt;/strong&gt; POST to configured URL&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;typedContent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;externalReferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fullReference&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ref.json#/Schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resource&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ref.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;component&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/Schema&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt; Implement &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/schema-util/common/src/main/java/io/apicurio/registry/content/refs/ReferenceFinder.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferenceFinder&lt;/code&gt;&lt;/a&gt; interface&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ExternalReference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;findExternalReferences&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TypedContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;deployment-example&quot;&gt;Deployment Example&lt;/h1&gt;

&lt;p&gt;Here’s how to deploy Registry with a custom RAML artifact type using Docker:&lt;/p&gt;

&lt;h2 id=&quot;1-create-your-configuration-file-raml-configjson&quot;&gt;1. Create your configuration file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raml-config.json&lt;/code&gt;):&lt;/h2&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;includeStandardArtifactTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;artifactTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;artifactType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RAML&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RAML&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RESTful API Modeling Language&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;application/x-yaml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scriptLocation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;raml-artifact-type.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentAccepter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentValidator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;compatibilityChecker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-create-your-javascript-implementation-raml-artifact-typejs&quot;&gt;2. Create your JavaScript implementation (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raml-artifact-type.js&lt;/code&gt;)&lt;/h2&gt;

&lt;h2 id=&quot;3-deploy-with-docker&quot;&gt;3. Deploy with Docker:&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:8080 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;/raml-config.json:/config/artifact-types.json &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;/raml-artifact-type.js:/deployments/raml-artifact-type.js &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; apicurio.artifact-types.config-file&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/config/artifact-types.json &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  apicurio/apicurio-registry:3.1.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;4-verify-it-works&quot;&gt;4. Verify it works:&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl http://localhost:8080/apis/registry/v3/admin/config/artifactTypes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should see your custom RAML type in the response along with all the standard types.&lt;/p&gt;

&lt;h1 id=&quot;performance-considerations&quot;&gt;Performance Considerations&lt;/h1&gt;

&lt;p&gt;When choosing an implementation approach, consider:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Java classes&lt;/strong&gt; - Fastest performance, no serialization overhead, but requires a custom build of Registry&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;JavaScript&lt;/strong&gt; - Good performance, easier to develop and deploy, isolated execution&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Webhooks&lt;/strong&gt; - Most flexible, language-agnostic, but adds network latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For high-throughput scenarios, Java classes offer the best performance. For moderate loads with frequent
updates, JavaScript provides a good balance. For maximum flexibility or when integrating with existing
services, webhooks are ideal.&lt;/p&gt;

&lt;h1 id=&quot;limitations&quot;&gt;Limitations&lt;/h1&gt;

&lt;p&gt;A few things to keep in mind:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The JavaScript runtime uses QuickJS, which supports ES2020 but not all Node.js APIs&lt;/li&gt;
  &lt;li&gt;Webhook endpoints must be accessible from the Registry deployment&lt;/li&gt;
  &lt;li&gt;Java classes must be compatible with the Registry’s Quarkus version&lt;/li&gt;
  &lt;li&gt;Content extraction is not yet supported for custom types&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;looking-forward&quot;&gt;Looking Forward&lt;/h1&gt;

&lt;p&gt;This feature represents a significant step toward making Apicurio Registry truly universal. We’re
excited to see what artifact types the community will add support for.&lt;/p&gt;

&lt;p&gt;Some ideas we’ve heard interest in:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;RAML 0.8 and 1.0&lt;/li&gt;
  &lt;li&gt;API Blueprint&lt;/li&gt;
  &lt;li&gt;Schematron&lt;/li&gt;
  &lt;li&gt;XACML&lt;/li&gt;
  &lt;li&gt;Custom DSLs for various domains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’d love to hear about your use cases! If you implement support for a custom artifact type, consider
sharing it with the community as a reusable module.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;get-started&quot;&gt;Get Started&lt;/h1&gt;

&lt;p&gt;Ready to try it out? Here are your next steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Check out the working example&lt;/strong&gt; - See a complete custom artifact type implementation in the
&lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/examples/custom-artifact-type&quot;&gt;custom-artifact-type example&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Read the documentation&lt;/strong&gt; for more details about deploying Apicurio Registry&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Try a simple implementation&lt;/strong&gt; using the JavaScript approach&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Join the discussion&lt;/strong&gt; on our &lt;a href=&quot;https://apicurio.zulipchat.com/&quot;&gt;Zulip chat&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more information:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/examples/custom-artifact-type&quot;&gt;Custom Artifact Type Example&lt;/a&gt; - Complete working example&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/@apicurio/artifact-type-builtins&quot;&gt;TypeScript Type Definitions&lt;/a&gt; - npm package with TypeScript types&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Apicurio/apicurio-registry&quot;&gt;Apicurio Registry GitHub Repository&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.apicur.io/registry/docs/&quot;&gt;Apicurio Registry Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/main/common/src/main/resources/META-INF/artifact-type-webhooks.json&quot;&gt;Webhook API Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can’t wait to see what you build with this new capability!&lt;/p&gt;
</description><category term="announcement" /><category term="registry" /><category term="features" /><pubDate>Mon, 27 Oct 2025 02:13:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/10/27/custom-artifact-types</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/10/27/custom-artifact-types</guid>
        </item><item>
            <title>Apicurio Studio Fully Deprecated: Functionality Integrated into Apicurio Registry 3.1.0</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;We have an important announcement to make regarding the future of Apicurio Studio and how we plan to continue
supporting our users who rely on its capabilities.&lt;/p&gt;

&lt;p&gt;As of today, &lt;strong&gt;Apicurio Studio is fully deprecated&lt;/strong&gt;. However, before you panic, let me explain what this means
and how we’re ensuring that Studio’s valuable functionality remains available to you through Apicurio Registry.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;the-challenge-limited-bandwidth-two-projects&quot;&gt;The Challenge: Limited Bandwidth, Two Projects&lt;/h1&gt;

&lt;p&gt;The Apicurio team has come to an important realization: we simply do not have the bandwidth to author and maintain
both Apicurio Studio and Apicurio Registry as separate projects. Between feature development, bug fixes, security
updates, and community support, maintaining two distinct codebases has stretched our resources too thin.&lt;/p&gt;

&lt;p&gt;After careful consideration, we’ve decided to continue our primary focus on Apicurio Registry, which has become the
cornerstone of schema and API management for many organizations and is the foundation for multiple Red Hat products.&lt;/p&gt;

&lt;p&gt;But here’s the critical part: &lt;strong&gt;we recognize that Studio is a popular and useful project&lt;/strong&gt;, and we didn’t want to
abandon the users who find it helpful. This commitment to supporting our Studio users has driven us to find a better
solution.&lt;/p&gt;

&lt;h1 id=&quot;the-solution-studio-features-built-into-registry-310&quot;&gt;The Solution: Studio Features Built into Registry 3.1.0&lt;/h1&gt;

&lt;p&gt;Rather than leaving Studio users without a path forward, we’ve directly incorporated Studio’s editing capabilities
into the Apicurio Registry user experience. Starting with &lt;strong&gt;Apicurio Registry 3.1.0&lt;/strong&gt;, all of the core Studio
functionality is available as an opt-in feature within Registry itself.&lt;/p&gt;

&lt;p&gt;This means you get the best of both worlds:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The robust schema/API management capabilities of Registry&lt;/li&gt;
  &lt;li&gt;The intuitive editing experience of Studio&lt;/li&gt;
  &lt;li&gt;A single project to install, configure, and maintain&lt;/li&gt;
  &lt;li&gt;Unified lifecycle management for your API designs&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;how-it-works-the-new-drafts-feature&quot;&gt;How It Works: The New “Drafts” Feature&lt;/h1&gt;

&lt;p&gt;The Studio functionality in Registry is built on top of a powerful new feature called &lt;strong&gt;Drafts&lt;/strong&gt;. Let me explain how
this works.&lt;/p&gt;

&lt;h2 id=&quot;understanding-draft-state&quot;&gt;Understanding Draft State&lt;/h2&gt;

&lt;p&gt;In Apicurio Registry 3.1.0, we’ve introduced a new artifact version state: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DRAFT&lt;/code&gt;. This state fundamentally changes
one of Registry’s core behaviors.&lt;/p&gt;

&lt;p&gt;Normally, artifact version content in Registry is immutable - once created, it cannot be changed. This immutability
is crucial for production schema registries where consistency and reliability are paramount. However, during the
design and development phase, you need the flexibility to iteratively edit and refine your API specifications.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DRAFT&lt;/code&gt; state solves this problem. When an artifact version is in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DRAFT&lt;/code&gt; state &lt;strong&gt;and&lt;/strong&gt; the content
mutability feature is enabled, you can update the content of that version as many times as needed. Once you’re
satisfied with your design, you can transition it out of draft state, at which point it becomes immutable like any
other Registry artifact version.&lt;/p&gt;

&lt;h2 id=&quot;enabling-studio-features&quot;&gt;Enabling Studio Features&lt;/h2&gt;

&lt;p&gt;To enable the Draft Mutability feature, and unlock Studio functionality in Registry, you need to set one 
configuration property:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apicurio.rest.mutability.artifact-version-content.enabled=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When this feature is enabled, several new capabilities appear in the Registry UI:&lt;/p&gt;

&lt;h3 id=&quot;1-drafts-section&quot;&gt;1. Drafts Section&lt;/h3&gt;

&lt;p&gt;A new “Drafts” section becomes available in the UI, providing:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Easy searching&lt;/strong&gt; for drafts that can be edited&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Easy creation&lt;/strong&gt; of new drafts, including from pre-defined templates&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Draft version creation&lt;/strong&gt; from existing artifact versions (whether they’re drafts or published versions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you a centralized place to manage all your in-progress Schema and API designs.&lt;/p&gt;

&lt;h3 id=&quot;2-edit-draft-button&quot;&gt;2. Edit Draft Button&lt;/h3&gt;

&lt;p&gt;When viewing any artifact version that is in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DRAFT&lt;/code&gt; state, you’ll see a new “Edit Draft” button. Clicking this
button launches the familiar Apicurio Studio editor experience, where you can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visually edit OpenAPI and AsyncAPI specifications&lt;/li&gt;
  &lt;li&gt;Make changes using the intuitive Studio interface&lt;/li&gt;
  &lt;li&gt;Save your changes directly back to Registry, updating the version content&lt;/li&gt;
  &lt;li&gt;Iterate on your design as many times as needed before finalizing&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;try-it-out-quick-evaluation&quot;&gt;Try It Out: Quick Evaluation&lt;/h1&gt;

&lt;p&gt;Want to see this in action? Here’s the fastest way to evaluate the new Studio features in Registry:&lt;/p&gt;

&lt;h2 id=&quot;using-docker-compose&quot;&gt;Using Docker Compose&lt;/h2&gt;

&lt;p&gt;We’ve created a pre-configured Docker Compose setup that runs Registry with Studio features enabled. Here’s how to
use it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Download the docker-compose.yml file:&lt;/strong&gt;
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; docker-compose.yml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/Apicurio/apicurio-registry/main/distro/docker-compose/in-memory-with-studio/docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Start the services:&lt;/strong&gt;
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker compose up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This will start two containers:&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;Apicurio Registry API (on port 8080)&lt;/li&gt;
      &lt;li&gt;Apicurio Registry UI (on port 8888)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Access the UI:&lt;/strong&gt;
Open your browser to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8888&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The docker-compose configuration automatically enables the content mutability feature with this environment
variable:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apicurio.rest.mutability.artifact-version-content.enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-individual-docker-containers&quot;&gt;Using Individual Docker Containers&lt;/h2&gt;

&lt;p&gt;If you prefer to run the containers separately:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Start the Registry backend:&lt;/strong&gt;
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:8080 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; apicurio.rest.mutability.artifact-version-content.enabled&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  apicurio/apicurio-registry:3.1.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Start the Registry UI:&lt;/strong&gt;
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8888:8080 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  apicurio/apicurio-registry-ui:3.1.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Access the UI:&lt;/strong&gt;
Open your browser to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8888&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that this configuration uses in-memory storage, which is perfect for evaluation but not suitable for production
use.&lt;/p&gt;

&lt;h1 id=&quot;what-this-means-for-studio-users&quot;&gt;What This Means for Studio Users&lt;/h1&gt;

&lt;p&gt;If you’re currently using Apicurio Studio, here’s what you need to know:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No panic needed:&lt;/strong&gt; All core editing functionality has been preserved in Registry&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Migration path:&lt;/strong&gt; You can import your existing API designs into Registry as draft artifacts&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improved workflow:&lt;/strong&gt; You now get both editing capabilities and full schema registry features in one platform&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Continued support:&lt;/strong&gt; By consolidating into Registry, we can provide better support and more frequent updates&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;looking-forward&quot;&gt;Looking Forward&lt;/h1&gt;

&lt;p&gt;This integration represents a significant step forward for the Apicurio ecosystem. By combining Studio’s editing
capabilities with Registry’s robust schema management, we’re creating a more comprehensive solution for API-first
development.&lt;/p&gt;

&lt;p&gt;We’re committed to continuing to improve and enhance these capabilities. As we move forward, you can expect:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Regular updates and improvements to the editing experience&lt;/li&gt;
  &lt;li&gt;Better integration between draft editing and Registry’s other features&lt;/li&gt;
  &lt;li&gt;Continued support for the API design workflows you rely on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We believe this consolidation will allow us to deliver more value to our users while being more sustainable for our
development team.&lt;/p&gt;

&lt;p&gt;Thank you for your understanding, your patience, and most importantly, your continued use of Apicurio projects.
We’re excited about this new direction and look forward to your feedback!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;For more information:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.apicur.io/registry/getting-started/&quot;&gt;Apicurio Registry Getting Started Guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Apicurio/apicurio-registry&quot;&gt;Apicurio Registry GitHub Repository&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://apicurio.zulipchat.com/&quot;&gt;Apicurio Community on Zulip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description><category term="announcement" /><category term="studio" /><category term="registry" /><pubDate>Thu, 23 Oct 2025 03:01:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/10/23/studio-fully-deprecated</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/10/23/studio-fully-deprecated</guid>
        </item><item>
            <title>Introducing MCP server for Apicurio Registry 3</title><dc:creator>Jakub Senko</dc:creator>
            <author>jsenko@redhat.com</author><description>&lt;p&gt;In this blog post, we’re introducing a new way you can interact with Apicurio Registry 3. We’ve created a &lt;strong&gt;Model Context Protocol (MCP) server for Apicurio Registry 3&lt;/strong&gt;, enabling Large Language Models (LLMs) to communicate directly with your Registry instance. You can use it to explore and analyze your content by asking natural language questions, make changes, and generate new schemas.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-is-mcp&quot;&gt;What is MCP?&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://modelcontextprotocol.io/introduction&quot;&gt;Model Context Protocol (MCP)&lt;/a&gt; is an open protocol that standardizes how applications provide context to LLMs. MCP provides a standardized method to connect AI models into diverse data sources and provide them with new executable functionality.&lt;/p&gt;

&lt;p&gt;The protocol operates on a client-server architecture, where an &lt;strong&gt;MCP Host&lt;/strong&gt; (like Claude Desktop, which we’ll use in a moment) is connecting to one or more &lt;strong&gt;MCP Servers&lt;/strong&gt;. These servers are lightweight programs that expose specific capabilities through the protocol.&lt;/p&gt;

&lt;p&gt;There are 3 types of functionality that an MCP server can expose:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Tools&lt;/li&gt;
  &lt;li&gt;Resources&lt;/li&gt;
  &lt;li&gt;Prompts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apicurio Registry MCP server uses &lt;strong&gt;tools&lt;/strong&gt; and &lt;strong&gt;prompts&lt;/strong&gt;, which we’ll explore in a moment.&lt;/p&gt;

&lt;h2 id=&quot;apicurio-registry-meets-llms&quot;&gt;Apicurio Registry Meets LLMs!&lt;/h2&gt;

&lt;p&gt;Our new MCP server for Apicurio Registry acts as a wrapper over existing REST API calls and helps LLMs understand the concepts of Apicurio Registry and what kind of operations are available. This means your LLMs can now execute REST API calls against your Apicurio Registry instance.&lt;/p&gt;

&lt;p&gt;Let’s see what MCP can do by asking a simple question about the Apicurio Registry server running in the background:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; What is the version of Apicurio Registry server?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Reply:&lt;/strong&gt; I’ll check the version of the Apicurio Registry server for you.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_server_info&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;The Apicurio Registry server is running version &lt;strong&gt;3.0.8-SNAPSHOT&lt;/strong&gt;. This is an in-memory deployment built on April 24, 2025. The server is described as a “High performance, runtime registry for schemas and API designs.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/get-version.png&quot; alt=&quot;What is the version of Apicurio Registry server?&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We see that Claude found the correct &lt;strong&gt;tool&lt;/strong&gt; named &lt;strong&gt;&lt;em&gt;get_server_info&lt;/em&gt;&lt;/strong&gt;, and called it to get the requested information. The MCP server makes a REST API call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/system/info&lt;/code&gt; on behalf of the LLM.&lt;/p&gt;

&lt;p&gt;Because tools might perform sensitive or potentially destructive operations, Claude Desktop will ask for confirmation:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/use-tool-prompt.png&quot; alt=&quot;Claude ask for confirmation before using a tool.&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;use-case-generating-content&quot;&gt;Use-case: Generating Content&lt;/h2&gt;

&lt;p&gt;One of the great uses for generative AI is, well, generating content. Let’s ask Claude to add some content into Apicurio Registry:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; Create 2 groups, and generate 4 new artifact versions in those groups. The content should be concise and an example of a real use-case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer&lt;/strong&gt; I’ll create two groups in the Apicurio Registry and generate artifact versions with real-world use cases. Let me start by creating the groups and then add meaningful artifacts to them.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_group&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_group&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now I’ll create artifacts and versions in each group. Let me start with the e-commerce group:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now let me create artifacts in the messaging-events group:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Perfect! I’ve successfully created 2 groups and 4 artifact versions with real-world use cases:&lt;/p&gt;

  &lt;h2 id=&quot;groups-created&quot;&gt;Groups Created:&lt;/h2&gt;

  &lt;ol&gt;
    &lt;li&gt;
      &lt;h3 id=&quot;ecommerce-apis-group&quot;&gt;ecommerce-apis Group&lt;/h3&gt;

      &lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; API schemas and specifications for e-commerce platform services&lt;/p&gt;

      &lt;p&gt;&lt;strong&gt;Labels:&lt;/strong&gt; domain: ecommerce, team: platform, environment: production&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;h3 id=&quot;messaging-events-group&quot;&gt;messaging-events Group&lt;/h3&gt;

      &lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Event schemas for real-time messaging and notifications system&lt;/p&gt;

      &lt;p&gt;&lt;strong&gt;Labels:&lt;/strong&gt; domain: messaging, team: communications, type: events&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;

  &lt;h2 id=&quot;artifact-versions-created&quot;&gt;Artifact Versions Created:&lt;/h2&gt;

  &lt;h3 id=&quot;e-commerce-apis-group&quot;&gt;E-commerce APIs Group:&lt;/h3&gt;

  &lt;ol&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;Product Catalog API v1.0 (OpenAPI)&lt;/strong&gt;&lt;/p&gt;

      &lt;ul&gt;
        &lt;li&gt;REST API specification for managing product catalog operations&lt;/li&gt;
        &lt;li&gt;Includes endpoints for listing and creating products with proper schema definitions&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;Order Schema v1.0 (JSON Schema)&lt;/strong&gt;&lt;/p&gt;

      &lt;ul&gt;
        &lt;li&gt;Validation schema for order data with customer info, items, and order details&lt;/li&gt;
        &lt;li&gt;Enforces proper order ID patterns and required fields&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;/ol&gt;

  &lt;h3 id=&quot;messaging-events-group-1&quot;&gt;Messaging Events Group:&lt;/h3&gt;

  &lt;ol&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;User Notification Event v1.0 (Avro)&lt;/strong&gt;&lt;/p&gt;

      &lt;ul&gt;
        &lt;li&gt;Schema for real-time user notifications across multiple channels (email, SMS, push, in-app)&lt;/li&gt;
        &lt;li&gt;Includes priority levels and metadata support for Kafka streaming&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;strong&gt;Chat Message Event v1.0 (JSON Schema)&lt;/strong&gt;&lt;/p&gt;

      &lt;ul&gt;
        &lt;li&gt;Schema for real-time chat messages with WebSocket streaming support&lt;/li&gt;
        &lt;li&gt;Supports text messages, attachments, and message metadata with encryption&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;Each artifact version represents a common real-world scenario where schema registries are essential for maintaining data consistency and API compatibility across microservices and event-driven architectures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Apicurio Registry has a hierarchical organizational structure: &lt;strong&gt;Artifacts&lt;/strong&gt; represent an evolving piece of content (schema). Artifacts are comprised of a list&lt;sup&gt;1&lt;/sup&gt; of immutable&lt;sup&gt;1&lt;/sup&gt; &lt;strong&gt;versions&lt;/strong&gt;, and &lt;strong&gt;groups&lt;/strong&gt; contain a list of artifacts to provide a high-level organizational unit.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;[1] NOTE: Apicurio Registry supports artifact branches and mutable versions for advanced use cases.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because of the MCP protocol, Claude is able to understand and work with this structure and use additional features, such as labels. We see that first 2 groups are created using the &lt;strong&gt;&lt;em&gt;create_group&lt;/em&gt;&lt;/strong&gt; tool, then 4 artifacts using the &lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt; tool, and finally the content using the &lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt; tool:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/generate-content-tools.png&quot; alt=&quot;Claude uses tools correctly to add generated content to Apicurio Registry.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a look at the Apicurio Registry web console to see the generated content:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/generate-content-ui.png&quot; alt=&quot;Generated content in Apicurio Registry web console.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We see that Claude can generate OpenAPI, JSON, and Avro schemas, and use meaningful metadata, such as descriptions and labels. Let’s take a closer look at an artifact version, for example the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chat-message-event&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/generate-content-ui-artifact-version.png&quot; alt=&quot;Generated artifact version metadata in Apicurio Registry web console.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And its content:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/generate-content-ui-artifact-content.png&quot; alt=&quot;Generated artifact version content in Apicurio Registry web console.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We have not specified any constraints on the generated data so far. This type of usage is great for providing data for development and testing. Instead of keeping a library of example schemas and manually adding them to a fresh Apicurio Registry instance, we can ask the LLM to do the work for us.&lt;/p&gt;

&lt;h2 id=&quot;use-case-complex-updates&quot;&gt;Use-case: Complex Updates&lt;/h2&gt;

&lt;p&gt;We’ve noticed that Claude automatically added some labels to the groups:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;messaging-events&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;domain=messaging&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;team=communications&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;type=events&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ecommerce-apis&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;&lt;em&gt;environment=production&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;domain=ecommerce&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;team=platform&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take advantage of this to show a more complex workflow. Imagine we have two deployment environments: production and development. We can use labels and groups to reorganize the schemas with the help of AI:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; Remove the “type” label from the “messaging-events” group, and add an “environment=production” label. Then, for each group used for production, create an equivalent group that can be used for development. Copy the content to the new groups.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer&lt;/strong&gt; I’ll help you manage the groups in your Apicurio Registry. Let me start by examining the current groups and then make the requested changes.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_groups&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now let me get the metadata for the “messaging-events” group to see its current labels:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_group_metadata&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Perfect! I can see the current structure. Now I’ll:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;Update the “messaging-events” group to remove the “type” label and add “environment=production”&lt;/li&gt;
    &lt;li&gt;Create development equivalents for both production groups&lt;/li&gt;
    &lt;li&gt;Copy the artifacts from production groups to their development counterparts&lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;Let’s start by updating the messaging-events group:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;update_group_metadata&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now let’s create the development equivalent groups. First, let me list the artifacts in the production groups to understand what needs to be copied:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_artifacts&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;list_artifacts&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now I’ll create the development groups:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_group&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_group&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now I need to copy the artifacts from the production groups to the development groups. Let me get the content and metadata for each artifact:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now I’ll create the artifacts in the development groups. Let me start by creating the artifacts themselves, then adding the content:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now let’s create the ecommerce artifacts in the development group:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Perfect! Let me verify the final result by listing all groups to show the completed changes:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_groups&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

  &lt;p&gt;I have successfully completed all the requested changes:&lt;/p&gt;

  &lt;h2 id=&quot;-updated-messaging-events-group&quot;&gt;✅ Updated “messaging-events” group:&lt;/h2&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Removed&lt;/strong&gt; the “type=events” label&lt;/li&gt;
    &lt;li&gt;Added “environment=production” label&lt;/li&gt;
    &lt;li&gt;The group now has labels: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain=messaging&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;team=communications&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environment=production&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h2 id=&quot;-created-development-groups&quot;&gt;✅ Created development groups:&lt;/h2&gt;

  &lt;h3 id=&quot;messaging-events-dev&quot;&gt;messaging-events-dev:&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;Description: “Event schemas for real-time messaging and notifications system (Development)”&lt;/li&gt;
    &lt;li&gt;Labels: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environment=development&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain=messaging&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;team=communications&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;Contains copies of:&lt;/p&gt;

      &lt;ul&gt;
        &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user-notification-event&lt;/code&gt; (AVRO schema)&lt;/li&gt;
        &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chat-message-event&lt;/code&gt; (JSON schema)&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3 id=&quot;ecommerce-apis-dev&quot;&gt;ecommerce-apis-dev:&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;Description: “API schemas and specifications for e-commerce platform services (Development)”&lt;/li&gt;
    &lt;li&gt;Labels: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environment=development&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain=ecommerce&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;team=platform&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;Contains copies of:&lt;/p&gt;

      &lt;ul&gt;
        &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;product-catalog-api&lt;/code&gt; (OpenAPI spec)&lt;/li&gt;
        &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;order-schema&lt;/code&gt; (JSON schema)&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;/ul&gt;

  &lt;h2 id=&quot;-all-content-copied&quot;&gt;✅ All content copied:&lt;/h2&gt;

  &lt;p&gt;All artifacts from the production groups have been duplicated to their respective development groups with identical content but appropriate development-specific labels and descriptions.&lt;/p&gt;

  &lt;p&gt;Your Apicurio Registry now has a clear separation between production and development environments, with all production groups properly labeled with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environment=production&lt;/code&gt; and corresponding development groups with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environment=development&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s take a look at the result:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/reorganize-groups-after.png&quot; alt=&quot;Reorganized group structure in Apicurio Registry web console.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The group’s labels have been updated as requested:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/reorganize-groups-after-group.png&quot; alt=&quot;Reorganized group metadata in Apicurio Registry web console.&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;use-case-exploration&quot;&gt;Use-case: Exploration&lt;/h2&gt;

&lt;p&gt;Let’s take a look at some of the exploration capabilities that the MCP server provides. Apicurio Registry does not currently have a REST endpoint for a full-text search, but the LLM can still look for information about the content by considering artifact, group, and version metadata (e.g. names, descriptions, and labels).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; Which production artifact contains an event ID field, and what is the type of that field?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; I’ll help you find production artifacts that contain an event ID field. Let me search through your Apicurio Registry to identify artifacts with event ID fields.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_groups&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now let me look at the production groups to find artifacts that might contain event ID fields. I’ll start with the production messaging events group since that’s most likely to contain event-related schemas.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_artifacts&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Let me also check the ecommerce-apis production group for any event-related artifacts:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_artifacts&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now let me examine the content of these artifacts to find which ones contain event ID fields. I’ll start with the messaging event artifacts since they’re most likely to have event IDs:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Based on my search through your production artifacts, I found one artifact that contains an event ID field:&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Artifact:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user-notification-event&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;messaging-events&lt;/code&gt; group&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Event ID Field Details:&lt;/strong&gt;&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Field Name&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eventId&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Type&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; (Avro schema type)&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; “Unique identifier for the notification event”&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;This is an Avro schema artifact that defines the structure for user notification events in your messaging system. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eventId&lt;/code&gt; field is defined as a required string type that serves as a unique identifier for each notification event.&lt;/p&gt;

  &lt;p&gt;The other artifacts I examined (chat-message-event and order-schema) do not contain an “eventId” field specifically, though they do have other ID fields like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;messageId&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orderId&lt;/code&gt; respectively.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We see that Claude quickly determined the correct artifact, without the ability to perform a full-text search. We have not tested this functionality against an Apicurio Registry with thousands of schemas, but I expect that with a good prompt, the search time could be limited to get a very good result.&lt;/p&gt;

&lt;h2 id=&quot;use-case-analysis&quot;&gt;Use-case: Analysis&lt;/h2&gt;

&lt;p&gt;I have cleaned my Apicurio Registry instance and created a new artifact &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log-message-schema&lt;/code&gt; that represents a simplified logging event in some kind of log storage and analysis service. The first version is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;title&quot;: &quot;Log Message Event&quot;,  
  &quot;required&quot;: [
    &quot;timestamp&quot;,
    &quot;level&quot;,
    &quot;message&quot;
  ],
  &quot;properties&quot;: {
    &quot;timestamp&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;format&quot;: &quot;date-time&quot;,
      &quot;description&quot;: &quot;ISO 8601 timestamp when the log event occurred.&quot;
    },
    &quot;level&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;enum&quot;: [
        &quot;DEBUG&quot;,
        &quot;INFO&quot;,
        &quot;WARN&quot;,
        &quot;ERROR&quot;
      ]
    },
    &quot;message&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;minLength&quot;: 1
    },
    &quot;metadata&quot;: {
      &quot;type&quot;: &quot;object&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the second version, I’ve added support for tracing information:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;title&quot;: &quot;Log Message Event with Tracing&quot;,
  &quot;required&quot;: [
    &quot;timestamp&quot;,
    &quot;level&quot;,
    &quot;message&quot;
  ],
  &quot;properties&quot;: {
    &quot;timestamp&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;format&quot;: &quot;date-time&quot;,
      &quot;description&quot;: &quot;ISO 8601 timestamp when the log event occurred.&quot;
    },
    &quot;level&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;enum&quot;: [
        &quot;DEBUG&quot;,
        &quot;INFO&quot;,
        &quot;WARN&quot;,
        &quot;ERROR&quot;
      ]
    },
    &quot;message&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;minLength&quot;: 1
    },
    &quot;tracing&quot;: {
      &quot;type&quot;: &quot;object&quot;,
      &quot;properties&quot;: {
        &quot;traceId&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;pattern&quot;: &quot;^[a-fA-F0-9]{32}$&quot;
        },
        &quot;spanId&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;pattern&quot;: &quot;^[a-fA-F0-9]{16}$&quot;
        },
        &quot;parentSpanId&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;pattern&quot;: &quot;^[a-fA-F0-9]{16}$&quot;
        }
      }
    },
    &quot;metadata&quot;: {
      &quot;type&quot;: &quot;object&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can ask Claude to summarize differences between these versions:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; What are the differences between versions of the “log-message-schema” artifact?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; I’ll help you find the differences between versions of the “log-message-schema” artifact. Let me first search for this artifact in the Apicurio Registry to see what versions exist.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;search_artifacts&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;I found the artifact. Now let me list all versions of this schema to see what versions are available:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_versions&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;I can see there are two versions of the log-message-schema artifact (version 1 and version 2). Let me retrieve the content of both versions to compare them:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;br /&gt;
[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Here are the key differences between the two versions of the “log-message-schema” artifact:&lt;/p&gt;

  &lt;h2 id=&quot;version-1--version-2-changes&quot;&gt;Version 1 → Version 2 Changes&lt;/h2&gt;

  &lt;h3 id=&quot;1-title-updated&quot;&gt;1. Title Updated&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;Version 1: “Log Message Event”&lt;/li&gt;
    &lt;li&gt;Version 2: “Log Message Event with Tracing”&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3 id=&quot;2-new-tracing-field-added&quot;&gt;2. New Tracing Field Added&lt;/h3&gt;

  &lt;p&gt;Version 2 introduces a new tracing object with distributed tracing capabilities:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traceId&lt;/code&gt;: 32-character hexadecimal string for trace identification&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spanId&lt;/code&gt;: 16-character hexadecimal string for span identification&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parentSpanId&lt;/code&gt;: 16-character hexadecimal string for parent span identification&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3 id=&quot;3-schema-structure&quot;&gt;3. Schema Structure&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;Version 1 had 4 properties: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timestamp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;level&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metadata&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Version 2 has 5 properties: adds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing&lt;/code&gt; object while keeping all original fields&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3 id=&quot;4-backward-compatibility&quot;&gt;4. Backward Compatibility&lt;/h3&gt;

  &lt;p&gt;The changes are backward compatible since:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;All required fields remain the same (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timestamp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;level&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt;)&lt;/li&gt;
    &lt;li&gt;The new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing&lt;/code&gt; field is optional&lt;/li&gt;
    &lt;li&gt;Existing properties have identical definitions&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; Version 2 enhances the schema by adding distributed tracing support while maintaining full backward compatibility with version 1. This allows log messages to include trace correlation information for better observability in distributed systems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This would be even more useful with a more complex schema or even multiple schema versions. Interestingly, the AI automatically included backward compatibility analysis and described the changes to be backward compatible. However, Claude is not correct in this case. Because we’ve added a new field &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing&lt;/code&gt; with a specific type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;additionalProperties&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; by default, strictly speaking, the schemas are not backward compatible. This is because the previous schema would accept a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing&lt;/code&gt; field with, for example, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; type. It’s important to keep in mind that LLMs are not infallible. Apicurio Registry supports compatibility checking and enforcement that would detect this compatibility issue, but LLMs can still be a nice additional tool to validate your new artifact versions. You might be sure that your system does not contain a message that has used a non-object &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing&lt;/code&gt; field in the past, but this is not something that Apicurio Registry can check.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;NOTE: There is a fantastic blog post named &lt;a href=&quot;https://yokota.blog/2021/03/29/understanding-json-schema-compatibility&quot;&gt;Understanding JSON Schema compatibility&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;prompt-driven-interactions&quot;&gt;Prompt-Driven Interactions&lt;/h2&gt;

&lt;p&gt;In addition to &lt;strong&gt;tools&lt;/strong&gt;, the MCP protocol allows MCP servers to define &lt;strong&gt;prompts&lt;/strong&gt;, which are reusable templates and workflows that guide the LLM’s interactions and behavior, similar to a “system” prompt. Prompts are useful to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Provide the LLM with a goal and “rules of engagement” for the conversation, for example:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The assistants goal is to use the MCP server to create a new artifact version in Apicurio Registry.

The assistant must use these general rules during the conversation:

1. The assistant will ask the user if they want to change the content until the user is happy with the result.
   
[...]
   
4. The assistant will always ask the user for confirmation when:   
  - Updating configuration properties
  - Creating group ID or artifact ID
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Instruct Claude on the steps it should take:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The assistant will follow these steps during the conversation:

1. The user might want to update an existing artifact or create a new artifact.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Provide additional information to the LLM about the options it can use:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2. The assistant will check if the Apicurio Registry is configured to allow draft artifacts. This feature is configured with a configuration property named &quot;apicurio.rest.mutability.artifact-version-content.enabled&quot;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Instruct the LLM about information it will need to find out, or ask the user:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;3. The assistant will determine which artifact types are supported by Apicurio Registry, and tell the options to the user.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apicurio Registry MCP server offers a prompt for creating a new artifact version called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_new_artifact_version&lt;/code&gt; (which the examples came from). Let’s try it out!&lt;/p&gt;

&lt;p&gt;First, we open a new chat and add the prompt to the context, by clicking on the &lt;strong&gt;&lt;em&gt;+&lt;/em&gt;&lt;/strong&gt; icon, then &lt;strong&gt;&lt;em&gt;Add from Apicurio Registry (docker)&lt;/em&gt;&lt;/strong&gt;, and then &lt;strong&gt;&lt;em&gt;create_new_artifact_version&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/prompt-add.png&quot; alt=&quot;Adding the create_new_artifact_version prompt to the chat context.&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; Create a new artifact version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; I’ll help you create a new artifact version in Apicurio Registry. Let me start by checking the current server status and understanding what you’d like to do.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_server_info&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Great! The Apicurio Registry server is running (version 3.0.8-SNAPSHOT). Now I need to understand what you’d like to do:&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Would you like to:&lt;/strong&gt;&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Update an existing artifact&lt;/strong&gt; - I’ll retrieve the current content and use it as a starting point&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Create a new artifact&lt;/strong&gt; - I’ll help you generate new content from scratch&lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;Please let me know which option you prefer, and if you’re updating an existing artifact, please provide the group ID and artifact ID.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As expected, Claude follows instructions from the prompt and asked the user whether they want to create or update an artifact. This reflects the expected way of working with content in Apicurio Registry. Compare with the same question, but without the MCP prompt (removed some parts for brevity):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; Create a new artifact version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; I’ll help you create a new artifact version in the Apicurio Registry. First, let me check what artifacts are available and then guide you through the process.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_groups&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Let me check the server info to make sure we’re connected:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_server_info&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Great! The Apicurio Registry is running. Now let me list the available groups to see what artifacts exist:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_groups&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;It seems there are no existing groups yet. Let me check the default group for artifacts:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_artifacts&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Perfect! I found an existing artifact called “log-message-schema” in the default group. Let me check its current versions:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;list_versions&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Great! The artifact “log-message-schema” already has 2 versions. Let me check the current content of the latest version to understand what we’re working with:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_version_content&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now I can help you create a new version. I’ll create version 3 with some improvements to the schema. Let me create a new version with enhanced features:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Perfect! I’ve successfully created a new artifact version (version 3) for the “log-message-schema” artifact. Here’s what was added in this new version:&lt;/p&gt;

  &lt;h2 id=&quot;new-version-details&quot;&gt;New Version Details:&lt;/h2&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Version:&lt;/strong&gt; 3&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Name:&lt;/strong&gt; Enhanced Log Message Schema&lt;br /&gt;
&lt;em&gt;[…]&lt;/em&gt;&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h2 id=&quot;key-enhancements-in-version-3&quot;&gt;Key Enhancements in Version 3:&lt;/h2&gt;

  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Added FATAL log level&lt;/strong&gt; to the existing DEBUG, INFO, WARN, ERROR levels&lt;br /&gt;
&lt;em&gt;[…]&lt;/em&gt;&lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;The version is now live and ready to use! You can reference it using:&lt;br /&gt;
&lt;em&gt;[…]&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We see that while the LLM did a good job of gathering data about current Apicurio Registry content and how to use available &lt;strong&gt;tools&lt;/strong&gt;, it created a new version of the only artifact it found (there was a single artifact in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; group used in an earlier example) without asking for confirmation or guidance about the changes that should be made. This is why it’s important to either craft your question carefully or take advantage of the pre-made &lt;strong&gt;MCP prompts&lt;/strong&gt; feature.&lt;/p&gt;

&lt;p&gt;Let’s return to our previous conversation:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; Create a new artifact.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Perfect! I’ll help you create a new artifact. Let me first check what artifact types are supported by this Apicurio Registry server.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_artifact_types&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Great! This registry supports several artifact types:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;OPENAPI&lt;/strong&gt; - OpenAPI/Swagger API specifications&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;ASYNCAPI&lt;/strong&gt; - AsyncAPI specifications for event-driven APIs&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;AVRO&lt;/strong&gt; - Apache Avro schemas&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;JSON&lt;/strong&gt; - JSON Schema definitions&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;PROTOBUF&lt;/strong&gt; - Protocol Buffer schemas&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;GRAPHQL&lt;/strong&gt; - GraphQL schemas&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;XML/XSD&lt;/strong&gt; - XML schemas&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;WSDL&lt;/strong&gt; - Web Service definitions&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;KCONNECT&lt;/strong&gt; - Kafka Connect schemas&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;&lt;strong&gt;What kind of content would you like to create?&lt;/strong&gt; Please choose from the options above, or let me know what type of API, schema, or specification you need help with.&lt;/p&gt;

  &lt;p&gt;For example:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;An OpenAPI specification for a REST API&lt;/li&gt;
    &lt;li&gt;An Avro schema for data serialization&lt;/li&gt;
    &lt;li&gt;A JSON schema for data validation&lt;/li&gt;
    &lt;li&gt;An AsyncAPI specification for messaging&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;We see that Claude has slowed down and is asking clarifying questions. Let’s continue with creating a new Avro schema (removed some parts for brevity):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; Create an Avro money transfer schema, that can be used in a Kafka topic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Excellent choice! I’ll create an Avro schema for money transfers that’s perfect for Kafka topics. Let me first check if the registry supports draft artifacts, which would allow us to iterate on the content.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;get_configuration_property&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;The draft artifacts feature is currently disabled. This means once we create an artifact version, we cannot modify its content - we’d need to create new versions for any changes.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Would you like me to enable the draft artifacts feature?&lt;/strong&gt; This would allow us to:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;Create the artifact in DRAFT state&lt;/li&gt;
    &lt;li&gt;Make iterative changes to the content&lt;/li&gt;
    &lt;li&gt;Publish it as ENABLED when you’re satisfied&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;Or would you prefer to work with the current setup where each change creates a new version?&lt;/p&gt;

  &lt;p&gt;Now, let me create a comprehensive Avro schema for money transfers. Here’s what I’ll include:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;Money Transfer Avro SchemaCode&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;I’ve created a comprehensive Avro schema for money transfers! This schema includes:&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;Unique transfer ID and timestamp for tracking&lt;/li&gt;
    &lt;li&gt;Precise decimal amounts using Avro’s decimal logical type&lt;br /&gt;
&lt;em&gt;[…]&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;Flexible metadata for additional information&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;&lt;strong&gt;Kafka-Optimized Design:&lt;/strong&gt;&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;Uses logical types for proper timestamp and decimal handling&lt;br /&gt;
&lt;em&gt;[…]&lt;/em&gt;&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;Now I need some information to create this artifact in the registry:&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;1. Group ID:&lt;/strong&gt; What group should this schema belong to?&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;I could suggest: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;payments&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;financial-services&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafka-schemas&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Or you can specify your own&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;&lt;strong&gt;2. Artifact ID:&lt;/strong&gt; What should we call this artifact?&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;I could suggest: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;money-transfer-schema&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoneyTransfer&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Or you can specify your own&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;&lt;strong&gt;3. Would you like me to make any changes to the schema content before we create it in the registry?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here we see how the prompt modifies the behavior of the LLM to make the experience of creating a new artifact version better:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The LLM is aware of the draft artifact versions feature. This feature allows users to temporarily modify an artifact version until the user is happy and transitions it to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENABLED&lt;/code&gt; state, which makes it immutable. This feature is great for this kind of iterative schema creation. As an alternative, either a new artifact version would have to be created after each modification, or the user can iterate on the schema directly in the chat.&lt;/li&gt;
  &lt;li&gt;The LLM knows about the configuration property it needs to enable, and is required to ask the user for permission before doing so.&lt;/li&gt;
  &lt;li&gt;The prompt also asks Claude to show any generated content in a separate pane (see image below), which enhances the editing experience.&lt;/li&gt;
  &lt;li&gt;The LLM is required to ask the user what group ID and artifact ID identifiers to use, but can provide suggestions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/prompt-edit-panel.png&quot; alt=&quot;Edited content shows up in a side pane.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s provide Claude with the required information:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; Use the draft artifact feature. I want to use group ID “payments” and the artifact ID should be the same as the name of the schema.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Perfect! I’ll enable the draft artifacts feature and use “payments” as the group ID and “MoneyTransfer” as the artifact ID (matching the schema name).&lt;/p&gt;

  &lt;p&gt;Let me first enable the draft artifacts feature:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;update_configuration_property&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Great! Draft artifacts are now enabled. Now I’ll create the “payments” group and then create the MoneyTransfer artifact.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_group&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Excellent! The “payments” group has been created. Now I’ll create the MoneyTransfer artifact:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_artifact&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Perfect! The MoneyTransfer artifact has been created. Now I’ll create the first version with the schema content in DRAFT state:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;create_version&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Excellent! 🎉 The MoneyTransfer artifact has been successfully created in DRAFT state. Here’s what we’ve accomplished:&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;✅ Created:&lt;/strong&gt;&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Group:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;payments&lt;/code&gt; (for financial schema organization)&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Artifact:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoneyTransfer&lt;/code&gt; (AVRO type)&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Version:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.0&lt;/code&gt; (in DRAFT state)&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;&lt;strong&gt;📋 Schema Details:&lt;/strong&gt;&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Group ID:&lt;/strong&gt; payments&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Artifact ID:&lt;/strong&gt; MoneyTransfer&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Version:&lt;/strong&gt; 1.0.0&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;State:&lt;/strong&gt; DRAFT (can be modified)&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Global ID:&lt;/strong&gt; 4&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Content ID:&lt;/strong&gt; 4&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;The schema is now stored in the registry and ready for use with Kafka topics. Since it’s in DRAFT state, we can make changes if needed.&lt;/p&gt;

  &lt;p&gt;Would you like to:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Make any changes&lt;/strong&gt; to the schema content?&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Publish it&lt;/strong&gt; by moving it to ENABLED state?&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Review the current content&lt;/strong&gt; in the registry?&lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;What would you like to do next?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As expected, the schema appears in our Apicurio Registry instance, but we can still update it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/prompt-draft-schema.png&quot; alt=&quot;Metadata of the draft artifact version in Apicurio Registry web console.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s make one more edit before we publish the schema - add support for &lt;a href=&quot;https://en.wikipedia.org/wiki/Single_Euro_Payments_Area&quot;&gt;SEPA&lt;/a&gt; transfers, and clean up some of the metadata (removed some parts for brevity):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; Use the draft artifact feature. I want to use group ID “payments” and the artifact ID should be the same as the name of the schema.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Perfect! I’ll update the schema to support SEPA transfers and clean up the version naming. Let me make these changes:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Add SEPA support&lt;/strong&gt; by including SEPA-specific fields and transfer types&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Remove version numbers&lt;/strong&gt; from the artifact version name and labels&lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;Money Transfer Avro Schema&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Now let me update the version metadata to remove the version number from the name and labels:&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;update_version_metadata&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;Perfect! ✅ I’ve successfully updated the MoneyTransfer schema with the following changes:&lt;/p&gt;

  &lt;h2 id=&quot;-sepa-support-added&quot;&gt;🆕 SEPA Support Added:&lt;/h2&gt;

  &lt;h3 id=&quot;new-transfer-types&quot;&gt;New Transfer Types:&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEPA_CREDIT&lt;/code&gt; - Standard SEPA credit transfers&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEPA_INSTANT&lt;/code&gt; - SEPA instant payments&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3 id=&quot;new-account-fields&quot;&gt;New Account Fields:&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iban&lt;/code&gt; - International Bank Account Number (for SEPA transfers)&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bic&lt;/code&gt; - Bank Identifier Code / SWIFT code (for SEPA transfers)&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3 id=&quot;new-sepa-specific-information&quot;&gt;New SEPA-Specific Information:&lt;/h3&gt;

  &lt;p&gt;&lt;em&gt;[…]&lt;/em&gt;&lt;/p&gt;

  &lt;h3 id=&quot;️-cleaned-up-labels--naming&quot;&gt;🏷️ Cleaned Up Labels &amp;amp; Naming:&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Name:&lt;/strong&gt; Changed from “Money Transfer Schema v1.0.0” to “Money Transfer Schema”&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Labels:&lt;/strong&gt; Removed version numbers, added “sepa-enabled”: “true”&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; Updated to mention SEPA support&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;The schema is still in &lt;strong&gt;DRAFT&lt;/strong&gt; state, so we can make additional changes if needed. The schema now fully supports both traditional transfers (domestic, international, wire) and European SEPA transfers with all the required fields.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Would you like to:&lt;/strong&gt;&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Make any other changes&lt;/strong&gt; to the schema?&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Publish it&lt;/strong&gt; by moving it to ENABLED state?&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Review the final schema&lt;/strong&gt; content?&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;What would you like to do next?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We see that we can iterate this way until the schema is ready, editing both the content and the metadata. Let’s publish the schema!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; Publish.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Perfect! I’ll publish the schema by moving it from DRAFT to ENABLED state.&lt;/p&gt;

  &lt;p&gt;[&lt;strong&gt;&lt;em&gt;update_version_state&lt;/em&gt;&lt;/strong&gt;]&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;🎉 Successfully Published!&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;The MoneyTransfer schema has been moved to ENABLED state and is now ready for production use.&lt;/p&gt;

  &lt;h2 id=&quot;-final-artifact-summary&quot;&gt;📋 Final Artifact Summary:&lt;/h2&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Group ID:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;payments&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Artifact ID:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoneyTransfer&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Version:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.0&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;State:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENABLED&lt;/code&gt; ✅&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AVRO&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Global ID:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h2 id=&quot;-ready-for-kafka-integration&quot;&gt;🚀 Ready for Kafka Integration:&lt;/h2&gt;

  &lt;p&gt;Your schema is now published and can be used with:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Kafka producers&lt;/strong&gt; to serialize money transfer events&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Kafka consumers&lt;/strong&gt; to deserialize money transfer events&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Schema validation&lt;/strong&gt; for incoming transfer data&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Schema evolution&lt;/strong&gt; for future updates&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h2 id=&quot;-key-features-included&quot;&gt;💡 Key Features Included:&lt;/h2&gt;

  &lt;ul&gt;
    &lt;li&gt;✅ Standard money transfers (domestic, international, wire)&lt;/li&gt;
    &lt;li&gt;✅ SEPA transfers (credit &amp;amp; instant)&lt;/li&gt;
    &lt;li&gt;✅ IBAN/BIC support for European banking&lt;/li&gt;
    &lt;li&gt;✅ Precise decimal amounts with proper currency handling&lt;/li&gt;
    &lt;li&gt;✅ Comprehensive account information&lt;/li&gt;
    &lt;li&gt;✅ Transfer status tracking&lt;/li&gt;
    &lt;li&gt;✅ Extensible metadata support&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;The schema is now live in your Apicurio Registry and ready to be consumed by your Kafka applications! 🎯&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2025-07-08-introducing-mcp-server-for-apicurio-registry-3/prompt-final-schema.png&quot; alt=&quot;Metadata of the final artifact version in Apicurio Registry web console.&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-get-started&quot;&gt;How to get started&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;We need a running Apicurio Registry instance. The easiest way is to use Docker or Podman to run Apicurio Registry 3 locally:&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# API server&lt;/span&gt;
docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:8080 quay.io/apicurio/apicurio-registry:latest-snapshot
&lt;span class=&quot;c&quot;&gt;# UI server&lt;/span&gt;
docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8888:8080 quay.io/apicurio/apicurio-registry-ui:latest-snapshot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;blockquote&gt;
      &lt;p&gt;&lt;em&gt;NOTE: You can replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;podman&lt;/code&gt; in all commands throughout this guide.&lt;/em&gt;&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Multiple LLM providers support the MCP protocol, but to follow this blog post and use Claude, register for &lt;a href=&quot;https://claude.ai&quot;&gt;Claude AI&lt;/a&gt; if you don’t have an account yet.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Install the &lt;a href=&quot;https://claude.ai/download&quot;&gt;Claude Desktop&lt;/a&gt; application. If you are using a system that is not officially supported, like Fedora, &lt;a href=&quot;https://github.com/bsneed/claude-desktop-fedora&quot;&gt;unofficial installation options&lt;/a&gt; are available. Alternatively, &lt;a href=&quot;https://code.visualstudio.com/docs/copilot/chat/mcp-servers&quot;&gt;some IDEs such as VS Code&lt;/a&gt; support integration with an MCP server.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Run Claude Desktop, and go to &lt;em&gt;File&lt;/em&gt; &amp;gt; &lt;em&gt;Settings…&lt;/em&gt; &amp;gt; &lt;em&gt;Developer&lt;/em&gt; &amp;gt; &lt;em&gt;Edit Config&lt;/em&gt;, to open the configuration file (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/Claude/claude_desktop_config.json&lt;/code&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Update the configuration file as follows (using Docker or Podman):&lt;/p&gt;

    &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Apicurio Registry&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;docker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;run&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--rm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--network=host&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;quay.io/apicurio/apicurio-registry-mcp-server:latest-snapshot&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;blockquote&gt;
      &lt;p&gt;&lt;em&gt;NOTE: Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;command&quot;: &quot;docker&quot;&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;command&quot;: &quot;podman&quot;&lt;/code&gt; if you prefer to use Podman.&lt;/em&gt;&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;NOTE: The Apicurio Registry MCP Server source code lives in the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/mcp&quot;&gt;Apicurio Registry GitHub repository&lt;/a&gt;, where you can also find the most recent README file with additional information, such as configuration options and how to build the MCP server from source.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As of writing of this article, Apicurio Registry MCP server currently supports a wide range of operations for managing your Apicurio Registry content:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Artifact Types&lt;/strong&gt;: Listing supported types.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Configuration Properties&lt;/strong&gt;: Getting, listing, and updating dynamic configuration properties.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Groups&lt;/strong&gt;: Creating, getting, listing, searching, and updating groups.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Artifacts&lt;/strong&gt;: Creating, getting, listing, searching, and updating artifacts and their metadata.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Versions&lt;/strong&gt;: Creating, getting content, listing, searching, updating metadata, and even updating the state or content of artifact versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;but there are still more capabilities to add, such as managing content rules, adding comments, import &amp;amp; export, and additional configuration options. &lt;strong&gt;We’d love if you decide to take a look at the source code and contribute a PR!&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;implementing-your-own-mcp-server&quot;&gt;Implementing your own MCP server&lt;/h2&gt;

&lt;p&gt;In conclusion, I’d like to provide some thoughts and lessons-learned during the implementation of the MCP server.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Safety first - Since MCP &lt;strong&gt;tools&lt;/strong&gt; can provide executable functionality that can be &lt;strong&gt;&lt;em&gt;destructive&lt;/em&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;em&gt;non-reversible&lt;/em&gt;&lt;/strong&gt; (e.g., data deletion), the author of the MCP server has to be careful when implementing such operations. Claude Desktop shows the user a prompt before executing any &lt;strong&gt;tool&lt;/strong&gt;, but that should only be the first line of defense against the LLM making a mistake. We suggest additional precautions such as:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Carefully worded &lt;strong&gt;tool&lt;/strong&gt; descriptions and &lt;strong&gt;prompts&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Sanity checks by the MCP server (e.g., limit the amount of data that can be deleted)&lt;/li&gt;
      &lt;li&gt;Requiring configuration options to enable potentially unsafe operations&lt;/li&gt;
      &lt;li&gt;Backups&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;Apicurio Registry MCP server does not yet support delete operations, but it can update configuration properties. As a precaution, only the configuration property enabling draft artifact versions can be modified, unless the MCP server is configured otherwise.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Paging is hard - The MCP server communicates with Apicurio Registry using the REST API where some of the operations are paged. We were unable to help Claude understand how to work with paged results, so the MCP server has to request large pages by default.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Quarkus is great! - The server has been implemented using the &lt;a href=&quot;https://docs.quarkiverse.io/quarkus-mcp-server/dev/index.html#&quot;&gt;Quarkus MCP Server extension&lt;/a&gt;, which made it very easy to define tools and prompts using just a couple of annotations. The most time-consuming part was writing descriptions for the LLM to help it understand Apicurio Registry concepts.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Ask the LLM itself to assist you with writing prompts! Plain and direct sentences, lots of examples, and text formatting worked for us.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description><category term="ai" /><pubDate>Tue, 08 Jul 2025 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/07/08/introducing-mcp-server-for-apicurio-registry-3</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/07/08/introducing-mcp-server-for-apicurio-registry-3</guid>
        </item><item>
            <title>Schema-Driven AI Metadata with Apicurio Registry</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;In this blog post, we will explore how to ensure AI model metadata is valid, consistent, and trustworthy across systems through schema validation. We demonstrate how Apicurio Registry can be used to store and validate machine learning model metadata using JSON Schema, providing a strong foundation for governance in AI workflows.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;schema-driven-ai-metadata-validating-model-metadata-with-apicurio-registry&quot;&gt;Schema-Driven AI Metadata: Validating Model metadata with Apicurio Registry&lt;/h1&gt;

&lt;p&gt;In modern AI systems, model metadata plays a crucial role in enabling discovery, governance, and interoperability. This Model metadata demo defines a standard way to describe machine learning models - what inputs they expect, what outputs they produce, and contextual metadata like authorship, framework, or creation date.&lt;/p&gt;

&lt;p&gt;But how do we ensure the metadata is valid and consistent across systems? How do we prevent malformed or incomplete descriptions from entering the pipeline?&lt;/p&gt;

&lt;p&gt;This is where schema validation comes into play. In this blog post, we’ll show how to use Apicurio Registry to store and validate model metadata using JSON Schema.&lt;/p&gt;

&lt;h2 id=&quot;why-schema-validation-for-model-metadata&quot;&gt;Why Schema Validation for model metadata?&lt;/h2&gt;

&lt;p&gt;In general, model metadata, is flexible and extensible, however, without validation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Teams might omit required fields (like modelId or inputSchema).&lt;/li&gt;
  &lt;li&gt;Different components may interpret metadata inconsistently.&lt;/li&gt;
  &lt;li&gt;Automation pipelines may break due to incompatible metadata changes.&lt;/li&gt;
  &lt;li&gt;Schema validation provides a contract—a shared understanding enforced at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-well-build&quot;&gt;What We’ll Build&lt;/h2&gt;

&lt;p&gt;✅ A JSON Schema that describes valid model metadata.&lt;/p&gt;

&lt;p&gt;🏗 A Java/Quarkus app to validate JSON documents against the schema using Apicurio Registry.&lt;/p&gt;

&lt;p&gt;📁 Example valid and invalid model metadata documents.&lt;/p&gt;

&lt;p&gt;🌐 An Model metadata REST API server that exposes endpoints to submit, validate, and retrieve model metadata.&lt;/p&gt;

&lt;h2 id=&quot;defining-the-model-metadata-schema&quot;&gt;Defining the model metadata schema&lt;/h2&gt;

&lt;p&gt;Here’s a simplified version of the model-context-schema.json:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;$schema&quot;: &quot;https://json-schema.org/draft/2020-12/schema&quot;,
  &quot;title&quot;: &quot;ModelContext&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;required&quot;: [&quot;modelId&quot;, &quot;version&quot;, &quot;inputSchema&quot;, &quot;outputSchema&quot;, &quot;metadata&quot;],
  &quot;properties&quot;: {
    &quot;modelId&quot;: { &quot;type&quot;: &quot;string&quot; },
    &quot;version&quot;: { &quot;type&quot;: &quot;string&quot; },
    &quot;inputSchema&quot;: { &quot;type&quot;: &quot;object&quot; },
    &quot;outputSchema&quot;: { &quot;type&quot;: &quot;object&quot; },
    &quot;metadata&quot;: {
      &quot;type&quot;: &quot;object&quot;,
      &quot;properties&quot;: {
        &quot;author&quot;: { &quot;type&quot;: &quot;string&quot; },
        &quot;created&quot;: { &quot;type&quot;: &quot;string&quot;, &quot;format&quot;: &quot;date-time&quot; },
        &quot;framework&quot;: { &quot;type&quot;: &quot;string&quot; },
        &quot;description&quot;: { &quot;type&quot;: &quot;string&quot; }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;registering-the-schema-in-apicurio&quot;&gt;Registering the Schema in Apicurio&lt;/h2&gt;

&lt;p&gt;Start Apicurio Registry locally or use a hosted instance.&lt;/p&gt;

&lt;p&gt;You can start the application using:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run -it -p 8080:8080 apicurio/apicurio-registry:latest-release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then register the model metadata schema:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST http://localhost:8080/apis/registry/v3/groups/mcp-models/artifacts -H &quot;Content-Type: application/json&quot; -d @./model-context-schema.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also enable schema compatibility checks (e.g., BACKWARD):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST http://localhost:8080/apis/registry/v3/groups/mcp-models/artifacts/model-context-schema/rules -H &quot;Content-Type: application/json&quot; -d @./rule.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;validating-metadata-with-java-quarkus&quot;&gt;Validating Metadata with Java (Quarkus)&lt;/h2&gt;

&lt;p&gt;To make this even more practical, we extended the demo with a Quarkus-based Model Metadata Server. This server exposes REST endpoints to submit and retrieve model metadata, while validating each submission using Apicurio.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;REST Endpoints:

POST /models — Accepts and validates MCP metadata

GET /models — Lists all registered models

GET /models/{id} — Retrieves model metadata by ID
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This server could act as a model metadata layer in front of other services—such as the Quarkus Superheroes demo app—where each superhero could have an associated prediction or simulation model validated by the Model Metadata layer.&lt;/p&gt;

&lt;p&gt;This shows how schema governance isn’t just a backend concern—it can be integrated into microservice architectures to promote validation, documentation, and trust.&lt;/p&gt;

&lt;h2 id=&quot;validating-metadata-submissions&quot;&gt;Validating Metadata Submissions&lt;/h2&gt;

&lt;p&gt;When a user submits a model metadata document, the server validates it against the registered schema. If the document is valid, it’s stored in the model metadata server. If not, an error response is returned.&lt;/p&gt;

&lt;p&gt;To start the server, run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mvn quarkus:dev -Dquarkus.http.port=8081
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, you can submit a valid model metadata document:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST http://localhost:8081/models -H &quot;Content-Type: application/json&quot; -d @sample-model-contexts/valid-context.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And you’ll see an output like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&quot;modelId&quot;:&quot;customer-churn-predictor&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Indicating a successful model submission.&lt;/p&gt;

&lt;p&gt;If you try to submit an invalid document:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST http://localhost:8081/models -H &quot;Content-Type: application/json&quot; -d @sample-model-contexts/invalid-context.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll get a validation error response:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&quot;details&quot;:[{&quot;description&quot;:&quot;$.artifactUri: integer found, string expected&quot;,&quot;context&quot;:&quot;1029&quot;},{&quot;description&quot;:&quot;$.metrics.accuracy: string found, number expected&quot;,&quot;context&quot;:&quot;1029&quot;},{&quot;description&quot;:&quot;$: required property &apos;version&apos; not found&quot;,&quot;context&quot;:&quot;1028&quot;}],&quot;error&quot;:&quot;Model validation failed&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;real-world-applications&quot;&gt;Real-World Applications&lt;/h1&gt;

&lt;p&gt;This pattern can be used for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;🔁 Validating model metadata in CI/CD before pushing to model registry.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;📜 Enforcing internal standards for all model submissions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;🤖 Automated documentation and discovery pipelines.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;✅ Ensuring input/output schemas remain compatible over time.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try It Yourself&lt;/p&gt;

&lt;p&gt;We’ve open-sourced the demo on GitHub: https://github.com/carlesarnal/model-metadata&lt;/p&gt;

&lt;p&gt;To run it locally:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Start Apicurio
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -it --rm -p 8080:8080 apicurio/apicurio-registry:latest&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Register the schema
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl -X POST http://localhost:8080/apis/registry/v3/groups/mcp-models/artifacts -H &quot;Content-Type: application/json&quot; -d @./model-context-schema.json&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Start the model metadata server (Quarkus)
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn quarkus:dev&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Submit a model document
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl -X POST http://localhost:8081/models -H &quot;Content-Type: application/json&quot; -d @sample-model-contexts/valid-context.json&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h1&gt;

&lt;p&gt;Schemas aren’t just for APIs anymore. In the world of AI and MLOps, they’re critical for ensuring that metadata—just like data—is consistent, complete, and trustworthy.&lt;/p&gt;

&lt;p&gt;Using Apicurio Registry to manage and validate model metadata gives your AI workflows a strong backbone of governance, validation, and extensibility.&lt;/p&gt;

&lt;p&gt;Try the model metadata server integration with your own microservices—or the Quarkus Superheroes demo—and see schema validation in action.&lt;/p&gt;

&lt;p&gt;Feel free to reach out or contribute to the demo repo. Happy validating! 🚀&lt;/p&gt;
</description><category term="ai" /><category term="use-case" /><pubDate>Fri, 02 May 2025 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/05/02/schema-drive-ai-metadata</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/05/02/schema-drive-ai-metadata</guid>
        </item><item>
            <title>Apicurio Registry 3.x - Evolving Our Serialization and Deserialization Modules</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;Serialization and deserialization (SerDes) are critical functionalities within Apicurio Registry, enabling users to seamlessly manage schema evolution and compatibility. With the release of Apicurio Registry 3.x, we’ve significantly evolved our SerDes modules, focusing on flexibility, reusability, and broader integration.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;whats-changed&quot;&gt;What’s Changed?&lt;/h2&gt;

&lt;h3 id=&quot;1-reusable-generic-codebase&quot;&gt;1. Reusable, Generic Codebase&lt;/h3&gt;
&lt;p&gt;Previously, in Apicurio Registry 2.6.x, our serializers and deserializers were independently organized by format—Avro, JSON Schema, and Protobuf—each housed within its separate module. This resulted in duplicated efforts and increased complexity when adding support for new messaging platforms.&lt;/p&gt;

&lt;p&gt;In Apicurio Registry 3.x, we’ve adopted a modular design to improve reuse:&lt;/p&gt;

&lt;p&gt;A new generic module now hosts all common, format-agnostic serialization logic.&lt;/p&gt;

&lt;p&gt;Specific messaging system implementations (Kafka, Pulsar, NATS) reuse this generic codebase.&lt;/p&gt;

&lt;p&gt;This approach significantly reduces duplication, simplifies maintenance, and streamlines contributions from the community.&lt;/p&gt;

&lt;h3 id=&quot;2-multi-platform-support&quot;&gt;2. Multi-Platform Support&lt;/h3&gt;
&lt;p&gt;With Apicurio Registry 2.6.x, our SerDes primarily focused on Kafka support. However, users increasingly require schema management in diverse messaging environments.&lt;/p&gt;

&lt;p&gt;With version 3.x, we’ve expanded our support beyond Kafka:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Apache Pulsar: Dedicated SerDes leveraging generic functionality to seamlessly integrate Apicurio Registry with Pulsar.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;NATS: Specific Avro support tailored for integration with NATS messaging systems.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This addition enables users to utilize Apicurio Registry in a broader range of architectures.&lt;/p&gt;

&lt;h3 id=&quot;3-improved-code-maintainability-and-extensibility&quot;&gt;3. Improved Code Maintainability and Extensibility&lt;/h3&gt;
&lt;p&gt;By restructuring our SerDes modules, we’ve created a solid foundation for future enhancements. Developers can now:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Easily implement SerDes for additional messaging platforms.&lt;/li&gt;
  &lt;li&gt;Share common serialization/deserialization logic without duplication.&lt;/li&gt;
  &lt;li&gt;Enjoy streamlined maintenance due to a centralized and modular architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;directory-structure-comparison&quot;&gt;Directory Structure Comparison&lt;/h2&gt;
&lt;p&gt;Here’s a concise before-and-after illustration of our SerDes directory structures:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Apicurio Registry 2.6.x

serdes/
├── avro-serde
├── jsonschema-serde
├── protobuf-serde
└── serde-common

Apicurio Registry 3.x
serdes/
├── generic
│   └── serde-common-avro
│   └── serde-common-protobuf
│   └── serde-common-jsonschema
├── kafka
│   └── avro-serde
│   └── protobuf-serde
│   └── jsonschema-serde
├── pulsar
│   └── avro-serde
│   └── protobuf-serde
│   └── jsonschema-serde
└── nats
│   └── avro-serde
│   └── protobuf-serde
│   └── jsonschema-serde
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The refined structure clearly separates common logic (generic) from system-specific code (kafka, pulsar, nats), emphasizing the new modular and reusable approach.&lt;/p&gt;

&lt;h2 id=&quot;configuration-simplification-and-improvements&quot;&gt;Configuration Simplification and Improvements&lt;/h2&gt;
&lt;p&gt;With Apicurio Registry 3.x, the configuration of serializers and deserializers has also been streamlined, reflecting the modular nature of the updated architecture. Here’s what’s changed:&lt;/p&gt;

&lt;h3 id=&quot;1-unified-configuration-properties&quot;&gt;1. Unified Configuration Properties&lt;/h3&gt;
&lt;p&gt;In Apicurio Registry 2.6.x, each SerDes module required individual configuration properties, leading to redundancy and complexity. Users needed to repeat similar settings across multiple format-specific serializers and deserializers.&lt;/p&gt;

&lt;p&gt;In Apicurio Registry 3.x, configuration properties have been unified and centralized, greatly simplifying setup. For instance:&lt;/p&gt;

&lt;p&gt;Common properties (registry URL, schema retrieval strategy, caching, compatibility checks) are now standardized and shared across different formats and platforms.&lt;/p&gt;

&lt;p&gt;Messaging platform-specific properties (e.g., Kafka, Pulsar, NATS) are clearly isolated, making it easy to manage configurations tailored to each platform.&lt;/p&gt;

&lt;h3 id=&quot;2-improved-clarity-and-documentation&quot;&gt;2. Improved Clarity and Documentation&lt;/h3&gt;
&lt;p&gt;With unified and modular configurations, it’s easier to understand and configure the SerDes for your specific needs. Users benefit from:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Clearer property names and structured organization.&lt;/li&gt;
  &lt;li&gt;Improved documentation that simplifies the onboarding and troubleshooting process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-easier-extensibility&quot;&gt;3. Easier Extensibility&lt;/h3&gt;
&lt;p&gt;The new configuration model provides a clearer path for adding support for future messaging systems or serialization formats:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Developers extending SerDes functionality benefit from standardized conventions.&lt;/li&gt;
  &lt;li&gt;Less boilerplate configuration means faster development and lower risk of misconfiguration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The reorganization of our SerDes modules marks a significant step forward for Apicurio Registry, enabling broader messaging platform support, better maintainability, and increased flexibility. We look forward to seeing how this improved architecture benefits your projects and workflows.&lt;/p&gt;

&lt;p&gt;We encourage the community to explore these changes, provide feedback, and contribute. Together, let’s continue evolving Apicurio Registry to meet our shared vision of efficient, robust, and versatile schema management.&lt;/p&gt;
</description><category term="registry" /><category term="guide" /><pubDate>Thu, 03 Apr 2025 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/04/03/evolving-serialization</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/04/03/evolving-serialization</guid>
        </item><item>
            <title>Apicurio Registry 3.0 Migration Guide (Configuration)</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;With the release of Apicurio Registry 3.0.0.Final, we’ve significantly redesigned the configuration model to improve consistency and maintainability. If you’re upgrading from Registry 2.x, you’ll need to update your application properties to match the new format.&lt;/p&gt;

&lt;p&gt;This guide provides a comprehensive walkthrough of all the changes and offers clear examples to ensure a smooth migration process.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;key-migration-concepts&quot;&gt;Key Migration Concepts&lt;/h2&gt;

&lt;p&gt;Before diving into the specifics, here are the fundamental changes you should be aware of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;New namespace&lt;/strong&gt;: Most configuration keys now use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.&lt;/code&gt; prefix instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consistent naming&lt;/strong&gt;: Property names follow a more structured and logical pattern&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Database configuration&lt;/strong&gt;: Datasource properties have moved from the Quarkus namespace to Apicurio’s namespace&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Better organization&lt;/strong&gt;: Related properties are now grouped under common prefixes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1-naming-convention-changes&quot;&gt;1. Naming Convention Changes&lt;/h2&gt;

&lt;p&gt;In Apicurio Registry 3.x, configuration properties now follow a standardized namespace structure under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;example-configuration-changes&quot;&gt;Example Configuration Changes&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.enabled&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.oidc.enabled&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.roles.admin&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.auth.roles.admin&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.events.kafka.topic&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.events.kafka.topic&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.rest.artifact.deletion.enabled&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.rest.deletion.artifact.enabled&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;migration-steps&quot;&gt;Migration Steps&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Review all configuration files (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.properties&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.yaml&lt;/code&gt;, deployment files)&lt;/li&gt;
  &lt;li&gt;Replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.&lt;/code&gt; prefix with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.&lt;/code&gt; for most properties&lt;/li&gt;
  &lt;li&gt;Note any property name changes beyond just the prefix (like the artifact deletion example above)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;2-authentication--authorization-configuration&quot;&gt;2. Authentication &amp;amp; Authorization Configuration&lt;/h2&gt;

&lt;p&gt;Authentication settings in Registry 3.x maintain the same functionality but follow the new naming convention.&lt;/p&gt;

&lt;h3 id=&quot;key-authentication-changes&quot;&gt;Key Authentication Changes&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.enabled&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.oidc.enabled&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.role-source&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.auth.role-source&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.roles.admin&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.auth.roles.admin&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.roles.developer&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.auth.roles.developer&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.roles.readonly&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.auth.roles.readonly&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.anonymous-read-access.enabled&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.auth.anonymous-read-access.enabled&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;oidc-configuration-updates&quot;&gt;OIDC Configuration Updates&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.oidc.client-id&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.oidc.client-id&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.oidc.client-secret&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;em&gt;Removed&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.oidc.token-endpoint&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.oidc.token-path&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.auth.oidc.auth-server-url&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.oidc.auth-server-url&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;example-updating-oidc-authentication-properties&quot;&gt;Example: Updating OIDC Authentication Properties&lt;/h3&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Old (Registry 2.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;registry.auth.enabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.auth.role-source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;token&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.auth.roles.admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;admin&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.auth.oidc.client-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-client&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.auth.oidc.client-secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;secret&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# New (Registry 3.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;quarkus.oidc.enabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.auth.role-source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;token&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.auth.roles.admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;admin&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;quarkus.oidc.client-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-client&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;quarkus.oidc.client-secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-kafka--eventing-configuration&quot;&gt;3. Kafka &amp;amp; Eventing Configuration&lt;/h2&gt;

&lt;p&gt;If you’re using Kafka-based storage or event notification in your Registry installation, update the following configurations:&lt;/p&gt;

&lt;h3 id=&quot;event-notification-settings&quot;&gt;Event Notification Settings&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.events.kafka.topic&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.events.kafka.topic&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.events.kafka.topic-partition&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;em&gt;Removed&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.events.ksink&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;em&gt;Removed&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;kafkasql-storage-settings&quot;&gt;KafkaSQL Storage Settings&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.kafkasql.bootstrap.servers&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.kafkasql.bootstrap.servers&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.kafkasql.topic&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.kafkasql.topic&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.kafkasql.topic.auto-create&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.kafkasql.topic.auto-create&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;example-updating-kafka-event-configuration&quot;&gt;Example: Updating Kafka Event Configuration&lt;/h3&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Old (Registry 2.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;registry.events.kafka.topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-registry-events&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.events.kafka.topic-partition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;5&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# New (Registry 3.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;apicurio.events.kafka.topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-registry-events&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-updating-kafkasql-storage-configuration&quot;&gt;Example: Updating KafkaSQL Storage Configuration&lt;/h3&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Old (Registry 2.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;registry.kafkasql.bootstrap.servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;kafka:9092&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.kafkasql.topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-registry-storage&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.kafkasql.topic.auto-create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# New (Registry 3.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;apicurio.kafkasql.bootstrap.servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;kafka:9092&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.kafkasql.topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-registry-storage&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.kafkasql.topic.auto-create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;kafka-security-configuration&quot;&gt;Kafka Security Configuration&lt;/h3&gt;

&lt;p&gt;If you’re using SASL/SSL for Kafka security:&lt;/p&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Old (Registry 2.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;registry.kafkasql.security.protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SASL_SSL&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.kafkasql.security.sasl.mechanism&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;PLAIN&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.kafkasql.security.sasl.client-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-client&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.kafkasql.security.sasl.client-secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-secret&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.kafkasql.security.sasl.token.endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://localhost:8090&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# New (Registry 3.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;apicurio.kafkasql.security.protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SASL_SSL&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.kafkasql.security.sasl.mechanism&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;PLAIN&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.kafkasql.security.sasl.client-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-client&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.kafkasql.security.sasl.client-secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-secret&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.kafkasql.security.sasl.token.endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://localhost:8090&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;4-ui--api-configuration&quot;&gt;4. UI &amp;amp; API Configuration&lt;/h2&gt;

&lt;p&gt;The UI and API settings have been reorganized in Registry 3.x for better clarity.&lt;/p&gt;

&lt;h3 id=&quot;ui-configuration-updates&quot;&gt;UI Configuration Updates&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.ui.contextPath&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.ui.contextPath&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.ui.features.readOnly&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.ui.features.read-only.enabled&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.ui.apisUrl&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.ui.config.apiUrl&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;ui-authentication-settings&quot;&gt;UI Authentication Settings&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.ui.auth.type&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;em&gt;Removed, OIDC is always used&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.ui.auth.oidc.client-id&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.ui.auth.oidc.client-id&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.ui.auth.oidc.redirect-url&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.ui.auth.oidc.redirect-uri&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.ui.auth.oidc.scope&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.ui.auth.oidc.scope&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.ui.auth.oidc.url&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.ui.auth.oidc.url&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;api-configuration-changes&quot;&gt;API Configuration Changes&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.api.errors.include-stack-in-response&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.api.errors.include-stack-in-response&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;example-updating-ui-configuration&quot;&gt;Example: Updating UI Configuration&lt;/h3&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Old (Registry 2.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;registry.ui.contextPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/my-ui&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.ui.features.readOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# New (Registry 3.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;apicurio.ui.contextPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/my-ui&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.ui.features.read-only.enabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-updating-ui-authentication&quot;&gt;Example: Updating UI Authentication&lt;/h3&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Old (Registry 2.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;registry.ui.auth.oidc.client-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-client&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.ui.auth.oidc.redirect-url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://example.com/callback&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.ui.auth.oidc.scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;openid profile&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.ui.auth.oidc.url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://auth.example.com&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.ui.auth.type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;oidc&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# New (Registry 3.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;apicurio.ui.auth.oidc.client-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;my-client&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.ui.auth.oidc.redirect-uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://example.com/callback&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.ui.auth.oidc.scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;openid profile&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.ui.auth.oidc.url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://auth.example.com&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.ui.auth.type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;oidc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-storage--database-configuration&quot;&gt;5. Storage &amp;amp; Database Configuration&lt;/h2&gt;

&lt;p&gt;One of the most significant changes in Registry 3.x is the migration of database configuration from the Quarkus namespace to the Apicurio namespace.&lt;/p&gt;

&lt;h3 id=&quot;sql-database-configuration&quot;&gt;SQL Database Configuration&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.datasource.jdbc.url&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.datasource.url&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.datasource.username&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.datasource.username&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.datasource.password&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.datasource.password&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.sql.init&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.sql.init&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;storage-type-configuration&quot;&gt;Storage Type Configuration&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.storage.kind&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.storage.kind&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;gitops-storage-configuration&quot;&gt;GitOps Storage Configuration&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Registry 2.x&lt;/th&gt;
      &lt;th&gt;Registry 3.x&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.gitops.repo.origin.uri&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.gitops.repo.origin.uri&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry.gitops.workdir&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.gitops.workdir&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;example-updating-sql-database-configuration&quot;&gt;Example: Updating SQL Database Configuration&lt;/h3&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Old (Registry 2.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;quarkus.datasource.jdbc.url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jdbc:postgresql://db/registry&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;quarkus.datasource.username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;quarkus.datasource.password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.sql.init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# New (Registry 3.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;apicurio.datasource.url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jdbc:postgresql://db/registry&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.datasource.username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.datasource.password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registry&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.sql.init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-updating-gitops-storage-configuration&quot;&gt;Example: Updating GitOps Storage Configuration&lt;/h3&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Old (Registry 2.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;registry.gitops.repo.origin.uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://github.com/my-org/registry-data.git&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;registry.gitops.workdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/tmp/registry-gitops&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# New (Registry 3.x)
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;apicurio.gitops.repo.origin.uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://github.com/my-org/registry-data.git&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;apicurio.gitops.workdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/tmp/registry-gitops&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;migration-checklist&quot;&gt;Migration Checklist&lt;/h2&gt;

&lt;p&gt;Follow these steps to ensure a successful migration:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Inventory&lt;/strong&gt;: Create a list of all your current Registry 2.x configuration properties&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Map&lt;/strong&gt;: Match each property to its Registry 3.x equivalent using this guide&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Update&lt;/strong&gt;: Modify all configuration files with the new property names&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Validate&lt;/strong&gt;: Check for any remaining or custom properties that might need special handling&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Test&lt;/strong&gt;: Start Registry 3.x with updated properties in a non-production environment first&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Monitor&lt;/strong&gt;: Check logs for any warnings about deprecated or invalid properties&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Verify&lt;/strong&gt;: Ensure all artifacts and configurations load correctly&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;common-migration-issues-and-solutions&quot;&gt;Common Migration Issues and Solutions&lt;/h2&gt;

&lt;h3 id=&quot;issue-missing-properties-after-migration&quot;&gt;Issue: Missing Properties After Migration&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Some properties have been renamed beyond just changing the prefix. Review the tables in this guide carefully for exact naming changes. It’s also important to check the latest version documentation.&lt;/p&gt;

&lt;h3 id=&quot;issue-authentication-failures-after-migration&quot;&gt;Issue: Authentication Failures After Migration&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Double-check all auth-related properties, especially:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The role mappings under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.auth.roles.*&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;OIDC configuration parameters, particularly the redirect URI&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;issue-database-connection-problems&quot;&gt;Issue: Database Connection Problems&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Ensure all database properties have been migrated from the Quarkus namespace to the Apicurio namespace. Check database logs for specific connection errors.&lt;/p&gt;

&lt;h3 id=&quot;issue-kafka-configuration-issues&quot;&gt;Issue: Kafka Configuration Issues&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Verify that all Kafka-related properties have been updated, especially security settings if you’re using authentication.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Migrating to Apicurio Registry 3.x involves a significant configuration update, but the new structure provides better organization and consistency. By following this guide, you should be able to migrate your Registry instance with minimal downtime.&lt;/p&gt;

&lt;p&gt;If you encounter any issues not covered here, please check the &lt;a href=&quot;https://www.apicur.io/registry/docs/apicurio-registry/3.0.x/index.html&quot;&gt;official documentation&lt;/a&gt; or reach out to the Apicurio community on &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy migrating!&lt;/p&gt;
</description><category term="registry" /><category term="guide" /><pubDate>Wed, 02 Apr 2025 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/04/02/application-configuration-migration</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/04/02/application-configuration-migration</guid>
        </item><item>
            <title>Apicurio Registry 3.0 Migration Guide</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;Apicurio Registry 3.x is our latest and greatest release to date. It has plenty of new features but it also comes with some breaking changes from the previous 2.6.x release. In this post we will explore the process to migrate data from Apicurio Registry 2.6.x to Apicurio Registry 3.x.&lt;/p&gt;

&lt;p&gt;Because of the breaking changes between 2.6.x and 3.x, there is no automatic upgrade and instead a migration process is required.  But don’t worry!  We promise the process is not hard.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;breaking-changes&quot;&gt;Breaking changes&lt;/h1&gt;

&lt;p&gt;As outlined in the Apicurio Registry 3 announcement, there are three major changes in this release:&lt;/p&gt;

&lt;h2 id=&quot;data-model-changes&quot;&gt;Data model changes&lt;/h2&gt;
&lt;p&gt;There are some significant core data model changes in 3.0.  Some of these changes
include (but are not limited to):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Artifacts have their own metaData&lt;/li&gt;
  &lt;li&gt;Artifacts can be empty (have no versions)&lt;/li&gt;
  &lt;li&gt;Groups can now have labels&lt;/li&gt;
  &lt;li&gt;User can define and maintain custom branches&lt;/li&gt;
  &lt;li&gt;“Latest” is now a branch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;rest-api-changes&quot;&gt;REST API changes&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Full group management, with metaData&lt;/li&gt;
  &lt;li&gt;Streamlined artifact and version creation&lt;/li&gt;
  &lt;li&gt;Search for groups at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/groups&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Search for versions at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/versions&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;A new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dryRun&lt;/code&gt; query param replaces the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/test&lt;/code&gt; endpoint(s)&lt;/li&gt;
  &lt;li&gt;Brand new Branch API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;kafka-storage-variant&quot;&gt;Kafka storage variant&lt;/h2&gt;
&lt;p&gt;We have completely rewritten the KafkaSQL layer to improve its
stability and to make it easier for us to maintain.  This will be the subject of
a separate blog post soon, but know that the new implementation should be much
easier to keep updated and should result in far fewer unexpected bugs.  Spoiler:
we are also introducing a “snapshotting” feature that will help address the slow
startup times that can result from larger deployments (deployments with lots of
artifacts).&lt;/p&gt;

&lt;h1 id=&quot;migrating-from-apicurio-registry-2x-to-3x&quot;&gt;&lt;strong&gt;Migrating from Apicurio Registry 2.x to 3.x&lt;/strong&gt;&lt;/h1&gt;

&lt;p&gt;Apart from the major breaking changes mentioned, Apicurio Registry 3.x introduces several improvements over 2.x, including performance optimizations and better maintainability. If you are currently using &lt;strong&gt;Apicurio Registry 2.x&lt;/strong&gt;, you need to migrate your existing artifacts, rules, and metadata to the new version.&lt;/p&gt;

&lt;p&gt;This guide will walk you through the &lt;strong&gt;migration process&lt;/strong&gt; step by step.&lt;/p&gt;

&lt;hr /&gt;
&lt;h2 id=&quot;step-1-export-data-from-apicurio-registry-2x&quot;&gt;&lt;strong&gt;Step 1: Export Data from Apicurio Registry 2.x&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Before migrating, you need to &lt;strong&gt;export&lt;/strong&gt; your existing data from &lt;strong&gt;Apicurio Registry 2.x&lt;/strong&gt;. The export process generates a &lt;strong&gt;ZIP file&lt;/strong&gt; that contains all artifacts, rules, and metadata.&lt;/p&gt;

&lt;p&gt;To export the data, use the &lt;strong&gt;admin export API&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; GET http://&amp;lt;registry-v2-url&amp;gt;/apis/registry/v2/admin/export &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; registry-export.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-2-import-the-exported-data-into-apicurio-registry-3x&quot;&gt;&lt;strong&gt;Step 2: Import the Exported Data into Apicurio Registry 3.x&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Once your Registry 3.x instance is running, you can import the exported ZIP file.&lt;/p&gt;

&lt;p&gt;Use the following command to send the data to the import API:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST http://&amp;lt;registry-v3-url&amp;gt;/apis/registry/v3/admin/import &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/zip&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--data-binary&lt;/span&gt; @registry-export.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;what-happens-during-import&quot;&gt;What Happens During Import?&lt;/h3&gt;
&lt;p&gt;All artifacts, rules, and metadata from Registry 2.x are restored.
Global are retained.
Artifact references are maintained.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Note: This process does not automatically clean up the old Registry 2.x instance. If needed, remove the old instance manually after validating the migration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-3-validate-the-migration&quot;&gt;&lt;strong&gt;Step 3: Validate the Migration&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Once the import is complete, it is essential to verify that all data has been migrated successfully.&lt;/p&gt;

&lt;p&gt;List the available artifacts in the default group in Registry 3.x:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; GET http://&amp;lt;registry-v3-url&amp;gt;/apis/registry/v3/groups/default/artifacts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ensure that any previously defined global rules (e.g., compatibility settings) are still in place:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; GET http://&amp;lt;registry-v3-url&amp;gt;/apis/registry/v3/admin/rules
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-4-update-client-applications&quot;&gt;&lt;strong&gt;Step 4: Update Client Applications&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Once you have Apicurio Registry 3.x running, you should update your client applications to ensure compatibility with the new version.&lt;/p&gt;

&lt;p&gt;If you’re using Apicurio Registry Client Libraries, update them to versions compatible with Registry 3.x.&lt;/p&gt;

&lt;p&gt;For example, update your Maven dependency:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.apicurio&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;apicurio-registry-java-sdk&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.0.6&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Replace with the latest version --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Migrating from Apicurio Registry 2.x to 3.x is a straightforward process when following these steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Export data from Registry 2.x.&lt;/li&gt;
  &lt;li&gt;Start a new Registry 3.x instance.&lt;/li&gt;
  &lt;li&gt;Import the exported data into Registry 3.x.&lt;/li&gt;
  &lt;li&gt;Validate the migration to ensure all artifacts and rules were successfully transferred.&lt;/li&gt;
  &lt;li&gt;Update client applications to work with Registry 3.x.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By completing these steps, you’ll benefit from the improved performance and features of Apicurio Registry 3.x.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;For more details, check the official Apicurio documentation or reach out to the Apicurio community.&lt;/p&gt;
</description><category term="registry" /><category term="guide" /><pubDate>Sun, 30 Mar 2025 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/03/30/migrate-registry-2-to-3</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/03/30/migrate-registry-2-to-3</guid>
        </item><item>
            <title>Apicurio Registry 3.0: Generated API SDKs (by Kiota)</title><dc:creator>Eric Wittmann &amp; Jakub Senko</dc:creator>
            <author></author><description>&lt;p&gt;In the world of API management, developer experience is paramount. Apicurio Registry 3.0 has
taken a significant leap forward by implementing Kiota for automated SDK generation, making it
easier than ever for developers to interact with the Registry’s Core REST API across multiple
programming languages.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-is-kiota&quot;&gt;What is Kiota?&lt;/h2&gt;

&lt;p&gt;Kiota is a groundbreaking SDK generation tool developed by Microsoft that simplifies the process
of creating type-safe, lightweight SDKs for REST APIs. Unlike traditional code generation methods,
Kiota focuses on creating lean, efficient client libraries that streamline API interactions.&lt;/p&gt;

&lt;h2 id=&quot;supported-languages-in-apicurio-registry-30&quot;&gt;Supported Languages in Apicurio Registry 3.0&lt;/h2&gt;

&lt;p&gt;With the introduction of Kiota, Apicurio Registry now provides automatic SDK generation for four
major programming languages:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Java&lt;/strong&gt;: Enabling robust, type-safe API interactions for enterprise and Android applications&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;: Providing seamless integration for web and Node.js projects&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;: Supporting data science, automation, and general-purpose application development&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Golang&lt;/strong&gt;: Offering high-performance SDK generation for cloud-native and systems programming&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;intuitive-api-structure-mirroring-the-rest-api-specification&quot;&gt;Intuitive API Structure: Mirroring the REST API Specification&lt;/h2&gt;

&lt;p&gt;One of the most powerful features of Kiota-generated SDKs is their intuitive, hierarchical structure
that directly reflects the OpenAPI specification of the Apicurio Registry REST API. This design means
that navigating and using the SDK becomes incredibly straightforward and predictable across all
supported languages.&lt;/p&gt;

&lt;h3 id=&quot;endpoint-navigation-made-simple&quot;&gt;&lt;strong&gt;Endpoint Navigation Made Simple&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The SDK’s structure allows developers to navigate to specific endpoints using a chained method
approach that closely mirrors the REST API’s path structure. This approach provides an incredibly
natural and consistent way of interacting with the API, regardless of the programming language.&lt;/p&gt;

&lt;p&gt;Let’s look at how you might invoke different endpoints across languages:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Searching Artifacts&lt;/strong&gt; - Invoke &lt;strong&gt;GET&lt;/strong&gt; on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/artifacts&lt;/code&gt; endpoint
    &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Java&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ArtifactMetaData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;artifacts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// TypeScript&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;artifacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// Go&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Artifacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Creating an Artifact&lt;/strong&gt; - Invoke &lt;strong&gt;POST&lt;/strong&gt; on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/artifacts&lt;/code&gt; endpoint
    &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Java&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;ArtifactMetaData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newArtifact&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;byGroupId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-group&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;artifacts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artifactData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// TypeScript&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;newArtifact&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;byGroupId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;my-group&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;artifacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;artifactData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// Go&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;artifactData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCreateArtifact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Set fields of artifactData...&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;newArtifact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ByGroupId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-group&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Artifacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;artifactData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;benefits-of-kiota-generated-sdks&quot;&gt;Benefits of Kiota-Generated SDKs&lt;/h2&gt;

&lt;h3 id=&quot;consistency-across-languages&quot;&gt;&lt;strong&gt;Consistency Across Languages&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kiota ensures that the SDK behavior remains consistent across all supported languages, reducing
cognitive overhead for developers working in multi-language environments.&lt;/p&gt;

&lt;h3 id=&quot;type-safety&quot;&gt;&lt;strong&gt;Type Safety&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Each generated SDK provides strong type checking, which helps catch potential errors during
compile-time and improves overall code quality.&lt;/p&gt;

&lt;h3 id=&quot;minimal-dependencies&quot;&gt;&lt;strong&gt;Minimal Dependencies&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kiota-generated SDKs are designed to be lightweight, with minimal external dependencies, resulting
in faster build times and smaller application footprints.&lt;/p&gt;

&lt;h3 id=&quot;automatic-updates&quot;&gt;&lt;strong&gt;Automatic Updates&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;As the Apicurio Registry API evolves, the Kiota-generated SDKs can be quickly regenerated to reflect
the latest API specification.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;To use the new Kiota-generated SDKs, simply:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Choose your preferred language&lt;/li&gt;
  &lt;li&gt;Install the SDK via your language’s package manager&lt;/li&gt;
  &lt;li&gt;Import the Apicurio Registry client&lt;/li&gt;
  &lt;li&gt;Start interacting with the Registry API immediately&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;example-java-sdk&quot;&gt;&lt;strong&gt;Example: Java SDK&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;To use the Apicurio Registry Java SDK, first add the dependency to your Maven &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.apicurio&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;apicurio-registry-java-sdk&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.0.6&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, you can interact with the Registry API:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io.apicurio.registry.rest.client.RegistryClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io.apicurio.registry.rest.client.models.SystemInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io.kiota.http.vertx.VertXRequestAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io.vertx.core.Vertx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BlogExampleApp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// URL of the Apicurio Registry server.&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;registryUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;http://localhost:8080/apis/registry/v3&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// The java SDK uses a Vertx based Kiota request adapter.&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Vertx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vertx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vertx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;vertx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;VertXRequestAdapter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vertXRequestAdapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VertXRequestAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;vertXRequestAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setBaseUrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;registryUrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// Create the registry client.&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;RegistryClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegistryClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertXRequestAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Use the client to get the registry&apos;s system info.&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;SystemInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;systemInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;systemInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getDescription&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Version &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// The vertx instance must be closed.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;vertx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-typescript-sdk&quot;&gt;&lt;strong&gt;Example: TypeScript SDK&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Install the SDK using npm (or include it in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @apicurio/apicurio-registry-sdk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then use it in your TypeScript or JavaScript project:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ApicurioRegistryClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@sdk/lib/generated-client/apicurioRegistryClient.ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RegistryClientFactory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@sdk/lib/sdk&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SystemInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@sdk/lib/generated-client/models&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// URL of the registry&apos;s v3 REST API&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;apiUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://localhost:8080/apis/registry/v3&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Create an instance of the client&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ApicurioRegistryClient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RegistryClientFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createRegistryClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;apiUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Get and print the system info from the &quot;/system/info&quot; endpoint&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SystemInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Version &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-python-sdk&quot;&gt;&lt;strong&gt;Example: Python SDK&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Install the SDK using pip:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;apicurioregistrysdk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use the SDK in your Python project:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;asyncio&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;kiota_abstractions.authentication.anonymous_authentication_provider&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AnonymousAuthenticationProvider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;kiota_http.httpx_request_adapter&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpxRequestAdapter&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;apicurioregistrysdk.client.registry_client&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegistryClient&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;auth_provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AnonymousAuthenticationProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Create a standard request adapter and set the API URL
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;request_adapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpxRequestAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;request_adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;http://localhost:8080/apis/registry/v3&quot;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Create the client
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegistryClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Get and print the system info from the &quot;/system/info&quot; endpoint
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;systemInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;systemInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;systemInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Version &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-go-sdk&quot;&gt;&lt;strong&gt;Example: Go SDK&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;To use the Apicurio Registry Go SDK, add the dependency to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go.mod&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;module example

go 1.23.0

toolchain go1.24.1

require (
	github.com/apicurio/apicurio-registry/go-sdk v0.0.0-20241211181742-779f0994a1de // v3.0.6
	github.com/microsoft/kiota-abstractions-go v1.9.1
	github.com/microsoft/kiota-http-go v1.5.1
)

require (
    // Indirect dependencies...
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then use it in your Go project:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
   &lt;span class=&quot;s&quot;&gt;&quot;context&quot;&lt;/span&gt;
   &lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;registry3&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;github.com/apicurio/apicurio-registry/go-sdk/pkg/registryclient-v3&quot;&lt;/span&gt;
   &lt;span class=&quot;s&quot;&gt;&quot;github.com/apicurio/apicurio-registry/go-sdk/pkg/registryclient-v3/models&quot;&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;kiotaAuth&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;github.com/microsoft/kiota-abstractions-go/authentication&quot;&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;github.com/microsoft/kiota-http-go&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;registryUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;registry3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;c&quot;&gt;// Compression is not currently supported by the Apicurio Registry server.&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetDefaultClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewRetryHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewRedirectHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewParametersNameDecodingHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCompressionHandlerWithOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCompressionOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewUserAgentHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewHeadersInspectionHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kiotaHttp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewNetHttpRequestAdapterWithParseNodeFactoryAndSerializationWriterFactoryAndHttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kiotaAuth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnonymousAuthenticationProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;panic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unexpected error: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetBaseUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;registryUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;registry3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getServerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;registry3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Server name: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Server version: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;handleError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handleError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;panic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;No error value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProblemDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Server error: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetDetail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unknown error: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;registryUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;http://localhost:8080/apis/registry/v3&quot;&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;registryUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;getServerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;See the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/examples/sdk/go-sdk&quot;&gt;full code of the example in our Apicurio Registry repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Apicurio Registry 3.0’s adoption of Kiota represents a significant advancement in API SDK generation. By providing consistent, type-safe, and easily maintainable SDKs across Java, TypeScript, Python, and Golang, the project has dramatically improved the developer experience.&lt;/p&gt;

&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.apicur.io/registry/&quot;&gt;Apicurio Registry Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/kiota&quot;&gt;Kiota GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Stay tuned for more updates and improvements in Apicurio Registry!&lt;/em&gt;&lt;/p&gt;
</description><category term="registry" /><category term="rest_api" /><category term="sdk" /><pubDate>Tue, 25 Mar 2025 16:39:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/03/25/registry-30-sdks</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/03/25/registry-30-sdks</guid>
        </item><item>
            <title>Core REST API Changes in Apicurio Registry 3.0</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;The release of Apicurio Registry 3.0 marks a significant milestone in our journey to provide
the best possible Registry experience. With this major version update, we’ve taken the
opportunity to implement several breaking changes to the Core REST API that both add powerful
new functionality and streamline existing features.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;why-these-changes-matter&quot;&gt;Why These Changes Matter&lt;/h1&gt;

&lt;p&gt;Before diving into the specific changes, it’s worth understanding the philosophy behind this
update. Our goal was to create a more consistent, logical, and powerful API surface that better
aligns with how users actually interact with the registry. These changes reflect feedback from
the community and our own experience supporting users and Red Hat customers over time.&lt;/p&gt;

&lt;p&gt;You can find the new core REST API here:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://registry.example.org/apis/registry/v3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;key-architectural-changes&quot;&gt;Key Architectural Changes&lt;/h1&gt;

&lt;h2 id=&quot;separation-of-artifact-and-version-concepts&quot;&gt;Separation of Artifact and Version Concepts&lt;/h2&gt;

&lt;p&gt;Perhaps the most fundamental change is the clear separation between artifacts and their versions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Artifacts now have their own metadata, separate from version metadata&lt;/li&gt;
  &lt;li&gt;Artifacts can exist without any versions (empty artifacts)&lt;/li&gt;
  &lt;li&gt;Content is now exclusively managed through version endpoints&lt;/li&gt;
  &lt;li&gt;Only versions have state (artifacts no longer have state)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This distinction allows for more granular control and creates a clearer mental model of the registry
structure.&lt;/p&gt;

&lt;h2 id=&quot;empty-artifacts-configure-first-create-later&quot;&gt;Empty Artifacts: Configure First, Create Later&lt;/h2&gt;
&lt;p&gt;One of the new capabilities in Apicurio Registry 3.0 is the ability to create empty artifacts without
immediately providing content for a first version. This seemingly small change enables important new
workflows, particularly around governance and validation.&lt;/p&gt;

&lt;p&gt;With empty artifacts, you can now:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Configure content rules before any versions exist&lt;/li&gt;
  &lt;li&gt;Set up metadata, labels, and ownership for an artifact before content development begins&lt;/li&gt;
  &lt;li&gt;Establish the artifact’s place in your organization’s taxonomy in advance&lt;/li&gt;
  &lt;li&gt;Create placeholder artifacts as part of planning activities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This feature is especially valuable for teams implementing strict governance processes. For example,
you can now:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create an empty artifact with appropriate metadata&lt;/li&gt;
  &lt;li&gt;Configure validation rules that enforce your organization’s standards&lt;/li&gt;
  &lt;li&gt;Set up branch structures in anticipation of development&lt;/li&gt;
  &lt;li&gt;Only then begin creating versions with content that must comply with the pre-established rules&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This “configure first, create later” approach helps prevent non-compliant content from entering the
registry in the first place, rather than discovering compliance issues after content has been created.&lt;/p&gt;

&lt;h2 id=&quot;hierarchical-rules-system&quot;&gt;Hierarchical Rules System&lt;/h2&gt;
&lt;p&gt;Apicurio Registry 3.0 expands the hierarchical rules system with the implementation of the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/rules&lt;/code&gt; endpoint. Rules can now be configured at three distinct levels in the registry,
creating a powerful inheritance model for content governance:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Global Rules&lt;/strong&gt; - Apply to all artifacts across the entire registry&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Group Rules&lt;/strong&gt; - Apply to all artifacts within a specific group&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Artifact Rules&lt;/strong&gt; - Apply to a specific artifact only&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Rules are evaluated with a clear precedence order: &lt;strong&gt;Artifact&lt;/strong&gt; rules override &lt;strong&gt;Group&lt;/strong&gt; rules, which override
&lt;strong&gt;Global&lt;/strong&gt; rules. This hierarchy allows for a sophisticated governance approach where broad standards can
be established at the global level, group-specific requirements added at the group level, and
artifact-specific customizations made at the individual artifact level.&lt;/p&gt;

&lt;p&gt;For example, you might implement:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Global&lt;/strong&gt; rules that enforce basic structural validation across all artifacts&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Group&lt;/strong&gt; rules that enforce naming conventions specific to a business unit&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Artifact&lt;/strong&gt; rules that implement specialized compatibility checks for critical APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This multi-tiered approach provides both consistency where needed and flexibility where required. It
significantly reduces rule management overhead by allowing you to define common rules at higher levels
while maintaining the ability to customize validation for specific artifacts.&lt;/p&gt;

&lt;p&gt;The Group Rules API follows a familiar pattern using standard HTTP methods:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;GET&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/rules&lt;/code&gt; - List all rules for a group&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;POST&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/rules&lt;/code&gt; - Create a new rule for a group&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;DELETE&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/rules&lt;/code&gt; - Delete all rules for a group&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;introduction-of-branches&quot;&gt;Introduction of Branches&lt;/h2&gt;

&lt;p&gt;A major new feature is the addition of branches, which provide:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Multiple development paths for artifacts&lt;/li&gt;
  &lt;li&gt;More flexible versioning strategies&lt;/li&gt;
  &lt;li&gt;Improved organization for complex artifact evolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New branch endpoints provide comprehensive management capabilities including creation, updating
metadata, managing versions within branches, and branch deletion.&lt;/p&gt;

&lt;h1 id=&quot;detailed-api-changes&quot;&gt;Detailed API Changes&lt;/h1&gt;

&lt;h2 id=&quot;endpoint-reorganization&quot;&gt;Endpoint Reorganization&lt;/h2&gt;

&lt;p&gt;Several endpoints have been moved or renamed for consistency:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/admin/artifactsType&lt;/code&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/admin/config/artifactTypes&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Version metadata management now happens at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/artifacts/:artifactId/versions/:versionExpression&lt;/code&gt; (previously, this endpoint managed content)&lt;/li&gt;
  &lt;li&gt;Version content is now accessed at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/artifacts/:artifactId/versions/:versionExpression/content&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;enhanced-group-management&quot;&gt;Enhanced Group Management&lt;/h2&gt;

&lt;p&gt;Groups have received significant enhancements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Added support for labels on groups&lt;/li&gt;
  &lt;li&gt;New &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT&lt;/code&gt; operation to update group metadata (description, labels, owner)&lt;/li&gt;
  &lt;li&gt;Implemented group rules endpoints at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/rules&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Added pagination to group-related endpoints&lt;/li&gt;
  &lt;li&gt;Changed “createdBy” to “owner” for improved clarity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;metadata-simplification&quot;&gt;Metadata Simplification&lt;/h2&gt;

&lt;p&gt;We’ve simplified the metadata structure throughout the API:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Combined “labels” and “properties” into a single “labels” concept (name/value pairs)&lt;/li&gt;
  &lt;li&gt;Changed “type” to “ruleType” for consistency with “artifactType”&lt;/li&gt;
  &lt;li&gt;Renamed parameters for clarity where appropriate&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;improved-search-capabilities&quot;&gt;Improved Search Capabilities&lt;/h2&gt;

&lt;p&gt;Search functionality has been enhanced:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Added GET &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/groups&lt;/code&gt; for searching groups with filtering and pagination&lt;/li&gt;
  &lt;li&gt;Added GET &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/versions&lt;/code&gt; to search/filter all versions&lt;/li&gt;
  &lt;li&gt;Added POST &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/versions&lt;/code&gt; to search by version content&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;content-management-changes&quot;&gt;Content Management Changes&lt;/h2&gt;

&lt;p&gt;Content handling has been significantly revised:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Removed POST &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/artifacts&lt;/code&gt; for raw artifact content&lt;/li&gt;
  &lt;li&gt;Changed content-type requirements for artifact creation&lt;/li&gt;
  &lt;li&gt;Added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dryRun&lt;/code&gt; query parameter to several endpoints (replaces the old “/test” endpoint)&lt;/li&gt;
  &lt;li&gt;All content creation operations now require an explicit &lt;strong&gt;Content-Type&lt;/strong&gt; header&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;removed-endpoints&quot;&gt;Removed Endpoints&lt;/h2&gt;

&lt;p&gt;Several endpoints have been removed as their functionality has been integrated elsewhere:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/artifacts/:artifactId/meta&lt;/code&gt; (metadata is now managed at the artifact endpoint)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/artifacts/:artifactId/state&lt;/code&gt; (state now only exists on versions)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/artifacts/:artifactId/owner&lt;/code&gt; (owner now part of editable metadata)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/:groupId/artifacts/:artifactId/test&lt;/code&gt; (replaced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dryRun&lt;/code&gt; query parameter)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;migration-considerations&quot;&gt;Migration Considerations&lt;/h1&gt;

&lt;p&gt;If you’re upgrading from Apicurio Registry 2.x to 3.0, you’ll need to update your client applications
to accommodate these changes. Here are some key migration points:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Update your client code to use the new endpoint paths&lt;/li&gt;
  &lt;li&gt;Revise your content management workflow to use the new version-focused approach&lt;/li&gt;
  &lt;li&gt;Take advantage of the new branch feature if you need multiple development paths&lt;/li&gt;
  &lt;li&gt;Consolidate your use of properties and labels into the unified labels concept&lt;/li&gt;
  &lt;li&gt;Update any automation that depends on artifact state (now only versions have state)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;core-api-backwards-compatibility&quot;&gt;Core API Backwards Compatibility&lt;/h2&gt;

&lt;p&gt;It’s worth noting that, while we encourage users to migrate to v3 of the API as soon as possible,
we will continue to support the v2 API (now deprecated) - presumably until v4!&lt;/p&gt;

&lt;p&gt;What that means is that the old v2 API endpoint should still work:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://registry.example.org/apis/registry/v2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h1&gt;

&lt;p&gt;These changes lay the groundwork for even more powerful features in future releases. The clearer
separation of artifacts, versions, and branches opens up possibilities for more sophisticated
governance, improved collaboration workflows, and enhanced content validation.&lt;/p&gt;

&lt;p&gt;We’re excited to see how the community embraces these changes and builds on them. As always, we
welcome your feedback and contributions to make Apicurio Registry even better.&lt;/p&gt;
</description><category term="registry" /><category term="rest_api" /><pubDate>Mon, 24 Mar 2025 14:57:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/03/24/rest-api-changes</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/03/24/rest-api-changes</guid>
        </item><item>
            <title>Semantic Versioning Support in Apicurio Registry 3.0</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;Apicurio Registry 3.0 introduces a powerful new feature: support for automatic semantic
versioning branches. This enhancement brings automatic branch management based on semantic
versioning patterns, making it easier to organize, track, and access different versions of
your schema artifacts. Let’s explore how this feature works and why it’s valuable for API
governance.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;understanding-semantic-versioning&quot;&gt;Understanding Semantic Versioning&lt;/h1&gt;
&lt;p&gt;Before diving into Apicurio’s implementation, let’s quickly review semantic versioning. 
Often abbreviated as “SemVer,” semantic versioning follows a pattern of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAJOR.MINOR.PATCH&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;MAJOR&lt;/strong&gt; version increases represent incompatible API changes&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MINOR&lt;/strong&gt; version increases add functionality in a backward-compatible manner&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PATCH&lt;/strong&gt; version increases represent bug fixes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structured approach to versioning makes it clear to users how a new version might impact
their existing implementations.&lt;/p&gt;

&lt;h1 id=&quot;how-semantic-versioning-works-in-apicurio-registry-30&quot;&gt;How Semantic Versioning Works in Apicurio Registry 3.0&lt;/h1&gt;
&lt;p&gt;Apicurio Registry now automatically creates and manages branch hierarchies based on semantic
version numbers. When you add artifacts with semantic version numbers, the registry intelligently
organizes them into appropriate branches.&lt;/p&gt;

&lt;h1 id=&quot;automatic-branch-creation&quot;&gt;Automatic Branch Creation&lt;/h1&gt;
&lt;p&gt;Whenever versions are created with semantic versioning patterns, Apicurio Registry automatically
creates corresponding branches at different levels of specificity:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Major version branches (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.x&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Minor version branches (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.x&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest&lt;/code&gt; branch that always points to the most recent version&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, when creating versions like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.0&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.1&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.2&lt;/code&gt;, the registry automatically
creates branches such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.x&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.x&lt;/code&gt; without any additional configuration.&lt;/p&gt;

&lt;h1 id=&quot;branch-behavior-and-characteristics&quot;&gt;Branch Behavior and Characteristics&lt;/h1&gt;
&lt;p&gt;The test code reveals several important aspects of these semantic versioning branches:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;System-defined branches&lt;/strong&gt;: These branches are created and managed by the system, not manually created by users.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Protection from deletion&lt;/strong&gt;: System-generated branches cannot be deleted, ensuring the integrity of the version hierarchy.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Automatic content management&lt;/strong&gt;: Each branch automatically contains the relevant versions that match its pattern.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Latest version awareness&lt;/strong&gt;: Each branch knows which version is the latest within its scope.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;example-workflow&quot;&gt;Example Workflow&lt;/h1&gt;
&lt;p&gt;Let’s walk through an example scenario based on the test case:&lt;/p&gt;

&lt;p&gt;Create an artifact with version: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add subsequent versions: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.3&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.0&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.0.0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After these operations, Apicurio Registry automatically creates these branches:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest&lt;/code&gt; (containing all 9 versions, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.0.0&lt;/code&gt; as the latest)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.x&lt;/code&gt; (containing all versions starting with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.&lt;/code&gt;, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.1&lt;/code&gt; as the latest)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.x&lt;/code&gt; (containing all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.&lt;/code&gt; versions, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.3&lt;/code&gt; as the latest)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.x&lt;/code&gt; (containing all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.&lt;/code&gt; versions, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.1&lt;/code&gt; as the latest)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.x&lt;/code&gt; (containing all versions starting with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.&lt;/code&gt;, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.2&lt;/code&gt; as the latest)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.x&lt;/code&gt; (containing all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.&lt;/code&gt; versions, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.2&lt;/code&gt; as the latest)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.x&lt;/code&gt; (containing all versions starting with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.&lt;/code&gt;, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.0.0&lt;/code&gt; as the latest)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.0.x&lt;/code&gt; (containing all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.0.&lt;/code&gt; versions, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.0.0&lt;/code&gt; as the latest)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;configuring-semantic-versioning-in-apicurio-registry&quot;&gt;Configuring Semantic Versioning in Apicurio Registry&lt;/h1&gt;
&lt;p&gt;It’s important to note that semantic versioning support in Apicurio Registry 3.0 is disabled
by default. To take advantage of this feature, you need to explicitly enable it through
configuration settings.&lt;/p&gt;

&lt;h2 id=&quot;enabling-semantic-versioning&quot;&gt;Enabling Semantic Versioning&lt;/h2&gt;
&lt;p&gt;You can enable semantic versioning in Apicurio Registry 3.0 by setting the appropriate 
environment variable.&lt;/p&gt;

&lt;h3 id=&quot;environment-variable&quot;&gt;Environment Variable&lt;/h3&gt;
&lt;p&gt;When deploying Apicurio Registry, set the following environment variable:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;APICURIO_SEMVER_BRANCHING_ENABLED=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;configuring-via-docker&quot;&gt;Configuring via Docker&lt;/h3&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:8080 &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;APICURIO_SEMVER_BRANCHING_ENABLED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true &lt;/span&gt;apicurio/apicurio-registry:latest-release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;verifying-the-configuration&quot;&gt;Verifying the Configuration&lt;/h2&gt;
&lt;p&gt;You can verify that semantic versioning is enabled by checking the registry’s API documentation
or by creating an artifact with a semantic version and confirming that the expected branches are
automatically created.&lt;/p&gt;

&lt;h1 id=&quot;benefits-of-semantic-versioning-in-apicurio-registry&quot;&gt;Benefits of Semantic Versioning in Apicurio Registry&lt;/h1&gt;
&lt;p&gt;The implementation of semantic versioning in Apicurio Registry 3.0 brings several significant benefits:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Simplified Version Navigation&lt;/strong&gt;
With automatically created branches, you can easily access specific version families. Need the latest stable version in the 1.x series? Simply reference the 1.x branch. This eliminates the need to manually search through all versions to find the one you need.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Predictable Version Management&lt;/strong&gt;
The semantic versioning pattern creates a predictable structure for managing artifact versions. API consumers can rely on established conventions for understanding compatibility implications when upgrading to newer versions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improved API Governance&lt;/strong&gt;
The automatic branch management supports better API governance by making it easier to maintain multiple version lines simultaneously. Teams can easily support older major versions for backward compatibility while continuing to develop newer versions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reduced Manual Configuration&lt;/strong&gt;
Since branches are automatically created and managed by the system based on semantic version patterns, users don’t need to manually create and maintain branch structures.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;using-semantic-versioning-in-practice&quot;&gt;Using Semantic Versioning in Practice&lt;/h1&gt;
&lt;p&gt;To use semantic versioning in Apicurio Registry 3.0 with curl commands, follow these examples
that demonstrate how to create and work with semantically versioned artifacts:&lt;/p&gt;

&lt;h2 id=&quot;create-a-group&quot;&gt;Create a Group&lt;/h2&gt;
&lt;p&gt;First, let’s create a group to organize our artifacts:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;groupId&quot;: &quot;my-semver-group&quot;}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;creating-an-artifact-with-initial-semantic-version&quot;&gt;Creating an Artifact with Initial Semantic Version&lt;/h2&gt;
&lt;p&gt;Now, let’s create an artifact with an initial semantic version (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.0&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups/my-semver-group/artifacts&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{
       &quot;artifactId&quot;: &quot;my-semver-artifact&quot;,
       &quot;artifactType&quot;: &quot;JSON&quot;,
       &quot;firstVersion&quot;: {
         &quot;version&quot;: &quot;1.0.0&quot;,
         &quot;content&quot;: {
            &quot;content&quot;: &quot;{\&quot;type\&quot;:\&quot;record\&quot;,\&quot;name\&quot;:\&quot;ExampleType\&quot;,\&quot;fields\&quot;:[{\&quot;name\&quot;:\&quot;foo\&quot;,\&quot;type\&quot;:\&quot;string\&quot;}]}&quot;,
            &quot;contentType&quot;: &quot;application/json&quot;
         }
       }
     }&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This creates the artifact with an explicitly specified semantic version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.0&lt;/code&gt; rather than
using the default version numbering scheme for a new artifact version, which in this case
would result in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;adding-additional-semantic-versions&quot;&gt;Adding Additional Semantic Versions&lt;/h2&gt;
&lt;p&gt;To add a new version to the artifact following the semantic versioning pattern:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups/my-semver-group/artifacts/my-semver-artifact/versions&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{
       &quot;version&quot;: &quot;1.0.1&quot;,
       &quot;content&quot;: {
         &quot;content&quot;: &quot;{\&quot;type\&quot;:\&quot;record\&quot;,\&quot;name\&quot;:\&quot;ExampleType\&quot;,\&quot;fields\&quot;:[{\&quot;name\&quot;:\&quot;foo-2\&quot;,\&quot;type\&quot;:\&quot;string\&quot;}]}&quot;,
         &quot;contentType&quot;: &quot;application/json&quot;
       }
     }&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You could then continue to add more versions with additional semantic versions:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Adding version 1.1.0&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups/my-semver-group/artifacts/my-semver-artifact/versions&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{
       &quot;version&quot;: &quot;1.1.0&quot;,
       &quot;content&quot;: {
         &quot;content&quot;: &quot;{\&quot;type\&quot;:\&quot;record\&quot;,\&quot;name\&quot;:\&quot;ExampleType\&quot;,\&quot;fields\&quot;:[{\&quot;name\&quot;:\&quot;foo-3\&quot;,\&quot;type\&quot;:\&quot;string\&quot;}]}&quot;,
         &quot;contentType&quot;: &quot;application/json&quot;
       }
     }&apos;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Adding version 2.0.0&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups/my-semver-group/artifacts/my-semver-artifact/versions&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{
       &quot;version&quot;: &quot;2.0.0&quot;,
       &quot;content&quot;: {
         &quot;content&quot;: &quot;{\&quot;type\&quot;:\&quot;record\&quot;,\&quot;name\&quot;:\&quot;ExampleType\&quot;,\&quot;fields\&quot;:[{\&quot;name\&quot;:\&quot;foo-4\&quot;,\&quot;type\&quot;:\&quot;string\&quot;}]}&quot;,
         &quot;contentType&quot;: &quot;application/json&quot;
       }
     }&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;accessing-branches&quot;&gt;Accessing Branches&lt;/h2&gt;
&lt;p&gt;Once you’ve created several versions with semantic versioning, you can access the automatically
generated branches:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;List all branches for the artifact&quot;&lt;/span&gt;
curl &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups/my-semver-group/artifacts/my-semver-artifact/branches&quot;&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The response will include all automatically created branches based on the semantic versioning
structure, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.x&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.x&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;accessing-versions-within-a-specific-branch&quot;&gt;Accessing Versions Within a Specific Branch&lt;/h2&gt;
&lt;p&gt;To retrieve all versions within a specific branch, such as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.x&lt;/code&gt; branch:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups/my-semver-group/artifacts/my-semver-artifact/branches/1.x/versions&quot;&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will return all versions that match the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.x&lt;/code&gt; pattern, with the latest version in that
branch listed first.&lt;/p&gt;

&lt;h2 id=&quot;getting-the-latest-version-in-a-branch&quot;&gt;Getting the Latest Version in a Branch&lt;/h2&gt;
&lt;p&gt;To access the latest version’s content or metadata from a specific branch:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metadata&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups/my-semver-group/artifacts/my-semver-artifact/versions/branch=1.x&quot;&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will return the metadata for version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.0&lt;/code&gt;, because that is the most recent version
on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.x&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:8080/apis/registry/v3/groups/my-semver-group/artifacts/my-semver-artifact/versions/branch=1.x/content&quot;&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will return the content for version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.0&lt;/code&gt;, because that is the most recent version
on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.x&lt;/code&gt; branch.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Semantic versioning support in Apicurio Registry 3.0 represents a significant enhancement for
managing schema evolution. The automatic creation and management of version-based branches
simplifies navigation, improves governance, and provides a more intuitive way to work with
multiple versions of your artifacts.&lt;/p&gt;

&lt;p&gt;By leveraging this feature, teams can more effectively manage their API lifecycle, ensuring
that consumers can reliably access the appropriate versions while maintaining clear expectations
around compatibility and change.&lt;/p&gt;

&lt;p&gt;If you’re working with evolving schemas or APIs, Apicurio Registry’s semantic versioning support
provides a powerful framework for maintaining order in what could otherwise become a complex
version management challenge. Remember to explicitly enable this feature in your deployment
configuration to take advantage of its benefits.&lt;/p&gt;

&lt;h1 id=&quot;bonus-content&quot;&gt;Bonus Content&lt;/h1&gt;
&lt;p&gt;There are two additional optional features that can be enabled to improve the functionality
of the semantic versioning support in Apicurio Registry 3.0.&lt;/p&gt;

&lt;h2 id=&quot;version-validation&quot;&gt;Version Validation&lt;/h2&gt;
&lt;p&gt;Apicurio Registry can validate that every artifact version you add conforms to the SemVer
format, or else it will be rejected. This feature can be enabled by setting the following
environment variable:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;APICURIO_SEMVER_VALIDATION_ENABLED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;version-coercion&quot;&gt;Version Coercion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: wonderful name for a feature.  Just say that out loud and delight in it.&lt;/p&gt;

&lt;p&gt;Apicurio Registry can be configured to automatically coerce invalid versions into
valid Semantic Versioning 2.0 compatible versions (when possible).  For example, when this
feature is enabled and you create a new version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.0&lt;/code&gt;, the version will be coerced into
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.0.0&lt;/code&gt;. This feature can be enabled by setting the following environment variable:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;APICURIO_SEMVER_BRANCHING_COERCE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;future-plans&quot;&gt;Future Plans&lt;/h1&gt;
&lt;p&gt;Automatic creation of semantic versioning branches is a great start to our overall planned
support for semantic versioning.  We will continue exploring this space in the future to
push semver support even further.  For example, it might be nice if the Compatibility Rule
understood semver so that its concept of compatibility was consistent with the version of
a schema or API being added.  In other words, the compatibility rules might differ if you
are adding version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.7&lt;/code&gt; (a patch release) vs. version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4.0.0&lt;/code&gt; (a major release).&lt;/p&gt;
</description><category term="registry" /><category term="semantic_versioning" /><pubDate>Fri, 21 Mar 2025 16:17:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/03/21/semantic-versioning</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/03/21/semantic-versioning</guid>
        </item><item>
            <title>Exporting from Confluent Schema Registry to Apicurio Registry</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;If you’re using Confluent Schema Registry and looking to migrate your schemas to &lt;strong&gt;Apicurio Registry&lt;/strong&gt;, you’re
in luck! The &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/utils/exportConfluent&quot;&gt;Confluent Export Tool&lt;/a&gt;
provides a simple way to export all schemas from a &lt;strong&gt;Confluent Schema Registry&lt;/strong&gt; in a format that is directly
importable into &lt;strong&gt;Apicurio Registry&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this blog post, we’ll walk through what the tool does, how to use it, and some key considerations for a smooth 
migration.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-is-the-confluent-export-tool&quot;&gt;What is the Confluent Export Tool?&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Confluent Export Tool&lt;/strong&gt; is a utility designed to extract all content from a Confluent Schema Registry instance
and save it into a format that is compatible with Apicurio Registry. The exported data can then be seamlessly imported
into Apicurio using its import capabilities.&lt;/p&gt;

&lt;h3 id=&quot;key-features&quot;&gt;Key Features:&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Full Export&lt;/strong&gt;: Captures all schemas from Confluent Schema Registry.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Apicurio Registry Compatible Format&lt;/strong&gt;: Structures data to match Apicurio Registry’s import requirements.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Easy to Use&lt;/strong&gt;: A simple command-line tool requiring minimal setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-use-the-confluent-export-tool&quot;&gt;How to Use the Confluent Export Tool&lt;/h2&gt;

&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Before using the tool, ensure you have:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A running &lt;strong&gt;Confluent Schema Registry&lt;/strong&gt; instance.&lt;/li&gt;
  &lt;li&gt;Java installed (Java 17 or later recommended).&lt;/li&gt;
  &lt;li&gt;Network access to the Confluent Schema Registry API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;steps-to-export-data&quot;&gt;Steps to Export Data&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Clone the Apicurio Registry Repository&lt;/strong&gt;:
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/Apicurio/apicurio-registry.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;apicurio-registry
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Build the Tool&lt;/strong&gt;:
Build the project using Maven:
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mvn clean package &lt;span class=&quot;nt&quot;&gt;-DskipTests&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;-pl&lt;/span&gt; utils/exportConfluent &lt;span class=&quot;nt&quot;&gt;-am&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run the Export Tool&lt;/strong&gt;:
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;utils/exportConfluent
java &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; target/apicurio-registry-utils-exportConfluent-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-runner&lt;/span&gt;.jar &amp;lt;schema-registry-url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;schema-registry-url&amp;gt;&lt;/code&gt; with the actual URL of your Confluent Schema Registry (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8081&lt;/code&gt;).&lt;/p&gt;

    &lt;p&gt;When complete, a new file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;confluent-schema-registry-export.zip&lt;/code&gt; will be created in the current working directory.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;importing-into-apicurio-registry&quot;&gt;Importing into Apicurio Registry&lt;/h2&gt;

&lt;p&gt;Once you have the exported ZIP file, you can import it into Apicurio Registry using the &lt;strong&gt;Apicurio Import API&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;http://&amp;lt;registry-url&amp;gt;/apis/registry/v3/admin/import&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Accept: application/json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/zip&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--data-binary&lt;/span&gt; @confluent-schema-registry-export.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you could log into the Apicurio Registry UI and import the file using the “&lt;em&gt;Import from .ZIP&lt;/em&gt;”
option found on the &lt;strong&gt;Explore&lt;/strong&gt; tab.&lt;/p&gt;

&lt;h2 id=&quot;considerations-and-best-practices&quot;&gt;Considerations and Best Practices&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Backup Your Data&lt;/strong&gt;: Before migration, always make a backup of your Confluent Schema Registry.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Confluent Authentication&lt;/strong&gt;: If your Confluent Schema Registry requires authentication, configure appropriate credentials for the export tool.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Apicurio Authentication&lt;/strong&gt;: If your Apicurio Registry instance requires authentication, configure appropriate credentials in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; command, or simply use the Registry UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Confluent Export Tool&lt;/strong&gt; simplifies migration from &lt;strong&gt;Confluent Schema Registry&lt;/strong&gt; to &lt;strong&gt;Apicurio Registry&lt;/strong&gt;, making
it easier for organizations to adopt Apicurio’s open-source schema management platform. By following the steps above,
you can efficiently transition your schemas and continue managing them in Apicurio.&lt;/p&gt;

&lt;p&gt;For more details, check out the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/main/utils/exportConfluent&quot;&gt;official GitHub repository&lt;/a&gt;
and start your migration today!&lt;/p&gt;
</description><category term="registry" /><category term="confluent" /><category term="migration" /><pubDate>Thu, 20 Mar 2025 13:22:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/03/20/export-from-confluent</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/03/20/export-from-confluent</guid>
        </item><item>
            <title>Red Hat middleware team transitioning to IBM</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;Hey everyone - I would like to provide an update to the Apicurio community regarding
the recent announcement from Red Hat:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.redhat.com/en/blog/evolving-our-middleware-strategy&quot;&gt;Evolving our middleware strategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post I will try to provide some context and explain how this change might 
impact us.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;evolving-middleware-strategy-at-ibmred-hat-tldr&quot;&gt;Evolving middleware strategy at IBM/Red Hat (tldr)&lt;/h1&gt;
&lt;p&gt;As some of you may know, the Apicurio community is sponsored by Red Hat.  Most
of the core contributors are Red Hat employees, starting from long before we
were acquired by IBM back in 2019.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(I myself have been a Red Hat employee since 2012)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Red Hat has multiple engineering teams, including (but not limited to):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Operating System (Red Hat Enterprise Linux)&lt;/li&gt;
  &lt;li&gt;Kubernetes (Red Hat OpenShift)&lt;/li&gt;
  &lt;li&gt;Ansible (Red Hat Ansible Automation Platform)&lt;/li&gt;
  &lt;li&gt;Middleware (JBoss, Quarkus, OpenJDK)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The team responsible for the middleware projects (such as JBoss, Quarkus, OpenJDK,
Kafka, Camel) is being transitioned from Red Hat to IBM.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-the-change&quot;&gt;Why the change?&lt;/h2&gt;
&lt;p&gt;Both Red Hat and IBM have made investments in Java middleware.  This transition is an 
opportunity to join forces, especially at a time when much attention is being directed 
toward AI and other strategic initiatives.&lt;/p&gt;

&lt;p&gt;Given its more limited resources, Red Hat has not been able to focus as much on
expanding our middleware products as much as they deserve.  Moving these teams to
IBM will allow us to refocus, grow, and collaborate more effectively.&lt;/p&gt;

&lt;h2 id=&quot;what-does-this-mean-for-apicurio&quot;&gt;What does this mean for Apicurio?&lt;/h2&gt;
&lt;p&gt;We don’t expect any changes, and I’m guessing that if I didn’t post anything here
most of the Apicurio community would never know about the transition at all.  I am
sharing this because I believe transparency is important.&lt;/p&gt;

&lt;p&gt;The good news for Apicurio is that there are no competing IBM products.
In fact, Apicurio Registry is already included as part of the IBM Event Streams
product.  My &lt;strong&gt;expectation&lt;/strong&gt; is that Apicurio Registry will continue as normal.
My &lt;strong&gt;hope&lt;/strong&gt; is that we can revitalize the Apicurio Studio project by finding it 
a new home within IBM’s broader portfolio.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;We’re working to release &lt;strong&gt;Red Hat build of Apicurio Registry 3.0 GA&lt;/strong&gt; before
the transition.  This means a &lt;strong&gt;3.0.7 release&lt;/strong&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry&lt;/code&gt; upstream.  After
that we’ll continue evolving both Registry and Studio in exciting new ways!&lt;/p&gt;

&lt;p&gt;We highly value our community’s contributions.  If you have any questions or
concerns, please chat with us on Zulip or open a discussion on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cheers!&lt;/strong&gt;&lt;/p&gt;
</description><category term="apicurio" /><category term="ibm" /><category term="redhat" /><pubDate>Mon, 03 Mar 2025 15:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2025/03/03/transition-to-ibm</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2025/03/03/transition-to-ibm</guid>
        </item><item>
            <title>A Practical Step-by-Step Guide to Securing Your Apicurio Registry with Azure Entra ID</title><dc:creator>Abel BOUCHAMA</dc:creator>
            <author>aboucham@redhat.com</author><description>&lt;p&gt;Hey everyone, in our previous blog post, we covered the authentication process using Azure Entra ID (formerly Azure AD).&lt;/p&gt;

&lt;p&gt;In this technical guide, we will provide a step-by-step approach to securing both the Apicurio Registry UI and API. By the end, you’ll be ready to protect your Apicurio Registry using Azure Entra ID effectively. 
Let’s dive in!.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;core-features&quot;&gt;Core Features&lt;/h1&gt;
&lt;p&gt;In this blog post, we will cover the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Step-by-Step Authentication/Authorization Configuration Entra-ID to secure ApiCurio Registry&lt;/strong&gt; - A detailed walkthrough for securing the Apicurio Registry UI and API using Azure Entra ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;part-i-access-to-the-ui-secured-with-azure-entra-id&quot;&gt;Part I: Access to the UI secured with Azure Entra ID&lt;/h1&gt;

&lt;h2 id=&quot;1-create-subscription-for-service-registry-operator&quot;&gt;1. Create subscription for service registry operator:&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc create -f - &amp;lt;&amp;lt;EOF
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  labels:
    operators.coreos.com/service-registry-operator.openshift-operators: &quot;&quot;
  name: service-registry-operator
  namespace: openshift-operators
spec:
  channel: 2.x
  installPlanApproval: Automatic
  name: service-registry-operator
  source: redhat-operators
  sourceNamespace: openshift-marketplace
  startingCSV: service-registry-operator.v2.6.1
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;￼&lt;/p&gt;

&lt;h2 id=&quot;2-install-apicurio-registry-2x&quot;&gt;2. Install ApiCurio Registry 2.x&lt;/h2&gt;

&lt;h4 id=&quot;21---install-postgres-db&quot;&gt;2.1 - Install Postgres DB&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc new-project registry
oc new-app -e POSTGRESQL_USER=psql -e POSTGRESQL_PASSWORD=password -e POSTGRESQL_DATABASE=apicurio postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check that the postgres is running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1/1&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc get pods -n registry
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;22-create-simple-apicurio-registry-none-secure&quot;&gt;2.2 Create Simple ApiCurio Registry NONE Secure:&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc apply -f - &amp;lt;&amp;lt;EOF
apiVersion: registry.apicur.io/v1
kind: ApicurioRegistry
metadata:
  name: apicurioregistry-psql
spec:
  configuration:
    logLevel: DEBUG
    registryLogLevel: DEBUG
    security:
      https:
        disableHttp: true
    persistence: &quot;sql&quot;
    sql:
      dataSource:
        url: &quot;jdbc:postgresql://postgresql:5432/apicurio&quot;
        userName: &quot;psql&quot;
        password: &quot;password&quot;
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apicurioregistry.registry.apicur.io/apicurioregistry-psql created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc get pods
NAME                                                READY   STATUS    RESTARTS   AGE
apicurioregistry-psql-deployment-67996897df-8426x   1/1     Running   0          89s
postgresql-67b7c66db-wcfck                          1/1     Running   0          6m37s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; oc get svc
NAME                            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
apicurioregistry-psql-service   ClusterIP   172.30.216.0    &amp;lt;none&amp;gt;        8080/TCP   116s
postgresql                      ClusterIP   172.30.209.37   &amp;lt;none&amp;gt;        5432/TCP   7m3s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc get routes
NAME                                  HOST/PORT                                                                                 PATH   SERVICES                        PORT   TERMINATION   WILDCARD
apicurioregistry-psql-ingress-hnz2f   apicurioregistry-psql.registry.router-default.apps.com   /      apicurioregistry-psql-service   http                 None
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;23-create-route-for-https-outside-connection-required-for-the-azure-entra-id-ad-for-the-redirect-url&quot;&gt;2.3 Create Route for HTTPS (outside connection) required for the Azure Entra ID (AD) for the redirect URL:&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;NOTE: Change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;host&lt;/code&gt; for your Apicurio Registry Console. you can get it from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc get route&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc create -f - &amp;lt;&amp;lt;EOF
kind: Route
apiVersion: route.openshift.io/v1
metadata:
  name: apicurioregistry-psql
  labels:
    app: apicurioregistry-psql
spec:
  host: apicurioregistry-psql.registry.router-default.apps.com
  to:
    kind: Service
    name: apicurioregistry-psql-service
    weight: 100
  port:
    targetPort: 8080
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;route.route.openshift.io/apicurioregistry-psql created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-secure-the-apicurio-registry-with-azure-entra-id&quot;&gt;3. Secure the ApiCurio Registry with Azure Entra ID:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Login to https://portal.azure.com/&lt;/li&gt;
  &lt;li&gt;Follow the steps in the blog (Chapter - Azure Entra ID Configuration): Blog https://www.apicur.io/blog/2023/07/13/registry-azure-ad&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prepare the values for the following env from the app registration &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry-example&lt;/code&gt; in Azure Entra ID:&lt;/p&gt;

&lt;p&gt;Auth OIDC settings:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;REGISTRY_AUTH_ENABLED=true
KEYCLOAK_API_CLIENT_ID=459569e9-c5f7-410a-a6e7-8db28d7e3647 #Azure AD &amp;gt; Admin &amp;gt; App registrations &amp;gt; Your app &amp;gt; Application (client) ID
REGISTRY_UI_AUTH_TYPE=oidc
REGISTRY_AUTH_URL_CONFIGURED=https://login.microsoftonline.com/6f9ef45b-456d-49e3-b5ba-1f6fe4c0fb78/v2.0 #Azure AD &amp;gt; Admin &amp;gt; App registrations &amp;gt; Your app &amp;gt; Directory (tenant) ID
REGISTRY_OIDC_UI_CLIENT_ID=459569e9-c5f7-410a-a6e7-8db28d7e3647 #Azure AD &amp;gt; Admin &amp;gt; App registrations &amp;gt; Your app &amp;gt; Application (client) ID
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Redirect Settings:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Change the host for your Apicurio Registry deployment (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc get route&lt;/code&gt;)
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CORS_ALLOWED_ORIGINS=https://apicurioregistry-psql.registry.router-default.apps.com #The host for your Apicurio Registry deployment
REGISTRY_OIDC_UI_REDIRECT_URL=https://apicurioregistry-psql.registry.router-default.apps.com/ui/ #The host for your Apicurio Registry console
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;Authorization settings:&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ROLE_BASED_AUTHZ_ENABLED=true
QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH=roles
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apply the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env var&lt;/code&gt; settings to the existing CR &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurioregistry-psql&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oc apply -f - &amp;lt;&amp;lt;EOF
apiVersion: registry.apicur.io/v1
kind: ApicurioRegistry
metadata:
  name: apicurioregistry-psql
spec:
  configuration:
    env:
      - name: REGISTRY_AUTH_ENABLED
        value: &apos;true&apos;
      - name: REGISTRY_AUTH_RBAC_ENABLED
        value: &apos;true&apos;
      - name: KEYCLOAK_API_CLIENT_ID
        value: &apos;1fc88f64-2303-42f6-b9df-609edee94efe&apos;
      - name: REGISTRY_UI_AUTH_TYPE
        value: &apos;oidc&apos;
      - name: REGISTRY_AUTH_URL_CONFIGURED
        value: &apos;https://login.microsoftonline.com/64dc69e4-d083-49fc-9569-ebece1dd1408/v2.0&apos;
      - name: REGISTRY_OIDC_UI_CLIENT_ID
        value: &apos;1fc88f64-2303-42f6-b9df-609edee94efe&apos;
      - name: CORS_ALLOWED_ORIGINS
        value: &apos;https://apicurioregistry-psql.registry.router-default.apps.com&apos;
      - name: REGISTRY_OIDC_UI_REDIRECT_URL
        value: &apos;https://apicurioregistry-psql.registry.router-default.apps.com/ui/&apos;
      - name: ROLE_BASED_AUTHZ_ENABLED
        value: &apos;true&apos;
      - name: QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH
        value: &apos;roles&apos;
      - name: registry.ccompat.max-subjects
        value: &apos;2000&apos;
      - name: CLIENT_CREDENTIALS_BASIC_AUTH_ENABLED
        value: &apos;true&apos;
      - name: AUTH_ENABLED
        value: &apos;true&apos;
      - name: CORS_ALLOWED_METHODS
        value: &apos;GET,PUT,POST,PATCH,DELETE,OPTIONS&apos;
      - name: CORS_ALLOWED_HEADERS
        value: &amp;gt;-
          x-registry-name,x-registry-name-encoded,x-registry-description,x-registry-description-encoded,x-registry-version,x-registry-artifactid,x-registry-artifacttype,x-registry-hash-algorithm,x-registry-content-hash,access-control-request-method,access-control-allow-credentials,access-control-allow-origin,access-control-allow-headers,authorization,content-type
    logLevel: DEBUG
    registryLogLevel: DEBUG
    security:
      https:
        disableHttp: true
    persistence: &quot;sql&quot;
    sql:
      dataSource:
        url: &quot;jdbc:postgresql://postgresql:5432/apicurio&quot;
        userName: &quot;psql&quot;
        password: &quot;password&quot;
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Login to your ApiCurio Registry Route HTTPS URL.&lt;/p&gt;

&lt;h1 id=&quot;part-ii-machine-to-machine-communication-client-credentials-grant&quot;&gt;Part II: Machine to machine communication (Client Credentials Grant)&lt;/h1&gt;

&lt;p&gt;Apart from having users accessing the UI, it is quite frequent to have the requirement of doing machine to machine authentication, especially in the context of something like Apache Kafka. To do this, you can configure your setup to use the &lt;strong&gt;Client Credentials Grant&lt;/strong&gt; using &lt;strong&gt;Entra ID&lt;/strong&gt;. This can be achieved with the following steps:&lt;/p&gt;

&lt;h2 id=&quot;1-app-registration&quot;&gt;1. App Registration:&lt;/h2&gt;

&lt;p&gt;First, your app needs to register with the API provider. It gets a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client ID&lt;/code&gt; (like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;username&lt;/code&gt;) and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client secret&lt;/code&gt; (like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;password&lt;/code&gt;). This is a unique key just for your app.&lt;/p&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new app registration&lt;/code&gt; that represents your client application. You can leave everything blank in the creation screen besides the name.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Overview&lt;/code&gt; tab of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new application registration&lt;/code&gt;, Click on the link of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Client credentials&lt;/code&gt;, you will be redirected to a page &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Certificates &amp;amp; secrets&lt;/code&gt;: create a new secret.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/images/guides/EntraID-ClientSecret.png&quot; alt=&quot;Client Secret&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API permissions&lt;/code&gt;, add a new API Permission to your client application, and, inside this option, add the role you want to use. This role should have been created in the last section as an Application role in the Registry server app registration. This is done &lt;strong&gt;so the role is sent within the token&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In your client identifying the application and in the one identifying the Registry server, in the API permissions section, you &lt;strong&gt;must grant admin consent for your directory&lt;/strong&gt;. Note that it may take a few seconds for this to take effect.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/images/guides/EntraID-ApiPermissions.png&quot; alt=&quot;API Permissions&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The registration of Registry server application &lt;strong&gt;must&lt;/strong&gt; have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exposed api&lt;/code&gt; and the box checked for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ID tokens (used for implicit and hybrid flows)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/EntraID-ExposedApi.png&quot; alt=&quot;Exposed API&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/EntraID-IDTokens.png&quot; alt=&quot;ID Tokens&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;2-requesting-a-token&quot;&gt;2. Requesting a Token:&lt;/h2&gt;

&lt;p&gt;When your app wants to access the API, it sends a request to the API’s authentication server. It includes:
    * The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client ID&lt;/code&gt;.
    * The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client secret&lt;/code&gt; (value generated in certificates&amp;amp;secrets)
    * A specific grant type, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;client_credentials.&quot;&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  curl -s -X POST \
    -H &quot;Content-Type: application/x-www-form-urlencoded&quot; \
    -d &apos;client_id=53d7fab9-8cbd-4c3b-84e8-d91a33eb90f8&amp;amp;scope=api://6ac8d309-0dcd-4aeb-b186-4911266038a8/.default&amp;amp;client_secret=Frs8Q~dUD8EwK2-RZ0JNEYS_Lbxlrs01m5F3tbx8&amp;amp;grant_type=client_credentials&apos; \
    &apos;https://login.microsoftonline.com/93a47e43-9ded-45a5-a855-eb462dd234b2/oauth2/v2.0/token&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The token generated has the following attributes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;token_type&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Bearer&quot;&lt;/code&gt;:  This means that the type of token you received is a “Bearer” token. It’s like a special pass that you need to include when you access the API.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expires_in&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3599&lt;/code&gt; : This tells you how long the token is valid, in seconds. Here, it’s 3599 seconds (which is about 1 hour). After that, you’ll need to request a new token.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext_expires_in&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3599&lt;/code&gt;: This is usually the same as expires_in, indicating the extended expiration time. It’s another way to say the token lasts for about 1 hour.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6...&quot;&lt;/code&gt; : This long string is the actual access token. It’s like the key that opens the door to the API. You’ll need to include this token in your requests to the API to access its resources.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;review-token-content&quot;&gt;Review Token Content:&lt;/h4&gt;

&lt;p&gt;Decode your JWT token (you can use tools like &lt;a href=&quot;https://jwt.io/&quot;&gt;jwt.io&lt;/a&gt;) to inspect the claims. Check if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; claims are included.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;img/JWT-DecodeToken.png&quot; alt=&quot;JWT Decode Token&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;3-interacting-with-the-apicurio-registry-api-using-access-token&quot;&gt;3. Interacting with the ApiCurio Registry API using Access Token:&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;response=$(curl -s -X POST -H &quot;Content-Type: application/x-www-form-urlencoded&quot; -d &apos;client_id=53d7fab9-8cbd-4c3b-84e8-d91a33eb90f8&amp;amp;scope=api://6ac8d309-0dcd-4aeb-b186-4911266038a8/.default&amp;amp;client_secret=Frs8Q~dUD8EwK2-RZ0JNEYS_Lbxlrs01m5F3tbx8&amp;amp;grant_type=client_credentials&apos; &apos;https://login.microsoftonline.com/93a47e43-9ded-45a5-a855-eb462dd234b2/oauth2/v2.0/token&apos;  | jq &quot;.access_token&quot;)

ACCESS_TOKEN=$(echo &quot;$response&quot; | sed &apos;s/^&quot;//; s/&quot;$//&apos;)
echo &quot;$ACCESS_TOKEN&quot;

MYREGISTRYURL=https://apicurioregistry-psql.registry.router-default.apps.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;get-list-of-groups&quot;&gt;Get list of groups&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -k -v -X GET -H &quot;Authorization: Bearer $ACCESS_TOKEN&quot; \
  $MYREGISTRYURL/apis/registry/v2/groups
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;creation-of-artifact&quot;&gt;Creation of artifact:&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -k -v -X POST -H &quot;Content-Type: application/json; artifactType=AVRO&quot; \
  -H &quot;X-Registry-ArtifactId: share-price1&quot; \
  -H &quot;Authorization: Bearer $ACCESS_TOKEN&quot; \
  --data &apos;{&quot;type&quot;:&quot;record&quot;,&quot;name&quot;:&quot;price&quot;,&quot;namespace&quot;:&quot;com.example&quot;, \
   &quot;fields&quot;:[{&quot;name&quot;:&quot;symbol&quot;,&quot;type&quot;:&quot;string&quot;},{&quot;name&quot;:&quot;price&quot;,&quot;type&quot;:&quot;string&quot;}]}&apos; \
  $MYREGISTRYURL/apis/registry/v2/groups/my-group/artifacts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;get-artifacts&quot;&gt;Get Artifacts:&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -s -k -v GET -H &quot;Authorization: Bearer $ACCESS_TOKEN&quot; \
  $MYREGISTRYURL/apis/registry/v2/groups/my-group/artifacts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;get-details-of-an-artifact&quot;&gt;Get Details of an Artifact:&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -s -k -v GET -H &quot;Authorization: Bearer $ACCESS_TOKEN&quot; \
  $MYREGISTRYURL/apis/registry/v2/groups/my-group/artifacts/share-price1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;useful-links&quot;&gt;Useful links:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Recording of Hands on Lab: https://drive.google.com/open?id=1DClnumPkmP2NFmOBhkYT0o9K8EAhCbXQ&lt;/li&gt;
  &lt;li&gt;Blog https://www.apicur.io/blog/2023/07/13/registry-azure-ad&lt;/li&gt;
  &lt;li&gt;Apicurio Registry API Definition: https://www.apicur.io/registry/docs/apicurio-registry/2.6.x/assets-attachments/registry-rest-api.htm#tag/Artifacts/operation/deleteArtifactsInGroup&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;p&gt;Last, but not least, we still have a lot of things to do!  As always, you can see the
stuff we’re tracking by viewing the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues&quot;&gt;GitHub issues&lt;/a&gt; for the project.&lt;/p&gt;

&lt;p&gt;If you find bugs or want to request a new feature, that’s a great place to start!&lt;/p&gt;
</description><category term="blog" /><category term="registry" /><category term="security" /><pubDate>Mon, 23 Sep 2024 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2024/09/23/registry-azure-entraid-tutorial</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2024/09/23/registry-azure-entraid-tutorial</guid>
        </item><item>
            <title>Apicurio Registry 3.0 - KafkaSQL Storage</title><dc:creator>Eric Wittmann &amp; Carles Arnal Castello</dc:creator>
            <author></author><description>&lt;p&gt;As promised in the announcement blog about the upcoming release of Apicurio Registry
3.0, this post is a deep dive into the changes we’re making to our Kafka storage
variant.  We’ll get into what the KafkaSQL storage variant is and why you might want 
to use it (pros and cons).  And importantly, we’ll discuss the significant changes
we’ve made to the implementation of this feature for 3.0.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;what-is-the-kafkasql-storage-variant&quot;&gt;What is the KafkaSQL storage variant?&lt;/h1&gt;
&lt;p&gt;Of course, Apicurio Registry requires some form of storage in order to persist the content
and metadata of artifacts and versions.  We currently support two different storage variants:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SQL (Postgresql or SQL Server)&lt;/li&gt;
  &lt;li&gt;Kafka&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While storing data in a SQL database is obvious (and is recommended for most use cases),
it can be useful to instead store registry data in a Kafka topic.  The KafkaSQL storage
variant provides such a feature.&lt;/p&gt;

&lt;h1 id=&quot;why-use-kafka-for-storage&quot;&gt;Why use Kafka for storage?&lt;/h1&gt;
&lt;p&gt;Even though we recommend using a database as the storage variant for Registry, there are
cases where using Kafka instead can make sense.  The most obvious one is that some
organizations have significant expertise managing Kafka clusters, but do not have 
similar expertise with relational databases.  It can be extremely convenient, when
using Apicurio Registry as a Kafka Schema Registry, to use Kafka for registry storage
(since you would obviously already have a working Kafka cluster).&lt;/p&gt;

&lt;h1 id=&quot;how-does-kafkasql-work&quot;&gt;How does KafkaSQL work?&lt;/h1&gt;
&lt;p&gt;The implementation of KafkaSQL is fairly simple:  a persistent Kafka topic is used
to communicate (and save) all data changes.  You can think of this Kafka topic as
a journal containing all changes to the registry.  Whenever a user creates a new
artifact, changes its metadata, adds a new version, or configures a rule, information
about that change is published to the Kafka topic.&lt;/p&gt;

&lt;p&gt;At the same time, Apicurio Registry is a &lt;strong&gt;consumer&lt;/strong&gt; of the same topic.  Whenever
a message is published, Registry will consume it and update an internal H2 SQL
database with that information.  This not only provides persistence (the Kafka
topic must be properly configured) but also allows multiple Registry instances
(typically kubernetes/openshift pods) to each have a full copy of the Registry
state in memory at the same time.&lt;/p&gt;

&lt;p&gt;The flow is therefore this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Change operation is received by Registry &lt;em&gt;(HTTP Request Thread)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;An HTTP request is received representing a registry data change&lt;/li&gt;
  &lt;li&gt;The change is turned into a Kafka message&lt;/li&gt;
  &lt;li&gt;The Kafka message is published to the Kafka topic (the journal)&lt;/li&gt;
  &lt;li&gt;The thread &lt;strong&gt;waits&lt;/strong&gt; for the message to be consumed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Registry Kafka consumer thread consumes messages from the Kafka topic &lt;em&gt;(Consumer Thread)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The message is consumed from the kafka topic&lt;/li&gt;
  &lt;li&gt;The message is converted to an internal SQL statement&lt;/li&gt;
  &lt;li&gt;The SQL statement is applied to the internal H2 database&lt;/li&gt;
  &lt;li&gt;The Request Thread is notified that the message was processed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Waiting request wakes up and responds &lt;em&gt;(HTTP Request Thread continued)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The thread wakes up and responds to the originating HTTP request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once nice thing about this implementation is that all &lt;strong&gt;read&lt;/strong&gt; requests are
handled the same way for both the SQL and KafkaSQL storage variants.  In
both cases we simply query the SQL database for the information (either the
PostgreSQL database for the SQL variant or the internal H2 database for
the KafkaSQL variant).&lt;/p&gt;

&lt;h1 id=&quot;what-are-the-pros-and-cons-of-using-kafkasql&quot;&gt;What are the pros and cons of using KafkaSQL?&lt;/h1&gt;
&lt;h2 id=&quot;pros&quot;&gt;Pros&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;You can store your registry data without provisioning and maintaining a
SQL database.&lt;/li&gt;
  &lt;li&gt;Read requests scale extremely well (linearly) with the number of Registry
pods you provision.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cons&quot;&gt;Cons&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Registry pod startup times get slower and slower as the system ages,
because all messages on the Kafka topic must be consumed on startup.&lt;/li&gt;
  &lt;li&gt;Data backup/restore might be harder for Kafka topics vs. a SQL database.&lt;/li&gt;
  &lt;li&gt;Fixing data corruption issues is much harder because you cannot simply
run some SQL commands to modify data directly in the storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;what-are-the-kafkasql-implementation-changes-in-30&quot;&gt;What are the KafkaSQL implementation changes in 3.0?&lt;/h1&gt;
&lt;p&gt;In Registry version 2.x, our approach to the structure of the Kafka messages
was based on Kafka topic &lt;a href=&quot;https://docs.confluent.io/kafka/design/log_compaction.html&quot;&gt;log compaction&lt;/a&gt;.
Log compaction works well for data streams where more recent messages completely
replace the data of old messages.  In such systems, only the latest message
is important.  Even though that’s not exactly how we have structured our
data model, we attempted to model our data as best we could to fit the log
compaction approach.  This approach worked &lt;strong&gt;to some degree&lt;/strong&gt;, but we had 
a number of edge cases that made it challenging.  It was also an approach
that was difficult to maintain:  every change to the storage layer needed to
be really thought about, since a bug in the way we structured our Kafka
messages could easily result in permanent data loss due to log compaction.&lt;/p&gt;

&lt;h2 id=&quot;no-more-log-compaction&quot;&gt;No more log compaction…&lt;/h2&gt;
&lt;p&gt;For the reasons explained above, we have decided to completely change the 
organization/structure of our Kafka messages.  Instead of trying to organize
our data into a log-compaction compatible structure, we are now creating
the Kafka messages in more of an RPC style.&lt;/p&gt;

&lt;p&gt;Essentially we are creating a one-to-one mapping between (write) methods 
in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegistryStorage&lt;/code&gt; interface and messages on the Kafka journal topic.
This makes maintaining both storage variants extremely easy, but it means
that we are no longer taking advantage of log compaction (since the 
messages are no longer structured in that way).  The major consequence
of this change is that the number of messages on the Kafka topic is 
even greater than before, potentially resulting in even longer pod
startup times (since it continues to be true that on startup a pod
must consume &lt;strong&gt;all&lt;/strong&gt; messages on the topic to fully reproduce the state).&lt;/p&gt;

&lt;p&gt;We already needed a solution to that problem, and now we need it even
more!  Which leads us to…&lt;/p&gt;

&lt;h2 id=&quot;state-snapshotsfor-speed&quot;&gt;State snapshots…for speed!&lt;/h2&gt;

&lt;p&gt;As already discussed in this blog, whenever a new instance of Registry 
is started pointing to an existing journal topic, it has to process every
single message to restore the internal state of the application that is 
used for reads. This has two main effects:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;In the event of data loss in the topic, there is no backup strategy
(by default)&lt;/li&gt;
  &lt;li&gt;It causes the replica start to be fairly slow (depending on the
topic size, of course).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To address these problems, a new feature has been implemented in Registry 3.x:
snapshots of the internal store.&lt;/p&gt;

&lt;p&gt;The way snapshots work in Registry 3 is that, when a (POST) call to the
endpoint &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/admin/snapshots&lt;/code&gt; is made, a new snapshot of the internal
store is created.  This is done by sending a snapshot marker message 
with a snapshot id (randomly generated) to the journal topic.  This
marker message represents a request for a new snapshot to be created.&lt;/p&gt;

&lt;p&gt;After this, the consumer thread (the same thread that is responsible for
processing all the other messages) reads the marker message and
creates a SQL dump of the internal store.  The SQL dump is created in a 
location that can be configured using the following property:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;System property: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.storage.snapshot.location&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Environment variable: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APICURIO_STORAGE_SNAPSHOT_LOCATION&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(&lt;em&gt;Note&lt;/em&gt;: The default value for this property is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Once the SQL dump is created, a message is published to the snapshots Kafka
topic.  The message includes the location of the snapshot and the snapshotId
(as the message key).  The snapshots topic is used to keep track of all the 
created snapshots. This means that, as you might be already thinking, in 
Registry 3 you will need to create and configure two Kafka topics instead of
just one:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Journal&lt;/strong&gt; topic (one message for each write operation)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Snapshots&lt;/strong&gt; topic (one message for each created snapshot)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the snapshot has been created, when a new replica is created (or an
existing replica is restarted), the messages on the snapshots topic are consumed
in order to find the most recent snapshot. If there is a snapshot present, 
the application loads the SQL dump retrieved from the location in the message and 
uses it to restore the internal database. This dramatically improves the application
startup time, since the internal state is restored by issuing a single SQL command
(H2 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RESTORE TO&lt;/code&gt;) instead of processing every single message present in the journal
topic.&lt;/p&gt;

&lt;p&gt;Once the database has been restored, the consumer thread starts consuming the messages
present in the journal topic, discarding all the messages until the corresponding 
snapshot marker message is found. All remaining messages (those messages on the
journal topic that follow the snapshot marker message) are processed as normal and 
dispatched to the SQL store as appropriate.&lt;/p&gt;

&lt;p&gt;With this new feature you can now set up a backup strategy of your Apicurio Registry data
when Kafka is used as the storage option, preventing potential data loss and also
improving replica startup time.&lt;/p&gt;

&lt;p&gt;Finally, here are two graphics comparing the startup times with and without snapshotting.
In the snapshot graph snapshots were created for each 250.000 artifacts:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/snapshotting/application_startup_times.png&quot; alt=&quot;Startup times comparison&quot; /&gt;&lt;/p&gt;
</description><category term="registry" /><category term="3.0" /><category term="kafka" /><category term="storage" /><pubDate>Fri, 21 Jun 2024 16:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2024/06/21/registry-3-kafkasql</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2024/06/21/registry-3-kafkasql</guid>
        </item><item>
            <title>Apicurio Registry 3.0</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;Hey everyone - just dropping a quick blog post to let you know that we have released
Apicurio Registry 3.0.0.M3.  We’re working hard to get this new major release of
Registry stable.  This milestone 3 release is a huge step towards that goal.  Very
soon you can expect a 3.0.0.Final release, but please consider getting your feet 
wet now.  We think 3.0 is ready for evaluation!  (more below the fold)&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;why-a-new-major-version&quot;&gt;Why a new major version?&lt;/h1&gt;
&lt;p&gt;Apicurio Registry 2.x has been around since April of 2021 (!!) and was a marked
improvement over 1.x.  It has served us well, but it was time to address some
core design decisions as well as some accrued technical debt.&lt;/p&gt;

&lt;p&gt;In particular, we wanted the freedom to change the data model (aka the database
schema) and the REST API without needing to maintain compatibility.  That should
really only be done in a major version release.&lt;/p&gt;

&lt;h1 id=&quot;whats-new-in-registry-30&quot;&gt;What’s new in Registry 3.0?&lt;/h1&gt;
&lt;p&gt;We plan to dig into some of these topics in greater detail in a few future blog
posts, but here are some of the highlights of version 3:&lt;/p&gt;

&lt;h2 id=&quot;data-model-changes&quot;&gt;Data model changes&lt;/h2&gt;
&lt;p&gt;There are some significant core data model changes in 3.0.  Some of these changes
include (but are not limited to):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Artifacts have their own metaData&lt;/li&gt;
  &lt;li&gt;Artifacts can be empty (have no versions)&lt;/li&gt;
  &lt;li&gt;Groups can now have labels&lt;/li&gt;
  &lt;li&gt;User can define and maintain custom branches&lt;/li&gt;
  &lt;li&gt;“Latest” is now a branch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;rest-api-changes&quot;&gt;REST API changes&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Full group management, with metaData&lt;/li&gt;
  &lt;li&gt;Streamlined artifact and version creation&lt;/li&gt;
  &lt;li&gt;Search for groups at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/groups&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Search for versions at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/versions&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;A new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dryRun&lt;/code&gt; query param replaces the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/test&lt;/code&gt; endpoint(s)&lt;/li&gt;
  &lt;li&gt;Brand new Branch API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;kafka-storage-variant&quot;&gt;Kafka storage variant&lt;/h2&gt;
&lt;p&gt;Somewhat surprising to us is that a majority of our users have deployed registry
using Kafka as the storage.  We do still recommend a database for persistence,
but of course we recognize the convenience and familiarity of using Kafka instead
for many users.  Rest assured we have no plans to stop supporting it!&lt;/p&gt;

&lt;p&gt;On the contrary - we have completely rewritten the KafkaSQL layer to improve its
stability and to make it easier for us to maintain.  This will be the subject of
a separate blog post soon, but know that the new implementation should be much
easier to keep updated and should result in far fewer unexpected bugs.  Spoiler:
we are also introducing a “snapshotting” feature that will help address the slow
startup times that can result from larger deployments (deployments with lots of
artifacts).&lt;/p&gt;

&lt;h1 id=&quot;try-it-out-now&quot;&gt;Try it out now!&lt;/h1&gt;
&lt;p&gt;We haven’t yet updated the web site with new documentation and getting started 
instructions, and we probably won’t do that until 3.0.0.Final is out.  So until
then, you can easily try it out using docker or podman:&lt;/p&gt;

&lt;h3 id=&quot;pull-the-images&quot;&gt;Pull the images&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker pull quay.io/apicurio/apicurio-registry:3.0.0.M3
docker pull quay.io/apicurio/apicurio-registry-ui:3.0.0.M3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;run-both-apicurio-registry-and-apicurio-registry-ui&quot;&gt;Run both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry-ui&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run -it -p 8080:8080 quay.io/apicurio/apicurio-registry:3.0.0.M3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run -it -p 8888:8080 quay.io/apicurio/apicurio-registry-ui:3.0.0.M3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The UI will be available here:  http://localhost:8888&lt;/p&gt;

&lt;p&gt;And the APIs will be available:  http://localhost:8080/apis&lt;/p&gt;
</description><category term="registry" /><category term="release" /><category term="3.0" /><pubDate>Mon, 17 Jun 2024 16:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2024/06/17/apicurio-registry-3</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2024/06/17/apicurio-registry-3</guid>
        </item><item>
            <title>Apicurio Registry - JSON Schema dereference</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;Apicurio Registry allows to manage artifacts with references as shown in the &lt;a href=&quot;https://www.apicur.io/registry/docs/apicurio-registry/2.5.x/getting-started/assembly-managing-registry-artifacts-api.html&quot;&gt;documentation&lt;/a&gt;.
One of the cool features we’ve added on top of this is the possibility of using a dereference parameter for certain API endpoints that optionally allows you to fetch the full content of an artifact with all the references inlined within the same content.
This is especially useful in certain contexts to reduce the number of HTTP requests in the Kafka Serializers and Deserializers, as you will see in this blog.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;intro&quot;&gt;Intro&lt;/h1&gt;

&lt;p&gt;The code example used for this blog post can be found in the Apicurio Registry &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.6.x/examples/serdes-with-references/src/main/java/io/apicurio/registry/examples/references/JsonSerdeReferencesDereferencedExample.java&quot;&gt;examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the limitations of the JSON Schema format is that you cannot discover the full schema from some data that adheres to the schema. That’s the main reason why, for this serde, the schema being used has to be registered upfront, and that’s what you see
in the code example from line &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.6.x/examples/serdes-with-references/src/main/java/io/apicurio/registry/examples/references/JsonSerdeReferencesDereferencedExample.java#L57-L132&quot;&gt;57 to line 132&lt;/a&gt;.
where the main schema &lt;em&gt;city.json&lt;/em&gt; is registered with all the references in the hierarchy. Along those lines, a full hierarchy of schemas is registered in Apicurio Registry. The hierarchy can be described as follows and represented as a tree:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                          citizen
     |          |                     |           
    city   qualification        citizenIdentifier
     |                                |
 qualification                   qualification
    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you may notice, there are three different files with the same name. This is done on purpose to demonstrate that you can have different schemas with the same name within the same hierarchy.
When they’re registered in the code example, they’re registered under the same group but using different artifact ids, so they can be referenced from their main schema.&lt;/p&gt;

&lt;p&gt;Since an image is way better than trying to describe it, this is the structure in the file system:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/registry-dereference/file-system.png&quot; alt=&quot;Registry (before)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the code example there are
also &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/2.6.x/examples/serdes-with-references/src/main/java/io/apicurio/registry/examples/references/model&quot;&gt;java classes&lt;/a&gt;
that represent each of the objects we have in the structure, so we can use them when producing Kafka messages. There is
one Java class per each JSON Schema file.&lt;/p&gt;

&lt;h1 id=&quot;using-the-rest-api&quot;&gt;Using the REST API&lt;/h1&gt;

&lt;p&gt;Although the example above uses the Apicurio Registry Java client to register the full hierarchy of schemas, it’s also
possible to use the REST API. Note that, when an artifact with references is created, the artifact content itself in the request must be quoted. Here’s a set of Curl commands that delivers this result:&lt;/p&gt;

&lt;p&gt;First we must start creating the leaves, in this case, those are the city qualification and the citizenIdentifier qualification:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//Creates the city qualification
curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts/&apos; \
--header &apos;X-Registry-ArtifactId: cityQualification&apos; \
--header &apos;Content-Type: application/json; artifactType=JSON&apos; \
--data &apos;{
&quot;$id&quot;: &quot;https://example.com/types/city/qualification.json&quot;,
&quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
&quot;title&quot;: &quot;Qualification&quot;,
&quot;type&quot;: &quot;object&quot;,
&quot;properties&quot;: {
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;,
&quot;description&quot;: &quot;The subject&apos;\&apos;&apos;s name&quot;
},
&quot;qualification&quot;: {
&quot;type&quot;: &quot;integer&quot;,
&quot;description&quot;: &quot;The city qualification&quot;,
&quot;minimum&quot;: 10
}
}
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//Creates the citizen identifier qualification
curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts/&apos; \
--header &apos;X-Registry-ArtifactId: identifierQualification&apos; \
--header &apos;Content-Type: application/json; artifactType=JSON&apos; \
--data &apos;{
&quot;$id&quot;: &quot;https://example.com/types/identifier/qualification.json&quot;,
&quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
&quot;title&quot;: &quot;Qualification&quot;,
&quot;type&quot;: &quot;object&quot;,
&quot;properties&quot;: {
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;,
&quot;description&quot;: &quot;The subject&apos;\&apos;&apos;s name&quot;
},
&quot;qualification&quot;: {
&quot;type&quot;: &quot;integer&quot;,
&quot;description&quot;: &quot;The identifier qualification&quot;,
&quot;minimum&quot;: 20
}
}
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the leaves are created, we can go up through the hierarchy, creating the schemas that will be referenced by the citizen. First we create the city schema:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts/&apos; \
--header &apos;X-Registry-ArtifactId: city&apos; \
--header &apos;Content-Type: application/create.extended+json&apos; \
--header &apos;X-Registry-ArtifactType: JSON&apos; \
--data &apos;{
&quot;content&quot;: &quot;{\r\n  \&quot;$id\&quot;: \&quot;https:\/\/example.com\/types\/city\/city.json\&quot;,\r\n  \&quot;$schema\&quot;: \&quot;http:\/\/json-schema.org\/draft-07\/schema#\&quot;,\r\n  \&quot;title\&quot;: \&quot;City\&quot;,\r\n  \&quot;type\&quot;: \&quot;object\&quot;,\r\n  \&quot;properties\&quot;: {\r\n    \&quot;name\&quot;: {\r\n      \&quot;type\&quot;: \&quot;string\&quot;,\r\n      \&quot;description\&quot;: \&quot;The city&apos;\&apos;&apos;s name.\&quot;\r\n    },\r\n    \&quot;zipCode\&quot;: {\r\n      \&quot;type\&quot;: \&quot;integer\&quot;,\r\n      \&quot;description\&quot;: \&quot;The zip code.\&quot;,\r\n      \&quot;minimum\&quot;: 0\r\n    },\r\n    \&quot;qualification\&quot;: {\r\n      \&quot;$ref\&quot;: \&quot;qualification.json\&quot;\r\n    }\r\n  }\r\n}&quot;,
&quot;references&quot;: [
{
&quot;name&quot;: &quot;qualification.json&quot;,
&quot;groupId&quot;: &quot;default&quot;,
&quot;artifactId&quot;: &quot;cityQualification&quot;,
&quot;version&quot;: &quot;1&quot;
}
]
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then the citizen identifier schema:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts/&apos; \
--header &apos;X-Registry-ArtifactId: citizenIdentifier&apos; \
--header &apos;Content-Type: application/create.extended+json&apos; \
--header &apos;X-Registry-ArtifactType: JSON&apos; \
--data &apos;{
&quot;content&quot;: &quot;{\r\n  \&quot;$id\&quot;: \&quot;https:\/\/example.com\/types\/identifier\/citizenIdentifier.json\&quot;,\r\n  \&quot;$schema\&quot;: \&quot;http:\/\/json-schema.org\/draft-07\/schema#\&quot;,\r\n  \&quot;title\&quot;: \&quot;Identifier\&quot;,\r\n  \&quot;type\&quot;: \&quot;object\&quot;,\r\n  \&quot;properties\&quot;: {\r\n    \&quot;identifier\&quot;: {\r\n      \&quot;type\&quot;: \&quot;integer\&quot;,\r\n      \&quot;description\&quot;: \&quot;The citizen identifier.\&quot;,\r\n      \&quot;minimum\&quot;: 0\r\n    },\r\n    \&quot;qualification\&quot;: {\r\n      \&quot;$ref\&quot;: \&quot;qualification.json\&quot;\r\n    }\r\n  }\r\n}&quot;,
&quot;references&quot;: [
{
&quot;name&quot;: &quot;qualification.json&quot;,
&quot;groupId&quot;: &quot;default&quot;,
&quot;artifactId&quot;: &quot;identifierQualification&quot;,
&quot;version&quot;: &quot;1&quot;
}
]
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The citizen qualification schema:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts/&apos; \
--header &apos;X-Registry-ArtifactId: qualification&apos; \
--header &apos;Content-Type: application/json; artifactType=JSON&apos; \
--data &apos;{
&quot;$id&quot;: &quot;https://example.com/qualification.json&quot;,
&quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
&quot;title&quot;: &quot;Qualification&quot;,
&quot;type&quot;: &quot;object&quot;,
&quot;properties&quot;: {
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;,
&quot;description&quot;: &quot;The subject&apos;\&apos;&apos;s name&quot;
},
&quot;qualification&quot;: {
&quot;type&quot;: &quot;integer&quot;,
&quot;description&quot;: &quot;The qualification.&quot;,
&quot;minimum&quot;: 0
}
}
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And, finally, the citizen schema itself with references to qualification, city and identifier&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts/&apos; \
--header &apos;X-Registry-ArtifactId: citizen&apos; \
--header &apos;Content-Type: application/create.extended+json&apos; \
--header &apos;X-Registry-ArtifactType: JSON&apos; \
--data &apos;{
&quot;content&quot;: &quot;{\r\n \&quot;$id\&quot;: \&quot;https:\/\/example.com\/citizen.json\&quot;,\r\n \&quot;$schema\&quot;: \&quot;http:\/\/json-schema.org\/draft-07\/schema#\&quot;,\r\n \&quot;title\&quot;: \&quot;Citizen\&quot;,\r\n \&quot;type\&quot;: \&quot;object\&quot;,\r\n \&quot;properties\&quot;:{\r\n\&quot;firstName\&quot;: {\r\n \&quot;type\&quot;: \&quot;string\&quot;,\r\n \&quot;description\&quot;: \&quot;The citizen&apos;\&apos;&apos;s first name.\&quot;\r\n },\r\n\&quot;lastName\&quot;: {\r\n \&quot;type\&quot;: \&quot;string\&quot;,\r\n \&quot;description\&quot;: \&quot;The citizen&apos;\&apos;&apos;s last name.\&quot;\r\n },\r\n \&quot;age\&quot;: {\r\n\&quot;description\&quot;: \&quot;Age in years which must be equal to or greater than zero.\&quot;,\r\n \&quot;type\&quot;: \&quot;integer\&quot;,\r\n\&quot;minimum\&quot;: 0\r\n },\r\n \&quot;city\&quot;: {\r\n \&quot;$ref\&quot;: \&quot;types\/city\/city.json\&quot;\r\n },\r\n \&quot;identifier\&quot;: {\r\n\&quot;$ref\&quot;: \&quot;types\/identifier\/citizenIdentifier.json\&quot;\r\n },\r\n \&quot;qualifications\&quot;: {\r\n \&quot;type\&quot;: \&quot;array\&quot;,\r\n\&quot;items\&quot;: {\r\n \&quot;$ref\&quot;: \&quot;qualification.json\&quot;\r\n }\r\n }\r\n },\r\n \&quot;required\&quot;: [\r\n \&quot;city\&quot;\r\n  ]\r\n}&quot;,
&quot;references&quot;: [
{
&quot;name&quot;: &quot;qualification.json&quot;,
&quot;groupId&quot;: &quot;default&quot;,
&quot;artifactId&quot;: &quot;qualification&quot;,
&quot;version&quot;: &quot;1&quot;
},
{
&quot;name&quot;: &quot;types/city/city.json&quot;,
&quot;groupId&quot;: &quot;default&quot;,
&quot;artifactId&quot;: &quot;city&quot;,
&quot;version&quot;: &quot;1&quot;
},
{
&quot;name&quot;: &quot;types/identifier/citizenIdentifier.json&quot;,
&quot;groupId&quot;: &quot;default&quot;,
&quot;artifactId&quot;: &quot;citizenIdentifier&quot;,
&quot;version&quot;: &quot;1&quot;
},
{
&quot;name&quot;: &quot;sample.address.json&quot;,
&quot;groupId&quot;: &quot;default&quot;,
&quot;artifactId&quot;: &quot;address&quot;,
&quot;version&quot;: &quot;1&quot;
}
]
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;json-schema-dereference&quot;&gt;JSON Schema dereference&lt;/h1&gt;

&lt;p&gt;By default, the endpoints that return the content of a particular artifact fetch just the main content, and then you must get the links to the references and,
one by one, do a full scan of the hierarchy to fetch any other sub-schema needed. For instance, the content of the &lt;em&gt;city.json&lt;/em&gt; schema is the following:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;types/city/city.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;types/identifier/citizenIdentifier.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualifications&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;array&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;items&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;qualification.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without dereferencing in order to be able to produce Kafka messages to adhere to this schema, that is already registered in Apicurio Registry, we would need to fetch all the nested references with multiple HTTP requests and then parse all of them while, when dereferencing is used, this is what you get:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen identifier.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualification&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Qualification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualification&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The identifier qualification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The subject&apos;s name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualifications&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;array&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;items&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Qualification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualification&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The qualification.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The subject&apos;s name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zipCode&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The zip code.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualification&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Qualification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualification&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The city qualification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The subject&apos;s name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The city&apos;s name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see, that’s the content of the full hierarchy, not just the schema of the &lt;em&gt;citizen&lt;/em&gt; element alone. There are two endpoints where you can get the content dereferenced, with the following curl commands:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl --location &apos;http://localhost:8080/apis/registry/v2/ids/globalIds/6?dereference=true&apos;
curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts/JsonSerdeReferencesExample?dereference=true&apos;
curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts/JsonSerdeReferencesExample/versions/1?dereference=true&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;
&lt;p&gt;Referencing options
===&lt;/p&gt;

&lt;p&gt;Note that, even though referencing a whole schema definition is the most common use case, it’s also possible to reference a single object definition defined in a json file with multiple schemas. For example, we might have the following schema definition:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-{json}&quot;&gt;{
  &quot;$id&quot;: &quot;https://example.com/types/all-types.json&quot;,
  &quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
  &quot;definitions&quot;: {
    &quot;City&quot;: {
      &quot;title&quot;: &quot;City&quot;,
      &quot;type&quot;: &quot;object&quot;,
      &quot;properties&quot;: {
        &quot;name&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;description&quot;: &quot;The city&apos;s name.&quot;
        },
        &quot;zipCode&quot;: {
          &quot;type&quot;: &quot;integer&quot;,
          &quot;description&quot;: &quot;The zip code.&quot;,
          &quot;minimum&quot;: 0
        }
      }
    },
    &quot;Identifier&quot;: {
      &quot;title&quot;: &quot;Identifier&quot;,
      &quot;type&quot;: &quot;object&quot;,
      &quot;properties&quot;: {
        &quot;identifier&quot;: {
          &quot;type&quot;: &quot;integer&quot;,
          &quot;description&quot;: &quot;The citizen identifier.&quot;,
          &quot;minimum&quot;: 0
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With definitions for both an identifier and the city objects. Then we might decide that we want to have a separate schema that points to an entire definition, like the one below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;$id&quot;: &quot;https://example.com/citizen.json&quot;,
  &quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
  &quot;title&quot;: &quot;Citizen&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;properties&quot;: {
    &quot;firstName&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;The citizen&apos;s first name.&quot;
    },
    &quot;lastName&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;The citizen&apos;s last name.&quot;
    },
    &quot;age&quot;: {
      &quot;description&quot;: &quot;Age in years which must be equal to or greater than zero.&quot;,
      &quot;type&quot;: &quot;integer&quot;,
      &quot;minimum&quot;: 0
    },
    &quot;city&quot;: {
      &quot;$ref&quot;: &quot;types/all-types.json#/definitions/City&quot;
    },
    &quot;identifier&quot;: {
      &quot;$ref&quot;: &quot;types/all-types.json#/definitions/Identifier&quot;
    }
  },
  &quot;required&quot;: [
    &quot;city&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or we might decide to just reference a single property inside a schema definition, like the schema below does:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;$id&quot;: &quot;https://example.com/citizen.json&quot;,
  &quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
  &quot;title&quot;: &quot;Citizen&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;properties&quot;: {
    &quot;firstName&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;The citizen&apos;s first name.&quot;
    },
    &quot;lastName&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;The citizen&apos;s last name.&quot;
    },
    &quot;age&quot;: {
      &quot;description&quot;: &quot;Age in years which must be equal to or greater than zero.&quot;,
      &quot;type&quot;: &quot;integer&quot;,
      &quot;minimum&quot;: 0
    },
    &quot;city&quot;: {
      &quot;$ref&quot;: &quot;types/all-types.json#/definitions/City/properties/name&quot;
    },
    &quot;identifier&quot;: {
      &quot;$ref&quot;: &quot;types/all-types.json#/definitions/Identifier&quot;
    }
  },
  &quot;required&quot;: [
    &quot;city&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The three options work exactly the same way when it comes to registering those schemas using the REST API as if we were registering a schema that references another. First we must register the schema that will be referenced, in this case, the one with the definitions:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;--location&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;http://localhost:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/apis/registry/v&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/groups/default/artifacts/&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;--header&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;X-Registry-ArtifactId:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;all-types&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;--header&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;Content-Type:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;application/json;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;artifactType=JSON&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;--data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/types/all-types.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;definitions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The city&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&apos;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;s name.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zipCode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The zip code.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city/qualification.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen identifier.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qualification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;identifier/qualification.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once this schema has been registered, we can now decide to register two separate schemas, the first one referencing the entire city object:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts&apos; \
--header &apos;X-Registry-ArtifactId: citizen-object-level&apos; \
--header &apos;Content-Type: application/create.extended+json&apos; \
--header &apos;X-Registry-ArtifactType: JSON&apos; \
--data &apos;{
    &quot;content&quot;: &quot;{\r\n  \&quot;$id\&quot;: \&quot;https:\/\/example.com\/citizen.json\&quot;,\r\n  \&quot;$schema\&quot;: \&quot;http:\/\/json-schema.org\/draft-07\/schema#\&quot;,\r\n  \&quot;title\&quot;: \&quot;Citizen\&quot;,\r\n  \&quot;type\&quot;: \&quot;object\&quot;,\r\n  \&quot;properties\&quot;: {\r\n    \&quot;firstName\&quot;: {\r\n      \&quot;type\&quot;: \&quot;string\&quot;,\r\n      \&quot;description\&quot;: \&quot;The citizen&apos;\&apos;&apos;s first name.\&quot;\r\n    },\r\n    \&quot;lastName\&quot;: {\r\n      \&quot;type\&quot;: \&quot;string\&quot;,\r\n      \&quot;description\&quot;: \&quot;The citizen&apos;\&apos;&apos;s last name.\&quot;\r\n    },\r\n    \&quot;age\&quot;: {\r\n      \&quot;description\&quot;: \&quot;Age in years which must be equal to or greater than zero.\&quot;,\r\n      \&quot;type\&quot;: \&quot;integer\&quot;,\r\n      \&quot;minimum\&quot;: 0\r\n    },\r\n    \&quot;city\&quot;: {\r\n      \&quot;$ref\&quot;: \&quot;types\/all-types.json#\/definitions\/City\&quot;\r\n    },\r\n    \&quot;identifier\&quot;: {\r\n      \&quot;$ref\&quot;: \&quot;types\/all-types.json#\/definitions\/Identifier\&quot;\r\n    }\r\n  },\r\n  \&quot;required\&quot;: [\r\n    \&quot;city\&quot;\r\n  ]\r\n}&quot;,
    &quot;references&quot;: [
        {
            &quot;name&quot;: &quot;types/all-types.json#/definitions/City&quot;,
            &quot;groupId&quot;: &quot;default&quot;,
            &quot;artifactId&quot;: &quot;all-types&quot;,
            &quot;version&quot;: &quot;1&quot;
        },
        {
            &quot;name&quot;: &quot;types/all-types.json#/definitions/Identifier&quot;,
            &quot;groupId&quot;: &quot;default&quot;,
            &quot;artifactId&quot;: &quot;all-types&quot;,
            &quot;version&quot;: &quot;1&quot;
        }
    ]
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then register the second schema, where just the city name property is referenced:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl --location &apos;http://localhost:8080/apis/registry/v2/groups/default/artifacts&apos; \
--header &apos;X-Registry-ArtifactId: citizen-property-level&apos; \
--header &apos;Content-Type: application/create.extended+json&apos; \
--header &apos;X-Registry-ArtifactType: JSON&apos; \
--data &apos;{
    &quot;content&quot;: &quot;{\r\n  \&quot;$id\&quot;: \&quot;https:\/\/example.com\/citizen.json\&quot;,\r\n  \&quot;$schema\&quot;: \&quot;http:\/\/json-schema.org\/draft-07\/schema#\&quot;,\r\n  \&quot;title\&quot;: \&quot;Citizen\&quot;,\r\n  \&quot;type\&quot;: \&quot;object\&quot;,\r\n  \&quot;properties\&quot;: {\r\n    \&quot;firstName\&quot;: {\r\n      \&quot;type\&quot;: \&quot;string\&quot;,\r\n      \&quot;description\&quot;: \&quot;The citizen&apos;\&apos;&apos;s first name.\&quot;\r\n    },\r\n    \&quot;lastName\&quot;: {\r\n      \&quot;type\&quot;: \&quot;string\&quot;,\r\n      \&quot;description\&quot;: \&quot;The citizen&apos;\&apos;&apos;s last name.\&quot;\r\n    },\r\n    \&quot;age\&quot;: {\r\n      \&quot;description\&quot;: \&quot;Age in years which must be equal to or greater than zero.\&quot;,\r\n      \&quot;type\&quot;: \&quot;integer\&quot;,\r\n      \&quot;minimum\&quot;: 0\r\n    },\r\n    \&quot;city\&quot;: {\r\n      \&quot;$ref\&quot;: \&quot;types\/all-types.json#\/definitions\/City\/properties\/name\&quot;\r\n    },\r\n    \&quot;identifier\&quot;: {\r\n      \&quot;$ref\&quot;: \&quot;types\/all-types.json#\/definitions\/Identifier\&quot;\r\n    }\r\n  },\r\n  \&quot;required\&quot;: [\r\n    \&quot;city\&quot;\r\n  ]\r\n}&quot;,
    &quot;references&quot;: [
        {
            &quot;name&quot;: &quot;types/all-types.json#/definitions/City/properties/name&quot;,
            &quot;groupId&quot;: &quot;default&quot;,
            &quot;artifactId&quot;: &quot;all-types&quot;,
            &quot;version&quot;: &quot;1&quot;
        },
        {
            &quot;name&quot;: &quot;types/all-types.json#/definitions/Identifier&quot;,
            &quot;groupId&quot;: &quot;default&quot;,
            &quot;artifactId&quot;: &quot;all-types&quot;,
            &quot;version&quot;: &quot;1&quot;
        }
    ]
}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The most relevant part here is the name of the reference (apart from the content itself, of course). The name of the reference is what’s going to determine the part of the schema to be used to fill the reference in the main content. In the first case, in the object scenario, the reference name is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;types/all-types.json#/definitions/City&lt;/code&gt; creating a reference to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all-types.json&lt;/code&gt; file in general, and to the city definition in particular. In the second scenario, the property path is used for the reference name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;types/all-types.json#/definitions/City/properties/name&lt;/code&gt;, creating a reference to the all-types.json file in general, and to the city name in particular.&lt;/p&gt;

&lt;p&gt;This is especially important for dereferencing content, since the final result will be different for each schema. In the object scenario, the city object will be inlined within the main schema as follows:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Citizen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s first name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen&apos;s last name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;identifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The citizen identifier.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zipCode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The zip code.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The city&apos;s name.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Age in years which must be equal to or greater than zero.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/citizen.json&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whereas in the second scenario, the property one, just the city name will be inlined:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;,
    &quot;title&quot;: &quot;Citizen&quot;,
    &quot;type&quot;: &quot;object&quot;,
    &quot;properties&quot;: {
        &quot;firstName&quot;: {
            &quot;description&quot;: &quot;The citizen&apos;s first name.&quot;,
            &quot;type&quot;: &quot;string&quot;
        },
        &quot;lastName&quot;: {
            &quot;description&quot;: &quot;The citizen&apos;s last name.&quot;,
            &quot;type&quot;: &quot;string&quot;
        },
        &quot;identifier&quot;: {
            &quot;title&quot;: &quot;Identifier&quot;,
            &quot;type&quot;: &quot;object&quot;,
            &quot;properties&quot;: {
                &quot;identifier&quot;: {
                    &quot;description&quot;: &quot;The citizen identifier.&quot;,
                    &quot;type&quot;: &quot;integer&quot;,
                    &quot;minimum&quot;: 0
                }
            }
        },
        &quot;city&quot;: {
            &quot;description&quot;: &quot;The city&apos;s name.&quot;,
            &quot;type&quot;: &quot;string&quot;
        },
        &quot;age&quot;: {
            &quot;description&quot;: &quot;Age in years which must be equal to or greater than zero.&quot;,
            &quot;type&quot;: &quot;integer&quot;,
            &quot;minimum&quot;: 0
        }
    },
    &quot;required&quot;: [
        &quot;city&quot;
    ],
    &quot;$id&quot;: &quot;https://example.com/citizen.json&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;json-schema-kafka-serde-dereference&quot;&gt;JSON Schema Kafka Serde dereference&lt;/h1&gt;

&lt;p&gt;Now, in order to make good use of all of this, we can use a Kafka producer. For this example, in the Kafka Producer there is one extremely important property we must set, the schema resolver configuration option &lt;em&gt;SERIALIZER_DEREFERENCE_SCHEMA&lt;/em&gt;. This is the option that will instruct the Schema Resolver to use the dereference parameter in the globalIds endpoint above (since that is the endpoint used by default to resolve the schema).&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Configure kafka settings&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;BOOTSTRAP_SERVERS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SERVERS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;CLIENT_ID_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Producer-&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACKS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;all&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;KEY_SERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringSerializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;VALUE_SERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;JsonSchemaKafkaSerializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ARTIFACT_RESOLVER_STRATEGY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleTopicIdStrategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EXPLICIT_ARTIFACT_GROUP_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//This is the most important configuration for this particular example.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SchemaResolverConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;SERIALIZER_DEREFERENCE_SCHEMA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;// Create the Kafka producer&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaProducer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this producer we can then send messages to Kafka that adhere to the &lt;em&gt;citizen&lt;/em&gt; schema as follows:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;City&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;City&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;New York&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setQualification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CityQualification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;city_qualification&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;CitizenIdentifier&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CitizenIdentifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;123456789&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setIdentifierQualification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IdentifierQualification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test_subject&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Carles&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Arnal&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Qualification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Qualification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Qualification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)));&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Send/produce the message on the Kafka Producer&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;producedRecord&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topicName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SUBJECT_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producedRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And consume them with a consumer that is created as follows, instructing the deserializer to use the schema dereferenced with the following configuration property &lt;em&gt;DESERIALIZER_DEREFERENCE_SCHEMA&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;BOOTSTRAP_SERVERS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SERVERS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GROUP_ID_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Consumer-&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ENABLE_AUTO_COMMIT_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTO_COMMIT_INTERVAL_MS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTO_OFFSET_RESET_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;earliest&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;KEY_DESERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringDeserializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;VALUE_DESERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;JsonSchemaKafkaDeserializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ARTIFACT_RESOLVER_STRATEGY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleTopicIdStrategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EXPLICIT_ARTIFACT_GROUP_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;VALIDATION_ENABLED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//This is the most important configuration for this example, that instructs the deserializer to fetch the dereferenced schema.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SchemaResolverConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DESERIALIZER_DEREFERENCE_SCHEMA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Configure Service Registry location&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// No other configuration needed for the deserializer, because the globalId of the schema&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// the deserializer should use is sent as part of the payload.  So the deserializer simply&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// extracts that globalId and uses it to look up the Schema from the registry.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Create the Kafka Consumer&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;KafkaConsumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaConsumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then consume the messages with the consumer configured above like this:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;messageCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsumerRecords&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ofSeconds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;messageCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Do nothing - no messages waiting.&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;No messages waiting...&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Citizen&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Consumed a message: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; @ &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getCity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of doing an HTTP request to get the &lt;em&gt;citizen schema&lt;/em&gt;, another request to fetch the pointers to the references, and, in this case (this process grows with the hierarchy size), another nine requests to discover the full hierarchy, a single HTTP request gets the full content of the hierarchy and then uses it to produce/consume messages.&lt;/p&gt;

</description><category term="registry" /><category term="openapi" /><category term="jsonchema" /><category term="asyncapi" /><category term="avro" /><pubDate>Tue, 14 May 2024 16:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2024/05/14/registry-dereference</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2024/05/14/registry-dereference</guid>
        </item><item>
            <title>Apicurio Studio - Now with OpenAPI Vendor Extensions</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;The Apicurio Studio OpenAPI editor has been around for a long time, and over
time support for vendor extensions has evolved somewhat.  It started with:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;You can use the Source tab, good luck!&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Eventually we added a Vendor Extension section in various places, which made 
things a bit better.  However, the UI was rather basic - just a list of the
extensions with values displayed as raw JSON.  Editing the values was also basic -
simply a source editor for the raw JSON value of the extension.&lt;/p&gt;

&lt;p&gt;We’ve made another incremental improvement in this area, which is to use form
generation (via &lt;a href=&quot;https://formly.dev/&quot;&gt;Formly&lt;/a&gt;) to generate a UI form used when editing the value of a
vendor extension!&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;what-are-openapi-vendor-extensions&quot;&gt;What are OpenAPI Vendor Extensions?&lt;/h1&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.openapis.org/&quot;&gt;OpenAPI specification&lt;/a&gt; defines a set of semantics for describing a REST API.
Endpoints, data types, operations, inputs and outputs, etc are 
all concepts defined by the specification.  These first order concepts are often all
you need to fully define the shape of your API.&lt;/p&gt;

&lt;p&gt;However, there may be aspects of your API that you want to define which go beyond the
common semantics defined by the OpenAPI specification.
In these cases, OpenAPI defines the concept of a Vendor Extension, which are open 
ended property names that must begin with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So imagine you want to define some API Management limits on your operations and you
have a REST API platform that can inspect the OpenAPI to find the configured limits 
for any given operation.  You might set rate limits for an operation directly in the
OpenAPI like this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;openapi&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;3.1.0&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Example API&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;/widgets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Get a list of widgets.&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;200&apos;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;#/components/responses/WidgetListResponse&apos;&lt;/span&gt;
            &lt;span class=&quot;err&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;500&apos;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;#/components/responses/ServerError&apos;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;listWidgets&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;List widgets&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;x-apimanagement-rateLimit&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;rate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;period&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;second&apos;&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;per&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Everything in the above example is standard OpenAPI semantics except for the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-apimanagement-rateLimit&lt;/code&gt; API Management property, which is a vendor extension.
This example might indicate that the platform should apply a rate limit of 100
requests per second per user.  Neat!&lt;/p&gt;

&lt;h1 id=&quot;what-inspired-this-feature&quot;&gt;What inspired this feature?&lt;/h1&gt;
&lt;p&gt;Apicurio Studio will be included as a component of a new Red Hat product called
&lt;a href=&quot;https://developers.redhat.com/products/red-hat-connectivity-link/overview&quot;&gt;Connectivity Link&lt;/a&gt;.
This new product has a lot of functionality in the network connectivity
and API management spaces, and one of the things it supports is enabling and
configuring its various features by adding some vendor extensions to the OpenAPI
document.&lt;/p&gt;

&lt;p&gt;Another component of Connectivity Link is &lt;a href=&quot;https://kuadrant.io/&quot;&gt;Kuadrant&lt;/a&gt;, 
which is an open source project that brings policy based application connectivity features 
to Kubernetes.  That project defines some 
&lt;a href=&quot;https://docs.kuadrant.io/0.7.0/kuadrantctl/doc/openapi-kuadrant-extensions/&quot;&gt;OpenAPI kuadrant extensions&lt;/a&gt; 
that you can use to configure things like routes and rate limits.&lt;/p&gt;

&lt;p&gt;We thought it would be nice if users could use a form to enter the Kuadrant 
vendor extension values, rather than having to copy/paste the correct JSON from
the Kuadrant documentation into the editor.&lt;/p&gt;

&lt;h1 id=&quot;wait-is-this-only-for-kuadrant&quot;&gt;Wait, is this ONLY for Kuadrant?&lt;/h1&gt;
&lt;p&gt;Of course, we could have simply added UI support for Kuadrant vendor extensions.  But
that would only add support for this one specific type of extension.  What we really
wanted was a way to easily configure Studio to support a wide range of vendor extensions,
without having to write code and release new versions.  So what we’ve done is implement
a dynamic form generation approach that allows you to configure Studio with a list of
vendor extensions at deploy time.  More details on that later.&lt;/p&gt;

&lt;h1 id=&quot;ok-but-show-me-what-it-looks-like&quot;&gt;OK but show me what it looks like!&lt;/h1&gt;
&lt;p&gt;Right, let’s show some stuff!  Here’s what things used to look like in the editor:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/vendor-extensions/before.png&quot; alt=&quot;Studio (before)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Notice that you have to provide the full vendor extension property name as well as 
the value in raw JSON format.  It’s not great.&lt;/p&gt;

&lt;p&gt;Now here’s what it looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/vendor-extensions/after.png&quot; alt=&quot;Studio (after)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Cool!&lt;/p&gt;

&lt;h1 id=&quot;how-do-i-use-this-feature&quot;&gt;How do I use this feature?&lt;/h1&gt;
&lt;p&gt;Unfortunately the feature isn’t magic, which means there is some configuration
needed.  Let’s dig into that in this final section.&lt;/p&gt;

&lt;h2 id=&quot;creating-a-vendor-extensions-configuration&quot;&gt;Creating a vendor-extensions configuration&lt;/h2&gt;
&lt;p&gt;What you need to do is create a JSON file with the correct
information (for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vendor-extensions.json&lt;/code&gt;).  You’ll need to create this
file and then eventually make it available to the Studio backend application
(see the next section).&lt;/p&gt;

&lt;p&gt;The vendor extension configuration file is just a JSON file with an array of 
supported vendor extensions.  Each vendor extension item in the array should 
have the following properties:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; : the name of the vendor extension property, which must begin with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schema&lt;/code&gt; : a JSON schema that describes the format of the vendor extension value&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;model&lt;/code&gt; : a JSON object that is the default values for the vendor extension value&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;components&lt;/code&gt; : an (optional) array of OpenAPI component names indicating valid placement of the vendor extension&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an example of this file (the one used in this blog post):&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;x-address&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Enter address&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;street&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Street&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;City&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;State&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pattern&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^[A-Z]{2}$&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zip&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Zip&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pattern&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^[0-9]{5}$&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;street&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;zip&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;street&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zip&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;components&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;document&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above example has only a single vendor extension defined, but you can specify
as many as you like.&lt;/p&gt;

&lt;p&gt;Note that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;components&lt;/code&gt; property allows you to specify where in the OpenAPI document
the vendor extension is valid.  In the above example it will only appear when
adding a vendor extension to the root of the document.  Other allowed values for this
are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;schema&lt;/li&gt;
  &lt;li&gt;response&lt;/li&gt;
  &lt;li&gt;parameter&lt;/li&gt;
  &lt;li&gt;header&lt;/li&gt;
  &lt;li&gt;requestBody&lt;/li&gt;
  &lt;li&gt;securityScheme&lt;/li&gt;
  &lt;li&gt;pathItem&lt;/li&gt;
  &lt;li&gt;operation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get the idea!&lt;/p&gt;

&lt;p&gt;When defining your JSON Schema for a vendor extension, we recommend making sure you 
have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt; defined for each property.  This will allow the form generator to create
appropriate labels in the UI.  See the Formly documentation for more information.&lt;/p&gt;

&lt;h2 id=&quot;telling-studio-where-the-configuration-is&quot;&gt;Telling Studio where the configuration is&lt;/h2&gt;
&lt;p&gt;Now that you have a vendor-extensions configuration file created, you need to tell 
Studio where it lives.  You can do this by setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APICURIO_UI_OPENAPI_VENDOR-EXTENSIONS_URL=&lt;/code&gt;
environment variable when starting up Studio.  For example:&lt;/p&gt;

&lt;p&gt;docker run -it -p 8080:8080 –env APICURIO_UI_OPENAPI_VENDOR-EXTENSIONS_URL=”https://raw.githubusercontent.com/Apicurio/apicurio-studio/0f81ea54bcf780daff59f5ea78c7990c6a4221f9/app/src/main/resources/extensions/address.json” apicurio/apicurio-studio:latest-snapshot&lt;/p&gt;

&lt;p&gt;Any valid URL that can be resolved by the application should work.  Just be careful
of using local file paths when running a docker image - if you want that to work then
you’ll obviously need to mount the file into the container.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Thanks for reading.  This feature is still in Beta and can do with some feedback and
iteration.  If you feel so inspired, please give it a try and let us know how it can
be improved (oh let me count the ways).&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;
</description><category term="studio" /><category term="openapi" /><pubDate>Fri, 10 May 2024 16:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2024/05/10/studio-vendor-extensions</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2024/05/10/studio-vendor-extensions</guid>
        </item><item>
            <title>Introducing VS Code extension for Apicurio Registry</title><dc:creator>Jakub Senko</dc:creator>
            <author>jsenko@redhat.com</author><description>&lt;p&gt;Are you looking for a tool that will make your work with Apicurio Registry easier? If you have Visual Studio Code installed, there is an extension that allows you to interact with Apicurio Registry directly from your IDE. This project was contributed to the Apicurio community by &lt;a href=&quot;https://github.com/jetmartin&quot;&gt;
Jean-Etienne Martin&lt;/a&gt;. 🎉&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;installation&quot;&gt;Installation&lt;/h1&gt;

&lt;p&gt;Installation is easy, because the extension is &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=apicurio.apicurio-registry-explorer&quot;&gt;published to the Visual Studio Marketplace&lt;/a&gt;. In Visual Studio Code, go to the quick open dialog with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl+P&lt;/code&gt;, and execute the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ext install apicurio.apicurio-registry-explorer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The extension will be installed within a few seconds. If you are using a non-official distribution of VS Code, such as &lt;a href=&quot;https://vscodium.com/&quot;&gt;VSCodium&lt;/a&gt;, the extension is also &lt;a href=&quot;https://open-vsx.org/extension/Apicurio/apicurio-registry-explorer&quot;&gt;available on the Open VSX Marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we need to connect to an Apicurio Registry instance. We’ll use docker to start a quick ephemeral Apicurio Registry container at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost:8080&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run -p 8080:8080 quay.io/apicurio/apicurio-registry-mem:latest-release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open the settings panel (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl+,&lt;/code&gt;), go to &lt;em&gt;Extensions&lt;/em&gt; &amp;gt; &lt;em&gt;Apicurio&lt;/em&gt;, and fill in the connection details:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/registry-vscode-extension/vscode-settings.png&quot; alt=&quot;VSCode Settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you have configured the connection settings correctly, and started with an empty Apicurio Registry, the extension panel should look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/registry-vscode-extension/extension-content-empty.png&quot; alt=&quot;Apicurio Registry Explorer extension with empty content&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But don’t worry, it won’t stay empty for much longer!&lt;/p&gt;

&lt;h1 id=&quot;structure-of-content-in-apicurio-registry&quot;&gt;Structure of content in Apicurio Registry&lt;/h1&gt;

&lt;p&gt;Before we use the extension to add new content to Apicurio Registry, let’s do a quick review of the organization of data in the Registry. This will help us better understand how to use the extension.&lt;/p&gt;

&lt;p&gt;Main organizational unit in Apicurio Registry is called an &lt;strong&gt;artifact&lt;/strong&gt;, which represents a piece content (e.g. an Avro schema) that can change over time. Each change to the artifact content results in a new &lt;strong&gt;version&lt;/strong&gt;, so an artifact can be thought of as a sequence of versions. Version represents a specific piece of content that can’t be further modified, so the evolution of a schema happens at the artifact level. Artifacts are further organized into &lt;strong&gt;groups&lt;/strong&gt;. Each artifact must belong to one specific group (the default group is unsurprisingly named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt;), and the groups can’t be nested.&lt;/p&gt;

&lt;p&gt;As a result of this hierarchical organizational structure, each static piece of content can be identified by a &lt;strong&gt;group ID:artifact ID:version&lt;/strong&gt; triple. There are other identifiers available, but the &lt;strong&gt;GAV&lt;/strong&gt; can be considered a primary identifier.&lt;/p&gt;

&lt;p&gt;In addition to the content itself, Apicurio Registry also stores &lt;strong&gt;metadata&lt;/strong&gt; for an artifact and each artifact version. The metadata contain information such as when and by whom was the content created or modified, secondary identifiers, and user-editable data such as &lt;strong&gt;artifact name&lt;/strong&gt;, description, or a map of &lt;strong&gt;properties&lt;/strong&gt;. Some of the metadata (e.g. the properties), are shared between an artifact and its &lt;strong&gt;latest version&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One of the interesting pieces of artifact version metadata is its &lt;strong&gt;state&lt;/strong&gt;, which can be one of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENABLED&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DEPRECATED&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DISABLED&lt;/code&gt;. This value affects Apicurio Registry behavior when retrieving content. A &lt;strong&gt;disabled version&lt;/strong&gt; behaves as if it were not present when a user attempts to retrieve it. Moreover, when reading artifact content (i.e. the latest version of an artifact), and the latest version is disabled, the previous version is returned instead. However, a disabled version still allows users to change its state, and will show in search results.&lt;/p&gt;

&lt;h1 id=&quot;adding-new-content-to-apicurio-registry&quot;&gt;Adding new content to Apicurio Registry&lt;/h1&gt;

&lt;p&gt;Let’s add a few Avro schemas to the Registry, so we can find out how the extension displays Apicurio Registry content. You can use the following two example Avro schemas if you don’t have any others on hand:&lt;/p&gt;

&lt;p&gt;The first version:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;namespace&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;example.avro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;record&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fields&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;favorite_number&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;int&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;favorite_color&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and a second version with updated content:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;namespace&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;example.avro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;record&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fields&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;favorite_number&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;int&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;favorite_color&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;favorite_food&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Click on &lt;em&gt;File&lt;/em&gt; &amp;gt; &lt;em&gt;Open Folder…&lt;/em&gt; to open a new empty working directory, and save the schemas above as new files &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user1.json&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ser2.json&lt;/code&gt;, respectively.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/registry-vscode-extension/vscode-avro-example-files.png&quot; alt=&quot;VSCode - AVRO example files&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To create a new artifact, click on the &lt;em&gt;…&lt;/em&gt; &amp;gt; &lt;em&gt;Add artifact&lt;/em&gt; in the &lt;em&gt;Apicurio Explorer&lt;/em&gt; view. You will be shown a sequence of prompts, where you can choose from a list of options or input a text:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;New or existing group ?&lt;/em&gt; asks you whether you want to create the artifact within an existing group, or do you want to create a new one. We’ll select &lt;em&gt;NEW&lt;/em&gt;, since our Apicurio Registry is currently empty. If you select &lt;em&gt;EXISTING&lt;/em&gt;, you will be asked to &lt;em&gt;Choose group&lt;/em&gt; from a list of known group IDs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Create a new group ID&lt;/em&gt; asks you to name the new group. We’ll use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt;, and confirm by writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; again.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Choose an artifact type to push&lt;/em&gt; asks you for the type of schema that is being published to Apicurio Registry, and lists the supported options. We’ll select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AVRO&lt;/code&gt;, and confirm.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/images/guides/registry-vscode-extension/extension-create-artifact-types.png&quot; alt=&quot;Apicurio Registry Explorer extension - Create artifact - Types&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Artifact ID&lt;/em&gt; asks you to specify the artifact ID. We’ll type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user&lt;/code&gt;, and confirm.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Initial version&lt;/em&gt; prompt asks you to name the first artifact version. We’ll follow semantic versioning and use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.0&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Search for file&lt;/em&gt; prompt allows you to specify a search pattern, which will be used to provide a list of files from which you can select the schema. We named our schema files &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user1.json&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user2.json&lt;/code&gt;, so we can input &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user*&lt;/code&gt;. If there are only a few files in your workspace, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; to save a few keystrokes🙂.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Select file&lt;/em&gt; allows us to select one of the files based on the search pattern. Select the path where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user1.json&lt;/code&gt; file is located on your computer.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Create AVRO artifact with identifiers ‘default:user:1.0.0’ ?&lt;/em&gt; is the last step. It asks us for a confirmation that we want to create the artifact with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default:user:1.0.0&lt;/code&gt; GAV identifier.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;exploring-apicurio-registry-content&quot;&gt;Exploring Apicurio Registry content&lt;/h1&gt;

&lt;p&gt;Let’s take a look at the three views that Apicurio Registry Explorer provides:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Apicurio Explorer&lt;/em&gt; shows a list of artifacts, grouped by artifact group ID. Each item in the list contains artifact name, artifact state, and a tooltip with the artifact ID. This can be configured by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.explorer.name&lt;/code&gt; checkbox in the extension settings. Another thing to note is that the number of artifacts shown here is currently limited to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50&lt;/code&gt; by default. This can be changed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio.search.limit&lt;/code&gt; setting, but is definitely an opportunity for future improvement. In the top right menu of this view, users can:
    &lt;ul&gt;
      &lt;li&gt;Refresh the views&lt;/li&gt;
      &lt;li&gt;Search for an artifact&lt;/li&gt;
      &lt;li&gt;Add a new artifact&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Apicurio Versions Explorer&lt;/em&gt; shows a list of versions for a selected artifact. Each item in the list contains an artifact version, and its creation time. In the top right menu of this view, users can:
    &lt;ul&gt;
      &lt;li&gt;Reverse the order of versions in the view&lt;/li&gt;
      &lt;li&gt;Add a new artifact version&lt;/li&gt;
      &lt;li&gt;Delete the entire artifact&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Apicurio Metas Explorer&lt;/em&gt; shows a list of metadata for a selected artifact or an artifact version. What metadata is shown depends on which item is selected in one of the above views. This view contains a simple key-value list of metadata, except in the case of properties, which is a map that can be expanded. In the top right menu of this view, users can:
    &lt;ul&gt;
      &lt;li&gt;Change editable metadata&lt;/li&gt;
      &lt;li&gt;Change artifact version state. This is presented as a separate operation in the UI.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/registry-vscode-extension/extension-content.png&quot; alt=&quot;Apicurio Registry Explorer extension content views&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;openapi-content-preview&quot;&gt;OpenAPI Content Preview&lt;/h1&gt;

&lt;p&gt;You can view the contents of an artifact version by hovering over an item in the &lt;em&gt;Apicurio Versions Explorer&lt;/em&gt;, and clicking on the &lt;em&gt;Open&lt;/em&gt; icon. This will download the version content and opens it as an unsaved file in a new editor tab. The extension will try to determine the path to your active workspace, and use it for the location of the unsaved file, but you can always click &lt;em&gt;File&lt;/em&gt; &amp;gt; &lt;em&gt;Save as…&lt;/em&gt; to change the file name and location.&lt;/p&gt;

&lt;p&gt;Apicurio Registry Explorer extension supports integration with &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Arjun.swagger-viewer&quot;&gt;Swagger Viewer extension&lt;/a&gt;. This enables users to preview OpenAPI content stored in Apicurio Registry in the form of a generated documentation. To install Swagger Viewer, go to the quick open dialog with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl+P&lt;/code&gt;, and execute the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ext install Arjun.swagger-viewer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;then, when you open an OpenAPI file, Apicurio Registry Explorer will automatically launch a Swagger Preview tab for the given schema.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/registry-vscode-extension/extension-swagger-preview-integration.png&quot; alt=&quot;Integration with the Swagger Preview extension&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;upcoming-features&quot;&gt;Upcoming Features&lt;/h1&gt;

&lt;p&gt;While there is a lot of useful functionality in the current version (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0.3&lt;/code&gt;), the extension is still in early stages of development. Here is a list of several features that we would like to see implemented:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Authorized access support.&lt;/em&gt; Currently, the extension does not support authenticated access to Apicurio Registry. Integration with Keycloak (or any other OIDC provider) is an important feature that is missing at the moment.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;UX improvements.&lt;/em&gt; There are several improvements that will make the user experience with Apicurio Registry Explorer better, such as:
    &lt;ul&gt;
      &lt;li&gt;Handle large number of artifacts and artifact versions in the view panels. Currently, there is a fixed, but configurable limit.&lt;/li&gt;
      &lt;li&gt;Improve the workflow of some actions that require user input, for example, support corrections without starting the process of creating an artifact from the beginning.&lt;/li&gt;
      &lt;li&gt;Auto-detection of schema type when creating a new artifact.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Integrated schema editor.&lt;/em&gt; As mentioned earlier, the extension supports a simple integration with Swagger Preview. However, it’s static and only works for OpenAPI. Apicurio community already has a project that is designed for editing of schemas, &lt;a href=&quot;http://www.apicur.io/studio&quot;&gt;Apicurio Studio&lt;/a&gt;, which &lt;a href=&quot;http://localhost:8080/blog/2024/03/22/studio-1.0&quot;&gt;has recently started to receive more development focus&lt;/a&gt;. We should take advantage of this and integrate Apicurio Registry Explorer extension with Apicurio Studio editor.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Integration with Apicurio Registry TypeScript client&lt;/em&gt;. This is more of an implementation detail. As you may know, there are several client libraries for interacting with Apicurio Registry. For Apicurio Registry version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.0.0&lt;/code&gt;, we are working on a &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/tree/feat/typescript-sdk/typescript-sdk&quot;&gt;TypeScript SDK&lt;/a&gt;, which Apicurio Registry Explorer will be able to use.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Apicurio is a community of users and developers who are passionate about tools that help manage &lt;a href=&quot;https://developers.redhat.com/blog/2019/02/25/full-api-lifecycle-management-a-primer&quot;&gt;the API lifecycle&lt;/a&gt;. Apicurio Registry Explorer extension is an open-source &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry-vscode-plugin/&quot;&gt;project available on GitHub&lt;/a&gt; that a is a part of this effort.&lt;/p&gt;

&lt;p&gt;We would be happy to hear about your experience with this extension, and any contributions, suggestions, and issue reports are always welcome. You can reach us on &lt;a href=&quot;https://apicurio.zulipchat.com&quot;&gt;Zulip&lt;/a&gt; or on GiHub.&lt;/p&gt;

&lt;p&gt;If you would like to contribute, please see the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry-vscode-plugin/blob/main/CONTRIBUTING.md&quot;&gt;development documentation available on GitHub&lt;/a&gt;.&lt;/p&gt;
</description><category term="blog" /><category term="registry" /><category term="vscode" /><pubDate>Fri, 19 Apr 2024 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2024/04/19/registry-vscode-plugin</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2024/04/19/registry-vscode-plugin</guid>
        </item><item>
            <title>Apicurio Studio 1.0: The Awakening</title><dc:creator>Eric Wittmann</dc:creator>
            <author>eric.wittmann@gmail.com</author><description>&lt;p&gt;We have a big announcement to make about Apicurio Studio that I hope some people
will be interested in.&lt;/p&gt;

&lt;p&gt;Studio has been languishing for quite some time without getting the attention
it deserves from us (the maintainers/developers).  But that is about to change!&lt;/p&gt;

&lt;p&gt;We’re announcing the &lt;strong&gt;deprecation of Apicurio Studio 0.2&lt;/strong&gt; and the beginning of
a new line of development for &lt;strong&gt;Apicurio Studio 1.0&lt;/strong&gt;!&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;history-and-motivation&quot;&gt;History and Motivation&lt;/h1&gt;
&lt;p&gt;Apicurio Studio is the first project in the Apicurio open source community, and
it was started back in 2016 (this fact makes me feel very old).  Studio has been
a reasonably popular and helpful project, but was never deeply integrated into
Red Hat products (Red Hat sponsors all of the Apicurio projects).&lt;/p&gt;

&lt;p&gt;In 2019 the Apicurio Registry project was started, and the majority of our 
development bandwidth has gone into that project, resulting in two Red Hat products:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Red Hat Integration Service Registry (soon to be rebranded as Red Hat build of Apicurio Registry)&lt;/li&gt;
  &lt;li&gt;Red Hat OpenShift Service Registry (a fully managed version of Registry)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Between the core project and the fully managed version, we’ve had plenty to do on
the Registry project over these last few years.  Couple that with the lack of 
product urgency around Studio, and we’ve found ourselves in the problematic
position of having an old/stale codebase that has atrophied.&lt;/p&gt;

&lt;p&gt;However, that is now changing with renewed interest in releasing Studio functionality
within at least one new Red Hat product (hopefully more will follow) and with the
sunsetting/removal of Red Hat OpenShift Service Registry.&lt;/p&gt;

&lt;h1 id=&quot;deprecation-of-apicurio-studio-02&quot;&gt;Deprecation of Apicurio Studio 0.2&lt;/h1&gt;
&lt;p&gt;As of the writing of this blog entry, the current (old?) version of Studio is version
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.2.62.Final&lt;/code&gt;.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.2&lt;/code&gt; line of development is now officially deprecated.  We may
choose to fix major bugs, but certainly no new development will happen.&lt;/p&gt;

&lt;p&gt;As a result, we will be doing the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; branch of the codebase will be deprecated and renamed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.2.x&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Open issues will be closed where appropriate&lt;/li&gt;
  &lt;li&gt;The eval version at https://studio.apicur.io/ will be deprecated&lt;/li&gt;
  &lt;li&gt;An pinned announcement will be added to Zulip&lt;/li&gt;
  &lt;li&gt;A blog post will be written (this thing you’re reading now!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;introduction-of-apicurio-studio-10&quot;&gt;Introduction of Apicurio Studio 1.0&lt;/h1&gt;
&lt;p&gt;As part of the Managed Application Services effort (now deceased) at Red Hat, we 
created a new version of Apicurio Studio that we will now be releasing as version
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0&lt;/code&gt;.  We did this because the Studio 0.2 codebase was not well positioned (technically)
to be deployed as a Red Hat managed service.  As a result, we took a bunch of the 
core functionality of Studio 0.2 and either re-implemented or re-used it in our new
Studio 1.0 codebase.  Note that we did this using an updated technical stack and in
a way that also leverages common components developed as part of Apicurio Registry.&lt;/p&gt;

&lt;p&gt;However, this sword cuts multiple ways:  we have preserved as much of the core 
functionality as possible in the time we had, we have added new functionality that
didn’t exist before, &lt;strong&gt;but&lt;/strong&gt; we have also needed to remove some functionality you
might have been relying on.&lt;/p&gt;

&lt;h2 id=&quot;what-is-being-preserved-in-10&quot;&gt;What is being preserved in 1.0&lt;/h2&gt;
&lt;p&gt;The following functionality/features have been preserved in some capacity in the new
Studio 1.0:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Core OpenAPI and AsyncAPI editing capabilities&lt;/li&gt;
  &lt;li&gt;Asynchronous collaboration (multiple users can edit the same spec, but not at the same time)&lt;/li&gt;
  &lt;li&gt;Editing history (although with less granularity)&lt;/li&gt;
  &lt;li&gt;Create designs from scratch or imported from file/URL&lt;/li&gt;
  &lt;li&gt;Create designs from template&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-has-been-lost-for-now&quot;&gt;What has been lost (for now)&lt;/h2&gt;
&lt;p&gt;The following features did not make it into the new Studio 1.0 reformulation (but
may be restored/reimplemented in the future):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Importing reusable data types from other OpenAPI docs&lt;/li&gt;
  &lt;li&gt;Synchronous collaboration (multiple users editing the same spec at the same time)&lt;/li&gt;
  &lt;li&gt;Management of user-defined templates&lt;/li&gt;
  &lt;li&gt;Documentation Preview using ReDoc&lt;/li&gt;
  &lt;li&gt;User defined / configured validation profiles&lt;/li&gt;
  &lt;li&gt;Per-user multi-tenancy (all users can see all content for now)&lt;/li&gt;
  &lt;li&gt;GitHub/GitLab/Bitbucket publishing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-is-new-in-10&quot;&gt;What is new in 1.0&lt;/h2&gt;
&lt;p&gt;The fllowing features did not exist in 0.2 and are new and novel to 1.0:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Support for Avro, Protobuf, and JSON Schema types (simple text editing only)&lt;/li&gt;
  &lt;li&gt;Show Changes (diff) feature in the editor&lt;/li&gt;
  &lt;li&gt;Modern technical stack: Quarkus, React, Patternfly 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;summary-and-bottom-line&quot;&gt;Summary and Bottom Line&lt;/h1&gt;
&lt;p&gt;The main takeaway should be that we are finally getting a chance to put some focus
back on Studio.  I think in the short term we may be taking a step back in terms of
functionality, but what we gain is the ability to better maintain and progress both
of our main Apicurio projects:  Studio and Registry.&lt;/p&gt;

&lt;p&gt;This should allow us to once again make steady functional progress on Studio, fixing
issues and implementing new features, in a way we haven’t been able to do for a few
years.&lt;/p&gt;

&lt;p&gt;I’m looking forward to it!  Thanks for your interest, and very possibly your patience.&lt;/p&gt;
</description><category term="announcement" /><category term="studio" /><pubDate>Fri, 22 Mar 2024 16:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2024/03/22/studio-1.0</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2024/03/22/studio-1.0</guid>
        </item><item>
            <title>Resolving a bug in Apicurio Registry KafkaSQL storage upgrade process</title><dc:creator>Jakub Senko</dc:creator>
            <author>jsenko@redhat.com</author><description>&lt;p&gt;KafkaSQL is a popular storage option for Apicurio Registry, introduced in version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.0.0.Final&lt;/code&gt;. It has become very reliable, despite its higher complexity compared to the SQL storage option. Sadly, no software is immune to bugs, and KafkaSQL is no exception. On the other hand, bugs can be interesting, and the debugging process can provide a great opportunity to learn about Apicurio Registry and KafkaSQL internals. I’ve described one such bug in my previous blog post, titled &lt;a href=&quot;https://www.apicur.io/blog/2021/12/09/kafkasql-storage-and-security&quot;&gt;Resolving a bug in KafkaSQL storage for Apicurio Registry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A few days ago &lt;a id=&quot;note-1-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-1&quot;&gt;[1]&lt;/a&gt;, an Apicurio Registry user reported that some of their Protobuf artifacts went missing after an upgrade from version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.4.Final&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.5.Final&lt;/code&gt;. They are using the KafkaSQL storage option, and after some investigation it turned out that other storage options are not affected. I’ve decided to write this article to explain the details, but if you are using KafkaSQL storage, please first jump to the &lt;a href=&quot;#summary&quot;&gt;Summary&lt;/a&gt; section to see if you might be at risk, and then read the &lt;a href=&quot;#mitigation&quot;&gt;Mitigation&lt;/a&gt; section for a list of actions you should take to mitigate the problem until we release a fixed version.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;To help the investigation, the user gave us two important pieces of information:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Firstly, they noticed the following suspicious log message:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  2024-01-29T15:24:18.157902803Z 2024-01-29 15:24:18 WARN &amp;lt;_&amp;gt; [io.apicurio.registry.storage.impl.kafkasql.sql.KafkaSqlStore] (KSQL Kafka Consumer Thread) update content canonicalHash, no row match contentId 13 contentHash 6f93522739b215fdd88db69fa59c8c2f70d97e195c6813d89ee6e151840fd5b7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;which pointed me to focus my investigation on the source of that log message, the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.5.8.Final/storage/kafkasql/src/main/java/io/apicurio/registry/storage/impl/kafkasql/KafkaSqlProtobufCanonicalizerUpgrader.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt;&lt;/a&gt; class. This class is responsible for upgrading the &lt;em&gt;canonical content hash&lt;/em&gt; of Protobuf artifacts to the correct value.  This is needed because we have updated the algorithm we use to compute the hash. Because this class is specific to Protobuf, runs during an upgrade process, and only for KafkaSQL storage, it is our prime suspect.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Secondly, the user was able to provide us with a dump of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic, which was very helpful in discovering how the data went missing &lt;a id=&quot;note-2-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-2&quot;&gt;[2]&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before I explain what happened, let me first review a few relevant features of Apicurio Registry.&lt;/p&gt;

&lt;h2 id=&quot;content-hashes&quot;&gt;Content Hashes&lt;/h2&gt;

&lt;p&gt;In Apicurio Registry, artifact content can be addressed by several identifiers, most commonly:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;group ID&lt;/em&gt;, &lt;em&gt;artifact ID&lt;/em&gt;, and &lt;em&gt;version&lt;/em&gt; triple (GAV)&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;global ID&lt;/em&gt;, which is unique for every artifact version&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;content ID&lt;/em&gt;, which is unique for every piece of content (sequence of bytes).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In some situations, however, these identifiers are not known up front. For example, the user might want to determine whether a piece of content already exists in Apicurio Registry and find out its identifiers, or create a new artifact version &lt;strong&gt;only if&lt;/strong&gt; the content does not already exist. Therefore, it’s useful to have a way of asking Apicurio Registry about the &lt;strong&gt;content itself&lt;/strong&gt; (sequence of bytes).&lt;/p&gt;

&lt;p&gt;To support this feature, Apicurio Registry stores a &lt;em&gt;content hash&lt;/em&gt;, which is a SHA-256 hash of the content, in addition to the content bytes. Users can then retrieve the information about a piece of content using only its hash.&lt;/p&gt;

&lt;p&gt;However, the &lt;em&gt;content hash&lt;/em&gt; does not take the structure of the content into account. The following two Protobuf schemas are semantically equivalent:&lt;/p&gt;

&lt;div class=&quot;language-protobuf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;syntax&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;proto3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;error_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-protobuf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;syntax&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;proto3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  
  &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  
  &lt;span class=&quot;c1&quot;&gt;// Error code must be between 100 and 999 (inclusive). &lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;error_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;but they do not result in the same SHA-256 hash. To support searching for content that is equivalent to the input, but not necessarily equal, Apicurio Registry also stores a &lt;em&gt;canonical content hash&lt;/em&gt;, which is a SHA-256 hash of the content after it has been converted to a canonical form.&lt;/p&gt;

&lt;p&gt;The extended list of identifiers for artifact content in Apicurio Registry is therefore:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;group ID&lt;/em&gt;, &lt;em&gt;artifact ID&lt;/em&gt;, and &lt;em&gt;version&lt;/em&gt; triple (GAV)&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;global ID&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;content ID&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;content hash&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;canonical content hash&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id=&quot;protobuf-canonical-content-hash-upgrader&quot;&gt;&lt;/a&gt; The support for content hashes has evolved across Apicurio Registry versions. For example, in version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.1.2.Final&lt;/code&gt;, we have implemented a canonicalizer for Protobuf artifacts. Before this version, the &lt;em&gt;content hash&lt;/em&gt; and &lt;em&gt;canonical content hash&lt;/em&gt; were computed using the same algorithm. These kinds of changes require careful consideration and implementation of a special process that executes during Apicurio Registry version upgrades. In the case of the KafkaSQL storage option, this upgrade process is performed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; class.&lt;/p&gt;

&lt;h2 id=&quot;artifact-references&quot;&gt;Artifact References&lt;/h2&gt;

&lt;p&gt;Apicurio Registry supports many schema types, and some of them allow specifying references to other schemas. For example, Protobuf supports an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt; statement:&lt;/p&gt;

&lt;div class=&quot;language-protobuf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;syntax&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;proto3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;google/protobuf/any.proto&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;repeated&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;google.protobuf.Any&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;details&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and JSON Schema has the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$ref&lt;/code&gt; keyword:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/schemas/error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;details&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;array&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;items&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/schemas/error_detail&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have implemented initial support for artifact references in Apicurio Registry version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.2.2.Final&lt;/code&gt;. This feature allows users to specify a mapping between &lt;em&gt;reference name&lt;/em&gt; and another artifact version that it points to.&lt;/p&gt;

&lt;p&gt;As an example, let’s consider the Protobuf schema above. The import statement value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google/protobuf/any.proto&lt;/code&gt; is the &lt;em&gt;reference name&lt;/em&gt;. If we have already registered the Protobuf file that describes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google.protobuf.Any&lt;/code&gt; under &lt;em&gt;group ID = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt;&lt;/em&gt;, &lt;em&gt;artifact ID = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any.proto&lt;/code&gt;&lt;/em&gt;, and &lt;em&gt;version = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;&lt;/em&gt;, we can then register a Protobuf file that references it by:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Making the following &lt;em&gt;POST&lt;/em&gt; request to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apis/registry/v2/groups/default/artifacts&lt;/code&gt;:
    &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;syntax = &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;proto3&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;import &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;google/protobuf/any.proto&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;(...)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;references&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;google/protobuf/any.proto&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;groupId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;artifactId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;any.proto&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;with a special content type header &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content-Type: application/create.extended+json&lt;/code&gt;, or&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;using Apicurio Registry client library:
    &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;
&lt;div class=&quot;highlight&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;
&lt;code class=&quot;wide&quot;&gt;
// io.apicurio:apicurio-registry-client:2.5.8.Final
var meta = client.createArtifact(&quot;default&quot;, &quot;error.proto&quot;, &quot;syntax = \&quot;proto3\&quot;;\n\nimport \&quot;google/protobuf/any.proto\&quot;;\n\n(...)&quot;, List.of(    
    ArtifactReference.builder()
      .name(&quot;google/protobuf/any.proto&quot;)
      .groupId(&quot;default&quot;)
      .artifactId(&quot;any.proto&quot;)
      .version(&quot;2&quot;)
      .build()
));
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;content-hashes-and-artifact-references&quot;&gt;Content Hashes and Artifact References&lt;/h2&gt;

&lt;p&gt;As a result of implementing support for artifact references, we wanted to consider two pieces of artifact content that have the same sequence of bytes, but different references as &lt;strong&gt;not&lt;/strong&gt; equivalent. Therefore, we had to change how content hashes are computed. Before version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.2.Final&lt;/code&gt;, the following &lt;strong&gt;legacy&lt;/strong&gt; algorithm was used:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;content_hash = sha256(content)
canonical_content_hash = sha256(canonicalize(content))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;that has changed to the &lt;strong&gt;current&lt;/strong&gt; algorithm:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;content_hash = sha256(content ++ references)
canonical_content_hash = sha256(canonicalize(content, dependencies) ++ references)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Similarly to the &lt;a href=&quot;#protobuf-canonical-content-hash-upgrader&quot;&gt;situation when Protobuf canonicalizer was implemented&lt;/a&gt;, these changes also require careful implementation of an upgrade process. The process is currently performed by the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.5.8.Final/app/src/main/java/io/apicurio/registry/storage/impl/sql/upgrader/ReferencesContentHashUpgrader.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferencesContentHashUpgrader&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.5.8.Final/app/src/main/java/io/apicurio/registry/storage/impl/sql/upgrader/ReferencesCanonicalHashUpgrader.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferencesCanonicalContentHashUpgrader&lt;/code&gt;&lt;/a&gt; classes. However, they have not been added in the same Apicurio Registry version. More detailed version timeline is described later.&lt;/p&gt;

&lt;h2 id=&quot;how-kafkasql-stores-artifact-content&quot;&gt;How KafkaSQL Stores Artifact Content&lt;/h2&gt;

&lt;p&gt;KafkaSQL storage consists of two main components. Every Apicurio Registry instance has its own in-memory H2 database, and connects to a shared Kafka topic named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt;. This topic is then used to replicate the state of each in-memory database across instances, and to provide persistence.&lt;/p&gt;

&lt;p&gt;When Apicurio Registry is restarted, it consumes all messages in the topic to load the persisted data and bring the instance into sync with any other active Apicurio Registry instances. For more details, see the &lt;a href=&quot;https://www.apicur.io/blog/2021/12/09/kafkasql-storage-and-security&quot;&gt;Resolving a bug in KafkaSQL storage for Apicurio Registry&lt;/a&gt; blog post.&lt;/p&gt;

&lt;p&gt;For this bug analysis, we will focus on the Kafka messages responsible for replicating artifact content data.&lt;/p&gt;

&lt;p&gt;When new content is inserted into Apicurio Registry, KafkaSQL storage produces a new &lt;em&gt;content message&lt;/em&gt; on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic. The message has the following structure &lt;a id=&quot;note-3-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-3&quot;&gt;[3]&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;key = {content_id, content_hash} // partition_key = content_hash
value = {operation, canonical_content_hash, content, references}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operation&lt;/code&gt; is one of:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE&lt;/code&gt;, which is used when the content is inserted into Apicurio Registry,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPDATE&lt;/code&gt;, which is currently only used by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt;, or&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMPORT&lt;/code&gt;, which is used by the export-import feature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Structure of the &lt;em&gt;content message key&lt;/em&gt; is important for determining which Kafka messages can be safely compacted. When &lt;a href=&quot;https://kafka.apache.org/documentation/#compaction&quot;&gt;Kafka log compaction&lt;/a&gt; runs, only the last message with a given key is preserved, and previous messages are deleted. This has the advantage of reducing the size of the topic by removing stale messages, but has the potential of causing problems in case of a bug, as we’ll see later. Apicurio Registry automatically creates the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic if it &lt;strong&gt;does not&lt;/strong&gt; already exist, and it configures the topic with &lt;strong&gt;log compaction enabled&lt;/strong&gt; by default.&lt;/p&gt;

&lt;h2 id=&quot;version-history&quot;&gt;Version History&lt;/h2&gt;

&lt;p&gt;Because we suspect that the issue is caused by a bug in the upgrade process for KafkaSQL storage, specifically the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt;, we need to analyze Apicurio Registry versions that contain relevant changes. This helps us understand how the bug was introduced and which versions are affected.&lt;/p&gt;

&lt;h3 id=&quot;version-212final&quot;&gt;&lt;strong&gt;Version 2.1.2.Final&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;In this version, we implemented the content canonicalizer for Protobuf artifacts &lt;a id=&quot;note-4-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-4&quot;&gt;[4]&lt;/a&gt;. Before this change, &lt;em&gt;canonical content hash&lt;/em&gt; for Protobuf artifacts was the same as &lt;em&gt;content hash&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;content_hash = sha256(content)
canonical_content_hash = sha256(content)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Protobuf content hashes are now computed using the following algorithm:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;content_hash = sha256(content)
canonical_content_hash = sha256(canonicalize(content))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s call these &lt;em&gt;legacy content hash&lt;/em&gt; and &lt;em&gt;legacy canonical content hash&lt;/em&gt; to differentiate between the current hash algorithm implemented in version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.2.Final&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;version-213final&quot;&gt;&lt;strong&gt;Version 2.1.3.Final&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;In this version, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; is implemented &lt;a id=&quot;note-5-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-5&quot;&gt;[5]&lt;/a&gt;, which is responsible for computing &lt;em&gt;legacy canonical content hash&lt;/em&gt; for Protobuf artifacts. The upgrader is executed for both SQL and KafkaSQL storage options.&lt;/p&gt;

&lt;h3 id=&quot;version-222final&quot;&gt;&lt;strong&gt;Version 2.2.2.Final&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;In this version, artifact references are implemented &lt;a id=&quot;note-6-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-6&quot;&gt;[6]&lt;/a&gt;. The algorithm for computing content hashes does not change.&lt;/p&gt;

&lt;h3 id=&quot;version-242final&quot;&gt;&lt;strong&gt;Version 2.4.2.Final&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;This version changes how content hashes are computed &lt;a id=&quot;note-7-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-7&quot;&gt;[7]&lt;/a&gt;, in order to take artifact references into account:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;content_hash = sha256(content ++ references)
canonical_content_hash = sha256(canonicalize(content, dependencies) ++ references)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s call these &lt;em&gt;current content hash&lt;/em&gt; and &lt;em&gt;current canonical content hash&lt;/em&gt; to differentiate between the legacy hash algorithm implemented in version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.1.2.Final&lt;/code&gt; for Protobuf artifacts.&lt;/p&gt;

&lt;p&gt;By mistake, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; was not updated, so it still attempts to change the &lt;em&gt;canonical content hash&lt;/em&gt; to the legacy value during Apicurio Registry restart.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.4.2.Final/app/src/main/java/io/apicurio/registry/storage/impl/sql/upgrader/ReferencesContentHashUpgrader.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferencesContentHashUpgrader&lt;/code&gt;&lt;/a&gt; is implemented to upgrade the &lt;em&gt;legacy content hash&lt;/em&gt; to the &lt;em&gt;current content hash&lt;/em&gt; in the in-memory database. It’s not executed for KafkaSQL storage option by mistake.&lt;/p&gt;

&lt;h3 id=&quot;version-243final&quot;&gt;&lt;strong&gt;Version 2.4.3.Final&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;User reports an &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues/3414&quot;&gt;issue related to content hashes in KafkaSQL&lt;/a&gt;. This is fixed in the next version.&lt;/p&gt;

&lt;p&gt;This version also fixes a bug in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; &lt;a id=&quot;note-8-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-8&quot;&gt;[8]&lt;/a&gt;, that resulted in an SQL error when the &lt;em&gt;canonical content hash&lt;/em&gt; change was being applied:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2024-02-07 13:54:26 DEBUG &amp;lt;&amp;gt; [io.apicurio.registry.storage.impl.kafkasql.sql.KafkaSqlSink] (KSQL Kafka Consumer Thread) Registry exception detected: io.apicurio.registry.storage.impl.sql.jdb.RuntimeSqlException: org.h2.jdbc.JdbcSQLDataException: Parameter &quot;#4&quot; is not set [90012-214]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;resulting in the operation failing, and in-memory database containing the same &lt;em&gt;canonical content hash&lt;/em&gt; as before.&lt;/p&gt;

&lt;h3 id=&quot;version-244final&quot;&gt;&lt;strong&gt;Version: 2.4.4.Final&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.4.4.Final/app/src/main/java/io/apicurio/registry/storage/impl/sql/upgrader/ReferencesCanonicalHashUpgrader.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferencesCanonicalHashUpgrader&lt;/code&gt;&lt;/a&gt; is implemented &lt;a id=&quot;note-9-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-9&quot;&gt;[9]&lt;/a&gt; to upgrade the &lt;em&gt;legacy canonical content hash&lt;/em&gt; to the &lt;em&gt;current canonical content hash&lt;/em&gt; in the in-memory database.&lt;/p&gt;

&lt;p&gt;Both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;References*&lt;/code&gt; upgraders are now executed for the KafkaSQL storage option.&lt;/p&gt;

&lt;h2 id=&quot;hypothesis&quot;&gt;Hypothesis&lt;/h2&gt;

&lt;p&gt;When examining the Kafka topic dump &lt;a id=&quot;note-10-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-10&quot;&gt;[10]&lt;/a&gt;, I’ve noticed that for some &lt;em&gt;content ID&lt;/em&gt;s, in this case &lt;em&gt;content ID = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;&lt;/em&gt;, the only message present is:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;
&lt;div class=&quot;highlight&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;
&lt;code class=&quot;wide&quot;&gt;
{
  &quot;offset&quot;: 10,
  &quot;key&quot;: &quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;b5f276336ecbb160556c114fe22e7cebd8353f0bc11127eb0f69a4aad32648ea\&quot;,\&quot;contentId\&quot;:2}&quot;,
  &quot;payload&quot;: &quot;\u0002\u0002\u0000\u0000\u0000@59e286281876629c2715b06c8ef294a1d4a713f5e4249d7a3e386bb734f7db90\u0000\u0000\u0000\u0000\u0000\u0000\u0000V[{\&quot;groupId\&quot;:null,\&quot;artifactId\&quot;:\&quot;any\&quot;,\&quot;version\&quot;:\&quot;1\&quot;,\&quot;name\&quot;:\&quot;google/protobuf/any.proto\&quot;}]&quot;
}
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;which is missing the content bytes (the four bytes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\u0000\u0000\u0000\u0000&lt;/code&gt; encode the length of the content, which is zero). This means that the previous &lt;em&gt;content messages&lt;/em&gt; must have been compacted. How is the message above produced? It is produced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We now have the information I needed to formulate a hypothesis about what happened. There are two related bugs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.5.8.Final/storage/kafkasql/src/main/java/io/apicurio/registry/storage/impl/kafkasql/KafkaSqlProtobufCanonicalizerUpgrader.java#L83-L91&quot;&gt;was not updated to use the &lt;em&gt;current canonical content hash&lt;/em&gt; algorithm&lt;/a&gt;, so it always attempts to change any Protobuf content that uses &lt;em&gt;current canonical content hash&lt;/em&gt; back to &lt;em&gt;legacy canonical content hash&lt;/em&gt; after each Apicurio Registry restart. Since the hashes are equal for Protobuf content without references, only Protobuf content with references is affected.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; performs the hash change &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/blob/2.5.8.Final/storage/kafkasql/src/main/java/io/apicurio/registry/storage/impl/kafkasql/KafkaSqlProtobufCanonicalizerUpgrader.java#L80&quot;&gt;using the following Kafka &lt;em&gt;content message&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;key = {content_id = (unchanged), content_hash = (unchanged)}
value = {operation = UPDATE, canonical_content_hash = (legacy value), content = null, references = (unchanged)}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Which has the same &lt;em&gt;content message key&lt;/em&gt; as the Kafka message that was used to insert the content. The previous &lt;em&gt;content message&lt;/em&gt; might become compacted and the content lost.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;reproducer-scenario&quot;&gt;Reproducer Scenario&lt;/h2&gt;

&lt;p&gt;This is a more detailed list of steps that I think should reproduce the problem:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;User starts with Apicurio Registry version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.2.2.Final-2.4.1.Final&lt;/code&gt; (inclusive), using KafkaSQL storage.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;User adds some Protobuf artifacts with references, let’s call them &lt;strong&gt;set A&lt;/strong&gt;. The content hashes for these artifacts are computed using the legacy algorithm. Therefore, the &lt;em&gt;content message keys&lt;/em&gt; contain &lt;em&gt;legacy content hash&lt;/em&gt;, and &lt;em&gt;content message values&lt;/em&gt; contain &lt;em&gt;legacy canonical content hash&lt;/em&gt;. The in-memory database contains the same.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;User upgrades Apicurio Registry to version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.2.Final-2.4.3.Final&lt;/code&gt; (inclusive).&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; is executed. For each Protobuf artifact in the database, it computes the  &lt;em&gt;legacy canonical content hash&lt;/em&gt;, which is equal to the canonical content hash of &lt;strong&gt;set A&lt;/strong&gt;, so nothing changes. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reference*&lt;/code&gt; upgraders are not executed for KafkaSQL in this version.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;User adds some more Protobuf artifacts with references, let’s call them &lt;strong&gt;set B&lt;/strong&gt;. The content hashes for these artifacts are computed using the current algorithm. Therefore, the &lt;em&gt;content message keys&lt;/em&gt; contain &lt;em&gt;current content hash&lt;/em&gt;, and &lt;em&gt;content message values&lt;/em&gt; contain &lt;em&gt;current canonical content hash&lt;/em&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;User restarts their Apicurio Registry instance.&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; is executed again. It’s not necessary to upgrade Apicurio Registry to a later version, because KafkaSQL upgraders are executed every time Apicurio Registry starts. For each Protobuf artifact in the database, it computes the &lt;em&gt;legacy canonical content hash&lt;/em&gt;, which equals to the &lt;em&gt;canonical content hash&lt;/em&gt; of &lt;strong&gt;set A&lt;/strong&gt;, but &lt;strong&gt;not set B&lt;/strong&gt;. Therefore, it changes the &lt;em&gt;canonical content hash&lt;/em&gt; of &lt;strong&gt;set B&lt;/strong&gt; to the legacy value, by sending a &lt;em&gt;content message value&lt;/em&gt; that is missing the content by mistake.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Some time later, Kafka compaction runs, which effectively deletes the content of &lt;strong&gt;set B&lt;/strong&gt;. The missing data is only noticed after another restart of Apicurio Registry, when the in-memory database is loaded from the Kafka topic.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;User upgrades Apicurio Registry to version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.4.Final-2.5.8.Final&lt;/code&gt; (inclusive) &lt;a id=&quot;note-11-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-11&quot;&gt;[11]&lt;/a&gt;.&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; is executed again.&lt;/li&gt;
      &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferencesContentHashUpgrader&lt;/code&gt; is executed, which updates any &lt;em&gt;legacy content hash&lt;/em&gt; it finds to &lt;em&gt;current content hash&lt;/em&gt;, but only in the in-memory database. This currently does not cause any problems, but since &lt;em&gt;content hash&lt;/em&gt; is part of the &lt;em&gt;content message key&lt;/em&gt;, there is a risk of a potential bug in the future. See &lt;a href=&quot;#additional-bug-risk&quot;&gt;Additional bug risk&lt;/a&gt; section for more details.&lt;/li&gt;
      &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferencesCanonicalHashUpgrader&lt;/code&gt; is executed, which updates any &lt;em&gt;legacy canonical content hash&lt;/em&gt; it finds to &lt;em&gt;current canonical content hash&lt;/em&gt;, but only in the in-memory database. This currently does not cause any problems.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;reproducer&quot;&gt;Reproducer&lt;/h2&gt;

&lt;p&gt;To confirm my hypothesis, I will follow the reproducer scenario above. We can skip steps 1, 2, and 3.1., starting with an empty Apicurio Registry version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.2.Final&lt;/code&gt;. We will configure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic with an aggressive log compaction &lt;a id=&quot;note-12-back&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#note-12&quot;&gt;[12]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will add two Protobuf artifacts, &lt;a href=&quot;https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/any.proto&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any.proto&lt;/code&gt;&lt;/a&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error.proto&lt;/code&gt;. The second artifact references the first one.&lt;/p&gt;

&lt;p&gt;The following shows abbreviated contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONTENT&lt;/code&gt; table in the in-memory database:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;
&lt;div class=&quot;highlight&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;
&lt;code class=&quot;wide&quot;&gt;
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
|TENANTID|CONTENTID|CANONICALHASH                                                   |CONTENTHASH                                                     |CONTENT                            |ARTIFACTREFERENCES                                                                    |
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
|_       |1        |628ccecee09bcc0bcfdc95adce7041e998bb82a7377c600d7dbbba4f70dcae04|bfd6f785126a85ec181c41bc29680b394627c32b7ef0c3e4cef3ced86e463e7d|syntax = &quot;proto3&quot;;                 |null                                                                                  |
|        |         |                                                                |                                                                |                                   |                                                                                      |
|        |         |                                                                |                                                                |package google.protobuf;           |                                                                                      |
|        |         |                                                                |                                                                |[...]                              |                                                                                      |
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
|_       |2        |b5a276ddf3fc1724dbe206cbc6da60adf8e32af5613ef0fe52fb1dde8da6b67a|b5f276336ecbb160556c114fe22e7cebd8353f0bc11127eb0f69a4aad32648ea|syntax = &quot;proto3&quot;;                 |[{&quot;groupId&quot;:null,&quot;artifactId&quot;:&quot;any&quot;,&quot;version&quot;:&quot;1&quot;,&quot;name&quot;:&quot;google/protobuf/any.proto&quot;}]|
|        |         |                                                                |                                                                |                                   |                                                                                      |
|        |         |                                                                |                                                                |import &quot;google/protobuf/any.proto&quot;;|                                                                                      |
|        |         |                                                                |                                                                |                                   |                                                                                      |
|        |         |                                                                |                                                                |message Error {                    |                                                                                      |
|        |         |                                                                |                                                                |[...]                              |                                                                                      |
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Notice that the &lt;em&gt;canonical content hash&lt;/em&gt; value in the second row is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b5a276ddf3fc1724dbe206cbc6da60adf8e32af5613ef0fe52fb1dde8da6b67a&lt;/code&gt;. This is the correct value that was computed using the current hash algorithm when the content was inserted.&lt;/p&gt;

&lt;p&gt;We restart Apicurio Registry, and see the following message in the log, which means that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; has been executed:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2024-02-07 12:29:47 DEBUG &amp;lt;&amp;gt; [io.apicurio.registry.storage.impl.kafkasql.KafkaSqlProtobufCanonicalizerUpgrader] (KSQL Kafka Consumer Thread) Protobuf content canonicalHash outdated value detected, updating contentId 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONTENT&lt;/code&gt; table now shows that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; updated the &lt;em&gt;canonical content hash&lt;/em&gt; to the incorrect legacy value:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;
&lt;div class=&quot;highlight&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;
&lt;code class=&quot;wide&quot;&gt;
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
|TENANTID|CONTENTID|CANONICALHASH                                                   |CONTENTHASH                                                     |CONTENT                            |ARTIFACTREFERENCES                                                                    |
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
|_       |1        |628ccecee09bcc0bcfdc95adce7041e998bb82a7377c600d7dbbba4f70dcae04|bfd6f785126a85ec181c41bc29680b394627c32b7ef0c3e4cef3ced86e463e7d|syntax = &quot;proto3&quot;;                 |null                                                                                  |
|        |         |                                                                |                                                                |                                   |                                                                                      |
|        |         |                                                                |                                                                |package google.protobuf;           |                                                                                      |
|        |         |                                                                |                                                                |[...]                              |                                                                                      |
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
|_       |2        |59e286281876629c2715b06c8ef294a1d4a713f5e4249d7a3e386bb734f7db90|b5f276336ecbb160556c114fe22e7cebd8353f0bc11127eb0f69a4aad32648ea|syntax = &quot;proto3&quot;;                 |[{&quot;groupId&quot;:null,&quot;artifactId&quot;:&quot;any&quot;,&quot;version&quot;:&quot;1&quot;,&quot;name&quot;:&quot;google/protobuf/any.proto&quot;}]|
|        |         |                                                                |                                                                |                                   |                                                                                      |
|        |         |                                                                |                                                                |import &quot;google/protobuf/any.proto&quot;;|                                                                                      |
|        |         |                                                                |                                                                |                                   |                                                                                      |
|        |         |                                                                |                                                                |message Error {                    |                                                                                      |
|        |         |                                                                |                                                                |[...]                              |                                                                                      |
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;At this moment, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic contains the following messages:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;
&lt;div class=&quot;highlight&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;
&lt;code class=&quot;wide&quot;&gt;
[
  {
    &quot;offset&quot;: 0,
    &quot;key&quot;: &quot;\u0000{\&quot;bootstrapId\&quot;:\&quot;c613911c-2f32-4a2a-9d79-fffa321f9548\&quot;}&quot;,
    &quot;payload&quot;: null
  },
  {
    &quot;offset&quot;: 1,
    &quot;key&quot;: &quot;\t{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;uuid\&quot;:\&quot;c6758a4e-1c7c-4c68-ad17-0dc99f1cb374\&quot;}&quot;,
    &quot;payload&quot;: &quot;\t{\&quot;action\&quot;:1}&quot;
  },
  {
    &quot;offset&quot;: 2,
    &quot;key&quot;: &quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;bfd6f785126a85ec181c41bc29680b394627c32b7ef0c3e4cef3ced86e463e7d\&quot;,\&quot;contentId\&quot;:1}&quot;,
    &quot;payload&quot;: &quot;\u0002\u0001\u0000\u0000\u0000@628ccecee09bcc0bcfdc95adce7041e998bb82a7377c600d7dbbba4f70dcae04\u0000\u0000\u0001\u0087syntax = \&quot;proto3\&quot;;\n\npackage google.protobuf;\n\noption go_package = \&quot;google.golang.org/protobuf/types/known/anypb\&quot;;\noption java_package = \&quot;com.google.protobuf\&quot;;\noption java_outer_classname = \&quot;AnyProto\&quot;;\noption java_multiple_files = true;\noption objc_class_prefix = \&quot;GPB\&quot;;\noption csharp_namespace = \&quot;Google.Protobuf.WellKnownTypes\&quot;;\n\nmessage Any {\n\n  string type_url = 1;\n\n  bytes value = 2;\n}\n\u0000\u0000\u0000\u0000&quot;
  },
  {
    &quot;offset&quot;: 3,
    &quot;key&quot;: &quot;\b{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;uuid\&quot;:\&quot;ea335c14-7f1b-40ee-8beb-94ef0b02890d\&quot;}&quot;,
    &quot;payload&quot;: &quot;\b{\&quot;action\&quot;:1}&quot;
  },
  {
    &quot;offset&quot;: 4,
    &quot;key&quot;: &quot;\u0003{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;artifactId\&quot;:\&quot;any\&quot;,\&quot;uuid\&quot;:\&quot;ecb0a768-617c-4b3d-af1a-3d340c4f2f44\&quot;}&quot;,
    &quot;payload&quot;: &quot;\u0003{\&quot;action\&quot;:1,\&quot;metaData\&quot;:{},\&quot;globalId\&quot;:1,\&quot;artifactType\&quot;:\&quot;PROTOBUF\&quot;,\&quot;contentHash\&quot;:\&quot;bfd6f785126a85ec181c41bc29680b394627c32b7ef0c3e4cef3ced86e463e7d\&quot;,\&quot;createdBy\&quot;:\&quot;\&quot;,\&quot;createdOn\&quot;:1707303731203}&quot;
  },
  {
    &quot;offset&quot;: 5,
    &quot;key&quot;: &quot;\t{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;uuid\&quot;:\&quot;35a22e8e-f826-4568-8bd8-9d8722a5f25f\&quot;}&quot;,
    &quot;payload&quot;: &quot;\t{\&quot;action\&quot;:1}&quot;
  },
  {
    &quot;offset&quot;: 6,
    &quot;key&quot;: &quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;b5f276336ecbb160556c114fe22e7cebd8353f0bc11127eb0f69a4aad32648ea\&quot;,\&quot;contentId\&quot;:2}&quot;,
    &quot;payload&quot;: &quot;\u0002\u0001\u0000\u0000\u0000@b5a276ddf3fc1724dbe206cbc6da60adf8e32af5613ef0fe52fb1dde8da6b67a\u0000\u0000\u0000\u008dsyntax = \&quot;proto3\&quot;;\n\nimport \&quot;google/protobuf/any.proto\&quot;;\n\nmessage Error {\n  string message = 1;\n  repeated google.protobuf.Any details = 2;\n}\n\u0000\u0000\u0000V[{\&quot;groupId\&quot;:null,\&quot;artifactId\&quot;:\&quot;any\&quot;,\&quot;version\&quot;:\&quot;1\&quot;,\&quot;name\&quot;:\&quot;google/protobuf/any.proto\&quot;}]&quot;
  },
  {
    &quot;offset&quot;: 7,
    &quot;key&quot;: &quot;\b{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;uuid\&quot;:\&quot;e57510fa-f204-4277-bb5d-7424987737af\&quot;}&quot;,
    &quot;payload&quot;: &quot;\b{\&quot;action\&quot;:1}&quot;
  },
  {
    &quot;offset&quot;: 8,
    &quot;key&quot;: &quot;\u0003{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;artifactId\&quot;:\&quot;error\&quot;,\&quot;uuid\&quot;:\&quot;b51ca026-3c39-44fd-b22b-6b29478d4477\&quot;}&quot;,
    &quot;payload&quot;: &quot;\u0003{\&quot;action\&quot;:1,\&quot;metaData\&quot;:{},\&quot;globalId\&quot;:2,\&quot;artifactType\&quot;:\&quot;PROTOBUF\&quot;,\&quot;contentHash\&quot;:\&quot;b5f276336ecbb160556c114fe22e7cebd8353f0bc11127eb0f69a4aad32648ea\&quot;,\&quot;createdBy\&quot;:\&quot;\&quot;,\&quot;createdOn\&quot;:1707303731454}&quot;
  },
  {
    &quot;offset&quot;: 9,
    &quot;key&quot;: &quot;\u0000{\&quot;bootstrapId\&quot;:\&quot;d58884b2-6552-4330-9cd3-f3f294a54e4b\&quot;}&quot;,
    &quot;payload&quot;: null
  },
  {
    &quot;offset&quot;: 10,
    &quot;key&quot;: &quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;b5f276336ecbb160556c114fe22e7cebd8353f0bc11127eb0f69a4aad32648ea\&quot;,\&quot;contentId\&quot;:2}&quot;,
    &quot;payload&quot;: &quot;\u0002\u0002\u0000\u0000\u0000@59e286281876629c2715b06c8ef294a1d4a713f5e4249d7a3e386bb734f7db90\u0000\u0000\u0000\u0000\u0000\u0000\u0000V[{\&quot;groupId\&quot;:null,\&quot;artifactId\&quot;:\&quot;any\&quot;,\&quot;version\&quot;:\&quot;1\&quot;,\&quot;name\&quot;:\&quot;google/protobuf/any.proto\&quot;}]&quot;
  }
]
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Notice that message &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt; is a bootstrap message marking the point where Apicurio Registry was restarted. Message &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt; is produced by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will wait for Kafka log compaction to run, and observe the contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic again:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;
&lt;div class=&quot;highlight&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;
&lt;code class=&quot;wide&quot;&gt;
[
  {
    &quot;offset&quot;: 1,
    &quot;key&quot;: &quot;\t{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;uuid\&quot;:\&quot;4c039a9a-62b5-459a-b6da-fc771345de31\&quot;}&quot;,
    &quot;payload&quot;: &quot;\t{\&quot;action\&quot;:1}&quot;
  },
  {
    &quot;offset&quot;: 2,
    &quot;key&quot;: &quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;bfd6f785126a85ec181c41bc29680b394627c32b7ef0c3e4cef3ced86e463e7d\&quot;,\&quot;contentId\&quot;:1}&quot;,
    &quot;payload&quot;: &quot;\u0002\u0001\u0000\u0000\u0000@628ccecee09bcc0bcfdc95adce7041e998bb82a7377c600d7dbbba4f70dcae04\u0000\u0000\u0001\u0087syntax = \&quot;proto3\&quot;;\n\npackage google.protobuf;\n\noption go_package = \&quot;google.golang.org/protobuf/types/known/anypb\&quot;;\noption java_package = \&quot;com.google.protobuf\&quot;;\noption java_outer_classname = \&quot;AnyProto\&quot;;\noption java_multiple_files = true;\noption objc_class_prefix = \&quot;GPB\&quot;;\noption csharp_namespace = \&quot;Google.Protobuf.WellKnownTypes\&quot;;\n\nmessage Any {\n\n  string type_url = 1;\n\n  bytes value = 2;\n}\n\u0000\u0000\u0000\u0000&quot;
  },
  {
    &quot;offset&quot;: 3,
    &quot;key&quot;: &quot;\b{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;uuid\&quot;:\&quot;59fb1cc2-5779-4e39-b089-3272ad8c3b10\&quot;}&quot;,
    &quot;payload&quot;: &quot;\b{\&quot;action\&quot;:1}&quot;
  },
  {
    &quot;offset&quot;: 4,
    &quot;key&quot;: &quot;\u0003{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;artifactId\&quot;:\&quot;any\&quot;,\&quot;uuid\&quot;:\&quot;7a77789f-d882-4578-b965-1b5050335b05\&quot;}&quot;,
    &quot;payload&quot;: &quot;\u0003{\&quot;action\&quot;:1,\&quot;metaData\&quot;:{},\&quot;globalId\&quot;:1,\&quot;artifactType\&quot;:\&quot;PROTOBUF\&quot;,\&quot;contentHash\&quot;:\&quot;bfd6f785126a85ec181c41bc29680b394627c32b7ef0c3e4cef3ced86e463e7d\&quot;,\&quot;createdBy\&quot;:\&quot;\&quot;,\&quot;createdOn\&quot;:1707305198467}&quot;
  },
  {
    &quot;offset&quot;: 5,
    &quot;key&quot;: &quot;\t{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;uuid\&quot;:\&quot;33711009-be5c-4491-86e2-d085ba9a39f5\&quot;}&quot;,
    &quot;payload&quot;: &quot;\t{\&quot;action\&quot;:1}&quot;
  },
  {
    &quot;offset&quot;: 7,
    &quot;key&quot;: &quot;\b{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;uuid\&quot;:\&quot;3dd98144-215b-45cf-92f0-e34f09a08a4e\&quot;}&quot;,
    &quot;payload&quot;: &quot;\b{\&quot;action\&quot;:1}&quot;
  },
  {
    &quot;offset&quot;: 8,
    &quot;key&quot;: &quot;\u0003{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;artifactId\&quot;:\&quot;error\&quot;,\&quot;uuid\&quot;:\&quot;07cc6743-ce36-4258-9fbd-c9c6ec8028d9\&quot;}&quot;,
    &quot;payload&quot;: &quot;\u0003{\&quot;action\&quot;:1,\&quot;metaData\&quot;:{},\&quot;globalId\&quot;:2,\&quot;artifactType\&quot;:\&quot;PROTOBUF\&quot;,\&quot;contentHash\&quot;:\&quot;b5f276336ecbb160556c114fe22e7cebd8353f0bc11127eb0f69a4aad32648ea\&quot;,\&quot;createdBy\&quot;:\&quot;\&quot;,\&quot;createdOn\&quot;:1707305199277}&quot;
  },
  {
    &quot;offset&quot;: 10,
    &quot;key&quot;: &quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;b5f276336ecbb160556c114fe22e7cebd8353f0bc11127eb0f69a4aad32648ea\&quot;,\&quot;contentId\&quot;:2}&quot;,
    &quot;payload&quot;: &quot;\u0002\u0002\u0000\u0000\u0000@59e286281876629c2715b06c8ef294a1d4a713f5e4249d7a3e386bb734f7db90\u0000\u0000\u0000\u0000\u0000\u0000\u0000V[{\&quot;groupId\&quot;:null,\&quot;artifactId\&quot;:\&quot;any\&quot;,\&quot;version\&quot;:\&quot;1\&quot;,\&quot;name\&quot;:\&quot;google/protobuf/any.proto\&quot;}]&quot;
  }
]
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Notice that the bootstrap messages at offset &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9&lt;/code&gt; have been deleted, because they are empty (this is on purpose). However, message &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt; has also been deleted because it shares the key with message &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt;. This was not intended and is the bug we need to fix.&lt;/p&gt;

&lt;p&gt;After another restart, the Protobuf content with references is missing in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONTENT&lt;/code&gt; table:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;
&lt;div class=&quot;highlight&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;
&lt;code class=&quot;wide&quot;&gt;
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
|TENANTID|CONTENTID|CANONICALHASH                                                   |CONTENTHASH                                                     |CONTENT                            |ARTIFACTREFERENCES                                                                    |
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
|_       |1        |628ccecee09bcc0bcfdc95adce7041e998bb82a7377c600d7dbbba4f70dcae04|bfd6f785126a85ec181c41bc29680b394627c32b7ef0c3e4cef3ced86e463e7d|syntax = &quot;proto3&quot;;                 |null                                                                                  |
|        |         |                                                                |                                                                |                                   |                                                                                      |
|        |         |                                                                |                                                                |package google.protobuf;           |                                                                                      |
|        |         |                                                                |                                                                |[...]                              |                                                                                      |
+--------+---------+----------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------+--------------------------------------------------------------------------------------+
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;span&gt;∎&lt;/span&gt;&lt;/p&gt;

&lt;h2 id=&quot;-additional-bug-risk&quot;&gt;&lt;a id=&quot;additional-bug-risk&quot;&gt;&lt;/a&gt; Additional Bug Risk&lt;/h2&gt;

&lt;p&gt;Because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferencesContentHashUpgrader&lt;/code&gt; does not write the updated content hashes to the Kafka topic in step 7.2., there might be a mismatch between the content hash value in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic and the in-memory database. If Apicurio Registry produced a new &lt;em&gt;content message&lt;/em&gt; for a given &lt;em&gt;content ID&lt;/em&gt; (for example, to upgrade the canonical content hash, content, or references), the &lt;em&gt;content message key&lt;/em&gt; that would be used is different from the one present in the topic, because the key would contain a &lt;em&gt;content hash&lt;/em&gt; that has changed.&lt;/p&gt;

&lt;p&gt;Currently, the only operation that updates content via &lt;em&gt;content message&lt;/em&gt; is the one that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaSqlProtobufCanonicalizerUpgrader&lt;/code&gt; uses. This operation is always executed &lt;strong&gt;before&lt;/strong&gt; the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reference*&lt;/code&gt; upgraders, so it does not cause issues, but it is still a potential bug. To fix this, we would need to change the format of the &lt;em&gt;content message key&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;-summary&quot;&gt;&lt;a id=&quot;summary&quot;&gt;&lt;/a&gt; Summary&lt;/h2&gt;

&lt;p&gt;The problem is caused by two bugs in the Apicurio Registry upgrade process that is specific to the KafkaSQL storage option.&lt;/p&gt;

&lt;p&gt;You are affected if:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You are using Apicurio Registry &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.2.Final&lt;/code&gt; or later, &lt;strong&gt;and&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;you are using KafkaSQL storage, &lt;strong&gt;and&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;you have created Protobuf artifacts with references, &lt;strong&gt;and&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;you restart the Apicurio Registry instance (for example during version upgrade).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Symptoms are different based on your Kafka log compaction configuration:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If Kafka compaction runs, some Protobuf artifacts with references &lt;strong&gt;might be deleted&lt;/strong&gt;, and disappear from Apicurio Registry after restart.&lt;/li&gt;
  &lt;li&gt;If Kafka compaction is disabled, data is &lt;strong&gt;not&lt;/strong&gt; lost, but some Protobuf artifacts with references might have their &lt;em&gt;canonical content hash&lt;/em&gt; updated to the &lt;strong&gt;legacy&lt;/strong&gt; version. If you are on version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.2.Final&lt;/code&gt;, because of a different bug, the hash change fails to be applied to the in-memory database, so the side effect is avoided at the cost of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reference*&lt;/code&gt; upgraders not being present in this version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-mitigation&quot;&gt;&lt;a id=&quot;mitigation&quot;&gt;&lt;/a&gt; Mitigation&lt;/h2&gt;

&lt;p&gt;If you are affected, you can mitigate the bug using the following steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Back up your Apicurio Registry data by either backing up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic directly, or by using the export-import feature.&lt;/li&gt;
  &lt;li&gt;Disable compaction of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic.&lt;/li&gt;
  &lt;li&gt;Avoid using features that rely on &lt;em&gt;canonical content hash&lt;/em&gt;, such as:
    &lt;ul&gt;
      &lt;li&gt;Searching for artifacts using canonical content (&lt;em&gt;POST&lt;/em&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/search/artifacts&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonical=true&lt;/code&gt; query parameter),&lt;/li&gt;
      &lt;li&gt;Searching artifact metadata by canonical content (&lt;em&gt;POST&lt;/em&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/{groupId}/artifacts/{artifactId}/meta&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonical=true&lt;/code&gt;), or&lt;/li&gt;
      &lt;li&gt;Skipping updating of an artifact on duplicate content (&lt;em&gt;POST&lt;/em&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/groups/{groupId}/artifacts&lt;/code&gt;, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifExists=RETURN_OR_UPDATE&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonical=true&lt;/code&gt;);&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;with Protobuf artifacts that have references, unless your Apicurio Registry version is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.2.Final&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Do not restart or upgrade Apicurio Registry (if possible), until a fixed version is released &lt;a href=&quot;#note-11&quot;&gt;[11]&lt;/a&gt;, and then upgrade directly to the fixed version, skipping any intermediate versions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;

&lt;p&gt;We have released a fixed version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.9.Final&lt;/code&gt;🎉. Please update your deployment directly to this version.&lt;/p&gt;

&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;/h2&gt;

&lt;p&gt;&lt;a id=&quot;note-1&quot;&gt;&lt;/a&gt; &lt;strong&gt;[1]&lt;/strong&gt; As of the writing of this article, latest Apicurio Registry version is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.8.Final&lt;/code&gt;. &lt;a href=&quot;#note-1-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-2&quot;&gt;&lt;/a&gt; &lt;strong&gt;[2]&lt;/strong&gt; If you come across a bug that you suspect is related to KafkaSQL, I have written a &lt;a href=&quot;https://www.apicur.io/registry/docs/apicurio-registry/2.5.x/getting-started/guide-exporting-registry-kafka-topic-data.html&quot;&gt;guide on how to generate a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic dump&lt;/a&gt; that could help us during investigation. &lt;a href=&quot;#note-2-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-3&quot;&gt;&lt;/a&gt; &lt;strong&gt;[3]&lt;/strong&gt; Ignoring multitenancy for simplicity. &lt;a href=&quot;#note-3-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-4&quot;&gt;&lt;/a&gt; &lt;strong&gt;[4]&lt;/strong&gt; Commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;42b30af2c4&lt;/code&gt;, PR #1973. &lt;a href=&quot;#note-4-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-5&quot;&gt;&lt;/a&gt; &lt;strong&gt;[5]&lt;/strong&gt; Commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;03654bb492&lt;/code&gt;, PR #2005. &lt;a href=&quot;#note-5-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-6&quot;&gt;&lt;/a&gt; &lt;strong&gt;[6]&lt;/strong&gt; Commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dd885c3aae&lt;/code&gt;, PR #2366. &lt;a href=&quot;#note-6-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-7&quot;&gt;&lt;/a&gt; &lt;strong&gt;[7]&lt;/strong&gt; Commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9628c43b45&lt;/code&gt;, PR #3228. &lt;a href=&quot;#note-7-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-8&quot;&gt;&lt;/a&gt; &lt;strong&gt;[8]&lt;/strong&gt; Commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6dac98fc8a&lt;/code&gt;, PR #3300. &lt;a href=&quot;#note-8-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-9&quot;&gt;&lt;/a&gt; &lt;strong&gt;[9]&lt;/strong&gt; Commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;67065bfc53&lt;/code&gt;, PR #3423. &lt;a href=&quot;#note-9-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-10&quot;&gt;&lt;/a&gt; &lt;strong&gt;[10]&lt;/strong&gt; This is an illustrative example, not the actual topic dump provided by the user. &lt;a href=&quot;#note-10-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-11&quot;&gt;&lt;/a&gt; &lt;strong&gt;[11]&lt;/strong&gt; We have released a fixed version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5.9.Final&lt;/code&gt;🎉. &lt;a href=&quot;#note-11-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;note-12&quot;&gt;&lt;/a&gt; &lt;strong&gt;[12]&lt;/strong&gt; To do this, we create the topic manually before starting Apicurio Registry, using the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bin/kafka-topics.sh --bootstrap-server localhost:9092 \
    --create --topic kafkasql-journal --partitions 1 --replication-factor 1 \
    --config min.cleanable.dirty.ratio=0.000001 --config cleanup.policy=compact \
    --config segment.ms=100 --config delete.retention.ms=100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the topic is small, we might also need to add additional messages to the topic, since active segments are not eligible for compaction. &lt;a href=&quot;#note-12-back&quot;&gt;[Go back]&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Thank you for using and supporting Apicurio projects! As always, if you have any suggestions or encounter any problem, feel free to contact the team by &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues&quot;&gt;filing an issue in GitHub&lt;/a&gt;.&lt;/p&gt;
</description><category term="blog" /><category term="registry" /><category term="kafkasql" /><pubDate>Thu, 08 Feb 2024 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2024/02/08/resolving-a-bug-in-apicurio-registry-kafkasql-storage-upgrade-process</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2024/02/08/resolving-a-bug-in-apicurio-registry-kafkasql-storage-upgrade-process</guid>
        </item><item>
            <title>Securing Apicurio Registry 2.4.x using Azure Entra ID</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;Hey everyone, in recent Apicurio Registry versions we’ve introduced support for securing the application using different OpenID Connect (OIDC) servers, and not just Keycloak. In this blog post, I will explain how to configure the application to secure it using Microsoft Azure Entra ID.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;core-features&quot;&gt;Core Features&lt;/h1&gt;
&lt;p&gt;In this blog post, we will cover the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Authentication based on Azure Entra ID&lt;/strong&gt; - optionally protect Apicurio Registry so that the registry API and web console require users to authenticate (OAuth Authorization Code Flow supported)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;azure-entra-id-configuration&quot;&gt;Azure Entra ID Configuration&lt;/h1&gt;

&lt;h3 id=&quot;authentication-based-on-azure-ad&quot;&gt;Authentication based on Azure AD&lt;/h3&gt;
&lt;p&gt;In order to secure Apicurio Registry you will need a valid directory in Azure and some specific configuration (described below).
Essentially, you must register the Apicurio Registry application in the Azure portal. Log in to the &lt;a href=&quot;https://portal.azure.com&quot;&gt;Azure Portal&lt;/a&gt;. You can use your personal email address or your GitHub account to log in.
After logging in, navigate to the Azure AD control panel by using the menu in the top-left corner. It should look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/azure-ad-portal.png&quot; alt=&quot;Azure AD Portal&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s dig into the Azure AD configuration for your Apicurio Registry deployment. Select &lt;strong&gt;Manage &amp;gt; App registrations&lt;/strong&gt; in the menu on the left. Select &lt;strong&gt;New registration&lt;/strong&gt;, and fill in the form. Enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry-example&lt;/code&gt; as the application name. We’ll also allow users from any organizational directory to log in.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/azure-ad-register-application.png&quot; alt=&quot;Azure AD App Registration&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Register the host of the server hosting your Apicurio Registry application as a redirect URI. As part of the logon process, users will be redirected from our application to Azure AD for authentication. We want to send them back to our application afterwards. Azure AD will not allow any redirect URLs that are not registered. We’ll come back to this later.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Register&lt;/strong&gt;. You should now be able to find the app registration by selecting &lt;strong&gt;Manage &amp;gt; App registrations&lt;/strong&gt; in the menu on the left.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/azure-ad-app-registered.png&quot; alt=&quot;Azure AD App Registered&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can now find the parameters we need to set up Apicurio Registry with Azure AD OIDC. Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry-example&lt;/code&gt; to display its details:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/azure-ad-app-details.png&quot; alt=&quot;Azure AD App Details&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Manage &amp;gt; Authentication&lt;/strong&gt; to configure the application with the redirect URLs and token as follows:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/azure-ad-app-configuration.png&quot; alt=&quot;Azure AD App Details&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To configure Apicurio Registry with Azure AD, you must configure the following environment variables in Apicurio Registry using the Azure AD Application ID and the Azure AD Directory ID, along with some Apicurio Registry-specific configuration:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;REGISTRY_AUTH_ENABLED=true
KEYCLOAK_API_CLIENT_ID=459569e9-c5f7-410a-a6e7-8db28d7e3647 #Azure AD &amp;gt; Admin &amp;gt; App registrations &amp;gt; Your app &amp;gt; Application (client) ID
REGISTRY_UI_AUTH_TYPE=oidc
REGISTRY_AUTH_URL_CONFIGURED=https://login.microsoftonline.com/6f9ef45b-456d-49e3-b5ba-1f6fe4c0fb78/v2.0 #Azure AD &amp;gt; Admin &amp;gt; App registrations &amp;gt; Your app &amp;gt; Directory (tenant) ID
REGISTRY_OIDC_UI_CLIENT_ID=459569e9-c5f7-410a-a6e7-8db28d7e3647 #Azure AD &amp;gt; Admin &amp;gt; App registrations &amp;gt; Your app &amp;gt; Application (client) ID
CORS_ALLOWED_ORIGINS=https://test-registry.com #The host for your Apicurio Registry deployment
REGISTRY_OIDC_UI_REDIRECT_URL=https://test-registry.com/ui/ #The host for your Apicurio Registry console
ROLE_BASED_AUTHZ_ENABLED=true
QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH=roles
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;role-based-authorization&quot;&gt;Role based authorization&lt;/h3&gt;

&lt;p&gt;To enable roles, you must set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ROLE_BASED_AUTHZ_ENABLED&lt;/code&gt; property to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Of course, if you enable roles in Apicurio Registry, you must also create them in Azure AD.
You must create them as Application roles. The default roles expected by Apicurio Registry are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-admin&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-developer&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-readonly&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another extremely important configuration is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH=true&lt;/code&gt; because Azure AD stores the roles in a claim called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;client-credentials-authentication&quot;&gt;Client credentials authentication&lt;/h3&gt;
&lt;p&gt;Apart from having users accessing the UI, it is quite frequent to have the requirement of doing machine to machine authentication, especially in the context of something like Apache Kafka.
To do this, you can configure your setup to use the &lt;em&gt;Client Credentials Grant&lt;/em&gt; using Entra ID. This can be achieved with the following steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a new app registration that represents your client application. You can leave everything blank in the creation screen besides the name.&lt;/li&gt;
  &lt;li&gt;In the Overview tab of your new application registration, create a new secret.&lt;/li&gt;
  &lt;li&gt;Add a new API Permission to your client application, and, inside this option, add the role you want to use. This role should have been created in the last section as an Application role in the Registry server app registration. This is done so the role is sent within the token.&lt;/li&gt;
  &lt;li&gt;In your client identifying the application &lt;em&gt;and&lt;/em&gt; in the one identifying the Registry server, in the API permissions section, you must grant admin consent for your directory. Note that it may take a few seconds for this to take effect.&lt;/li&gt;
  &lt;li&gt;Finally, configure your producer as follows, but note that the values here are just examples:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Configure kafka settings&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;BOOTSTRAP_SERVERS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SERVERS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;CLIENT_ID_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Producer-&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACKS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;all&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;KEY_SERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringSerializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;VALUE_SERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AvroKafkaSerializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTO_REGISTER_ARTIFACT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//Security related properties&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTH_CLIENT_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CLIENT_SECRET&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//Here you must use the client secret of your client application created in step 2 above.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTH_CLIENT_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;c9715ca2-de14-4484-8b39-95f10050b099&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//This is the client ID of your client application.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SerdeConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTH_TOKEN_ENDPOINT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://login.microsoftonline.com/6f9ef45b-456d-49e3-b5ba-2f6fe4c0fb78/oauth2/v2.0/token&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//This is the token endpoint, the UUID is your tenant ID from Entra ID.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SchemaResolverConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTH_CLIENT_SCOPE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;api://frr5bc25-b280-4def-91ff-179d6c3b9d4f/.default&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//Finally, this is the expected value structure for the scope. The UUID is the application ID of the app registration that represents your Apicurio Registry server.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Create the Kafka producer&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaProducer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The result of the code snippet above is a Kafka producer that uses  the AvroKafkaSerializer provided by the Apicurio Registry team, integrated with Entra ID.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;Last, but not least, we still have a lot of things to do!  As always, you can see the
stuff we’re tracking by viewing the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues&quot;&gt;GitHub issues&lt;/a&gt; for the project.&lt;/p&gt;

&lt;p&gt;If you find bugs or want to request a new feature, that’s a great place to start!&lt;/p&gt;
</description><category term="blog" /><category term="registry" /><category term="security" /><pubDate>Thu, 13 Jul 2023 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2023/07/13/registry-azure-ad</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2023/07/13/registry-azure-ad</guid>
        </item><item>
            <title>Resolving a bug in KafkaSQL storage for Apicurio Registry</title><dc:creator>Jakub Senko</dc:creator>
            <author>jsenko@redhat.com</author><description>&lt;p&gt;One of the major changes introduced in Apicurio Registry &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.x&lt;/code&gt; were improvements in the supported storage options.
We have kept the possibility of using both PostgreSQL database and Kafka to store your data, but the internals of the 
storage options have been improved, and in some ways simplified.&lt;/p&gt;

&lt;p&gt;We have received great feedback from the users of the new KafkaSQL storage option, but recently several users have 
reported an interesting bug, that seems to only affect users with the KafkaSQL persistence enabled.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The following error appears when a user attempts to update or create a new artifact:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/registry-storage-kafkasql-error-ui.png&quot; alt=&quot;KafkaSQL error in UI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The first time that one of the users saw this error, they were able to resolve it by deleting the Kafka topic
where the Apicurio Registry stores its data. However, the error started appearing again
after some time. We have investigated the problem, found the cause, and would
like to take this opportunity to share the details with the Apicurio community.&lt;/p&gt;

&lt;p&gt;In this blog post, we will take a look at the KafkaSQL storage option in more detail. 
We’ll describe the bug and the solution, and provide a temporary workaround for users 
until they are able to upgrade to the patched version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.1.4.Final&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;kafkasql-storage&quot;&gt;KafkaSQL Storage&lt;/h1&gt;

&lt;p&gt;A major use-case for Apicurio Registry is to manage and distribute schemas for messages in Kafka topics. 
This means that a lot of our users have a Kafka cluster available, so supporting it as a storage option has been a priority for us.&lt;/p&gt;

&lt;p&gt;KafkaSQL storage shares common logic with the standard SQL storage option, 
but does not use a central SQL database to store Apicurio Registry data.
Instead, each node uses a local SQL database. To keep the replicas in sync, each change in one of the
local databases is replicated to the other nodes in the cluster using a Kafka topic.
This makes the read operations on the data straightforward, and allows us to keep the commonality with the SQL persistence option.&lt;/p&gt;

&lt;p&gt;The design of the Kafka messages closely follows the tables that are used both in the local SQL database, 
and the database for the standard SQL storage.&lt;/p&gt;

&lt;p&gt;Each Kafka message has a key and a corresponding value. Kafka itself is agnostic about their content, and treats them as a byte array.
In the KafkaSQL storage, they are represented by objects that are serialized into a JSON String, 
prepended with marker bytes used to recognize message type.&lt;/p&gt;

&lt;p&gt;In general, each message value class has a corresponding key class. 
Classes for the keys are mainly responsible for determining the partition that the message is going to reside in, 
the tenant information in case of a multi-tenant deployment, and identifying information for the object that the message value represents. 
For example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArtifactVersionKey&lt;/code&gt; contains group ID, artifact ID and version identifiers for the given artifact version object.
Choice of the partition key is important for maintaining consistency of the data between nodes, 
because ordering of the messages is only guaranteed in the same partition.&lt;/p&gt;

&lt;p&gt;Classes for the values contain a field with the operation that the message represents, which includes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPDATE&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt;.
The other fields carry the rest of the information, such as (for example) the editable metadata for an artifact version.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/registry-storage-kafkasql-diagram.svg&quot; alt=&quot;KafkaSQL Storage Diagram&quot; /&gt;
&lt;em&gt;KafkaSQL storage option workflow diagram&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a change to a piece of data occurs, the originating node creates and sends the message to the Kafka topic,
and blocks the user thread until the operation is processed. 
Its local database is not modified directly. Each node only applies changes that are read form the Kafka topic.
Therefore, the user thread is unblocked only after the original message is consumed from the topic by the node.
This ensures that in case of a composite operation, messages are always processed in the correct order,
and critically in the &lt;em&gt;same&lt;/em&gt; order for each node.&lt;/p&gt;

&lt;p&gt;For example, when adding a new artifact version, the user may store a new content that is not already
present in the storage. Therefore, the operation usually consists of two messages that have to be kept in the correct order,
or the artifact version wouldn’t be able to reference the new content.&lt;/p&gt;

&lt;p&gt;An important thing to note is that when a node is restarted, and its local SQL database is empty,
simply reading all the messages from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; will replicate the data that is already present on other live nodes.
This works even when the entire Apicurio Registry application is stopped and restarted,
as long as the data in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic remains persisted and backed up.&lt;/p&gt;

&lt;h1 id=&quot;investigation&quot;&gt;Investigation&lt;/h1&gt;

&lt;p&gt;In order to investigate the issue, we have to first attempt to reproduce the issue 
on a local Apicurio Registry deployment with KafkaSQL storage.
The following is a list of steps we have performed.&lt;/p&gt;

&lt;p&gt;First, a local Kafka instance must be downloaded, and then we can run it:&lt;/p&gt;

&lt;p&gt;Start Zookeper:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bin/zookeeper-server-start.sh config/zookeeper.properties
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and the start Kafka:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bin/kafka-server-start.sh config/server.properties
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have been able to get the contents of the Kafka topics from one the users who reported the issue.
They used Kafkacat tool to export the data. An example output of Kafkacat looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;
&lt;div class=&quot;highlight&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;
&lt;code class=&quot;wide&quot;&gt;
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:0,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230546039,&quot;headers&quot;:[&quot;req&quot;,&quot;ef7fd699-2311-4ba6-9d5a-f9087815eb83&quot;],&quot;key&quot;:&quot;\u0000{\&quot;bootstrapId\&quot;:\&quot;24240819-0738-49a7-8ab2-2b971176b0e1\&quot;}&quot;,&quot;payload&quot;:null}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:1,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230571121,&quot;headers&quot;:[&quot;req&quot;,&quot;9520eb8f-23a4-40f4-99a0-9d917406f700&quot;],&quot;key&quot;:&quot;\u0001{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;ruleType\&quot;:\&quot;VALIDITY\&quot;}&quot;,&quot;payload&quot;:&quot;\u0001{\&quot;action\&quot;:\&quot;CREATE\&quot;,\&quot;config\&quot;:{\&quot;configuration\&quot;:\&quot;FULL\&quot;}}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:3,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230581768,&quot;headers&quot;:[&quot;req&quot;,&quot;2a897033-111e-4fdf-a3cc-02941a9db84f&quot;],&quot;key&quot;:&quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;377072c58f3999d1e42455c1cf2563d4e5a367c3ee66a5be41ac376902183bc4\&quot;,\&quot;contentId\&quot;:1}&quot;,&quot;payload&quot;:&quot;\u0002\u0001\u0000\u0000\u0000@9e15d06c0aa184746bc092841d0a0efd0ce276c5d36873bf471482e3401e92ab\u0000\u0000\u0000�{\n  \&quot;type\&quot;: \&quot;record\&quot;,\n  \&quot;name\&quot;: \&quot;User1\&quot;,\n  \&quot;namespace\&quot;: \&quot;foo\&quot;,\n  \&quot;fields\&quot;: [\n    { \&quot;name\&quot;: \&quot;active1\&quot;, \&quot;type\&quot;: [\&quot;null\&quot;, \&quot;boolean\&quot;] }\n  ]\n}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:5,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230581843,&quot;headers&quot;:[&quot;req&quot;,&quot;2dd94bfb-34a1-4c23-b93c-54242f94aba4&quot;],&quot;key&quot;:&quot;\u0003{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;artifactId\&quot;:\&quot;d0c00bec-3e4a-4386-ad67-b23b5a91ce96\&quot;,\&quot;uuid\&quot;:\&quot;6b50bb64-65ba-4ed8-beaf-deea49a7e610\&quot;}&quot;,&quot;payload&quot;:&quot;\u0003{\&quot;action\&quot;:\&quot;CREATE\&quot;,\&quot;metaData\&quot;:{\&quot;name\&quot;:\&quot;User1\&quot;},\&quot;globalId\&quot;:1,\&quot;artifactType\&quot;:\&quot;AVRO\&quot;,\&quot;contentHash\&quot;:\&quot;377072c58f3999d1e42455c1cf2563d4e5a367c3ee66a5be41ac376902183bc4\&quot;,\&quot;createdBy\&quot;:\&quot;\&quot;,\&quot;createdOn\&quot;:1637230581817}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:6,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230593970,&quot;headers&quot;:[&quot;req&quot;,&quot;9a34b683-02af-4360-8617-1fc5f17c6509&quot;],&quot;key&quot;:&quot;\t{\&quot;tenantId\&quot;:\&quot;_\&quot;}&quot;,&quot;payload&quot;:&quot;\t{\&quot;action\&quot;:\&quot;CREATE\&quot;}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:7,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230593993,&quot;headers&quot;:[&quot;req&quot;,&quot;3d3beae5-6561-4b42-9731-c7c6c74ce5c7&quot;],&quot;key&quot;:&quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;edc351831a60539d904157020074ef1043868247fb8641e5cf4356407245dd32\&quot;,\&quot;contentId\&quot;:2}&quot;,&quot;payload&quot;:&quot;\u0002\u0001\u0000\u0000\u0000@e200bb1898f5ccee20f649e98097306fb72a8b32ecf9db64261d13387730dc95\u0000\u0000\u0000�{\n  \&quot;type\&quot;: \&quot;record\&quot;,\n  \&quot;name\&quot;: \&quot;User2\&quot;,\n  \&quot;namespace\&quot;: \&quot;foo\&quot;,\n  \&quot;fields\&quot;: [\n    { \&quot;name\&quot;: \&quot;active2\&quot;, \&quot;type\&quot;: [\&quot;null\&quot;, \&quot;boolean\&quot;] }\n  ]\n}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:8,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230594011,&quot;headers&quot;:[&quot;req&quot;,&quot;6485fb14-313d-42c2-891b-5b80a6f7b244&quot;],&quot;key&quot;:&quot;\b{\&quot;tenantId\&quot;:\&quot;_\&quot;}&quot;,&quot;payload&quot;:&quot;\b{\&quot;action\&quot;:\&quot;CREATE\&quot;}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:9,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230594026,&quot;headers&quot;:[&quot;req&quot;,&quot;8f807fb9-d581-4976-8750-b3d3d80df333&quot;],&quot;key&quot;:&quot;\u0003{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;artifactId\&quot;:\&quot;0b9c15b5-54a5-40d3-9b75-433070d6ad99\&quot;,\&quot;uuid\&quot;:\&quot;b2c6e59d-b9b7-4be1-84a9-9d0dd907466a\&quot;}&quot;,&quot;payload&quot;:&quot;\u0003{\&quot;action\&quot;:\&quot;CREATE\&quot;,\&quot;metaData\&quot;:{\&quot;name\&quot;:\&quot;User2\&quot;},\&quot;globalId\&quot;:2,\&quot;artifactType\&quot;:\&quot;AVRO\&quot;,\&quot;contentHash\&quot;:\&quot;edc351831a60539d904157020074ef1043868247fb8641e5cf4356407245dd32\&quot;,\&quot;createdBy\&quot;:\&quot;\&quot;,\&quot;createdOn\&quot;:1637230594011}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:10,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230608757,&quot;headers&quot;:[&quot;req&quot;,&quot;6ec25f20-eb62-4914-8e8b-97e9c7fa29c7&quot;],&quot;key&quot;:&quot;\t{\&quot;tenantId\&quot;:\&quot;_\&quot;}&quot;,&quot;payload&quot;:&quot;\t{\&quot;action\&quot;:\&quot;CREATE\&quot;}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:11,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230608773,&quot;headers&quot;:[&quot;req&quot;,&quot;b68d11b9-67fd-427a-a167-e5c438c8334f&quot;],&quot;key&quot;:&quot;\u0002{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;contentHash\&quot;:\&quot;b11088caecbc3fcd799b2a289f85502b6c0d00d8113b0fb51789528863e6c0e1\&quot;,\&quot;contentId\&quot;:3}&quot;,&quot;payload&quot;:&quot;\u0002\u0001\u0000\u0000\u0000@06f0fb80396e5f02a4a5f3d96ba5da6af35ca33ec1251c58698170283d0dd37a\u0000\u0000\u0000�{\n  \&quot;type\&quot;: \&quot;record\&quot;,\n  \&quot;name\&quot;: \&quot;User3\&quot;,\n  \&quot;namespace\&quot;: \&quot;foo\&quot;,\n  \&quot;fields\&quot;: [\n    { \&quot;name\&quot;: \&quot;active3\&quot;, \&quot;type\&quot;: [\&quot;null\&quot;, \&quot;boolean\&quot;] }\n  ]\n}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:12,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230608792,&quot;headers&quot;:[&quot;req&quot;,&quot;e69c6a1b-7df1-40cd-a640-ac9b3716c687&quot;],&quot;key&quot;:&quot;\b{\&quot;tenantId\&quot;:\&quot;_\&quot;}&quot;,&quot;payload&quot;:&quot;\b{\&quot;action\&quot;:\&quot;CREATE\&quot;}&quot;}
{&quot;topic&quot;:&quot;kafkasql-journal&quot;,&quot;partition&quot;:0,&quot;offset&quot;:13,&quot;tstype&quot;:&quot;create&quot;,&quot;ts&quot;:1637230608807,&quot;headers&quot;:[&quot;req&quot;,&quot;6875bbc9-92fe-4572-accc-7dab2bdcd84e&quot;],&quot;key&quot;:&quot;\u0003{\&quot;tenantId\&quot;:\&quot;_\&quot;,\&quot;artifactId\&quot;:\&quot;447e73bf-e86b-480b-8e45-3222c0980bc9\&quot;,\&quot;uuid\&quot;:\&quot;a5fa1ba0-f40a-4591-91c6-a6cb422f2f4a\&quot;}&quot;,&quot;payload&quot;:&quot;\u0003{\&quot;action\&quot;:\&quot;CREATE\&quot;,\&quot;metaData\&quot;:{\&quot;name\&quot;:\&quot;User3\&quot;},\&quot;globalId\&quot;:3,\&quot;artifactType\&quot;:\&quot;AVRO\&quot;,\&quot;contentHash\&quot;:\&quot;b11088caecbc3fcd799b2a289f85502b6c0d00d8113b0fb51789528863e6c0e1\&quot;,\&quot;createdBy\&quot;:\&quot;\&quot;,\&quot;createdOn\&quot;:1637230608791}&quot;}
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;You can get a Kafka topic dump using Kafkacat directly, however it’s possible to run it via Docker like this:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PWD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;host &lt;span class=&quot;nt&quot;&gt;--volume&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;:/data edenhill/kafkacat:1.5.0 kafkacat &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; localhost:9092 &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; kafkasql-journal &lt;span class=&quot;nt&quot;&gt;-Z&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-D&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;n &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-J&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now import the sample topic dump into our local Kafka cluster. 
However, we were not able to do it using Kafkacat directly, because the data is wrapped 
in an &lt;em&gt;envelope&lt;/em&gt; with additional metadata. 
Therefore, we can use one of the example projects from &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry-examples&quot;&gt;Apicurio Registry Examples&lt;/a&gt; 
to create a simple application to preprocess and load the data into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic.
The preprocessing mostly consists of removing the metadata envelope and producing clean messages.
As a note, it is important to use correct encoding, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StandardCharsets.US_ASCII&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UTF&lt;/code&gt; when reading the dump file.
Now, we can run the local Registry with the data the user has provided.&lt;/p&gt;

&lt;p&gt;First, we build Apicurio Registry with KafkaSQL storage:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mvn clean &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Pprod&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Pkafkasql&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-DskipTests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and then create an environment variable representing the project root:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To see how the data are being processed and stored, we need to have access to the local database. 
By default, it’s an in-memory h2 database, but we’ll use a file-based configuration,
to which we can easily connect from an IDE.
To achieve this, we will override the default value of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quarkus.datasource.jdbc.url&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Run Apicurio Registry instance with the debugger enabled:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;java &lt;span class=&quot;nt&quot;&gt;-Xdebug&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Xrunjdwp&lt;/span&gt;:transport&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dt_socket,server&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;y,suspend&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;n,address&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5005 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-Dquarkus&lt;/span&gt;.http.port&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;8180 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-Dquarkus&lt;/span&gt;.datasource.jdbc.url&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;jdbc:h2:file:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PROJECT&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/registry_database;MV_STORE=FALSE;AUTO_SERVER=TRUE&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; ./storage/kafkasql/target/apicurio-registry-storage-kafkasql-2.1.3-SNAPSHOT-runner.jar | &lt;span class=&quot;nb&quot;&gt;tee &lt;/span&gt;log.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we can connect both the debugger and database client to the application from our IDE.&lt;/p&gt;

&lt;p&gt;You can use the following URL, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sa&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sa&lt;/code&gt; as the username/password options:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jdbc:h2:file:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PROJECT&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/registry_database;MV_STORE=FALSE;AUTO_SERVER=TRUE&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After loading the data from the Kafka topic into the local database and setting up debugging,
we can inspect the contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ARTIFACTS&lt;/code&gt; and then the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONTENT&lt;/code&gt; tables:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+---------+--------------+-------------------------------------+-----+----------+---------------------------+-------+
|TENANTID |GROUPID       |ARTIFACTID                           |TYPE |CREATEDBY |CREATEDON                  |LATEST |
+---------+--------------+-------------------------------------+-----+----------+---------------------------+-------+
|_        |__$GROUPID$__ |d0c00bec-3e4a-4386-ad67-b23b5a91ce96 |AVRO |          |2021-11-18 11:16:21.817000 |1      |
+---------+--------------+-------------------------------------+-----+----------+---------------------------+-------+
|_        |__$GROUPID$__ |0b9c15b5-54a5-40d3-9b75-433070d6ad99 |AVRO |          |2021-11-18 11:16:34.011000 |2      |
+---------+--------------+-------------------------------------+-----+----------+---------------------------+-------+
|_        |__$GROUPID$__ |447e73bf-e86b-480b-8e45-3222c0980bc9 |AVRO |          |2021-11-18 11:16:48.791000 |3      |
+---------+--------------+-------------------------------------+-----+----------+---------------------------+-------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;ARTIFACTS table&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+---------+----------+----------------+----------------+------------------+
|TENANTID |CONTENTID |CANONICALHASH   |CONTENTHASH     |CONTENT           |
+---------+----------+----------------+----------------+------------------+
|_        |1         |9e15d06c0aa1... |377072c58f39... |{                 |
|         |          |                |                |&quot;type&quot;: &quot;record&quot;, |
|         |          |                |                |&quot;name&quot;: &quot;User1&quot;,  |
|         |          |                |                | ...              |
|         |          |                |                |}                 |
+---------+----------+----------------+----------------+------------------+
|_        |2         |e200bb1898f5... |edc351831a60... |{                 |
|         |          |                |                |&quot;type&quot;: &quot;record&quot;, |
|         |          |                |                |&quot;name&quot;: &quot;User2&quot;,  |
|         |          |                |                | ...              |
|         |          |                |                |}                 |
+---------+----------+----------------+----------------+------------------+
|_        |3         |06f0fb80396e... |b11088caecbc... |{                 |
|         |          |                |                |&quot;type&quot;: &quot;record&quot;, |
|         |          |                |                |&quot;name&quot;: &quot;User3&quot;,  |
|         |          |                |                | ...              |
|         |          |                |                |}                 |
+---------+----------+----------------+-----------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;CONTENT table&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The data has been loaded as expected, and the database content looks good, so we can try to add another artifact:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;record&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;namespace&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fields&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;active4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;boolean&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Sample Avro schema&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;and indeed we observe the error we have been attempting to reproduce.
The details of the exception are as follows, which corresponds to the reported issue:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Caused by: io.apicurio.registry.storage.impl.sql.jdb.RuntimeSqlException: SQL error: Expected only one result row but got none.
	at io.apicurio.registry.storage.impl.sql.jdb.MappedQueryImpl.one(MappedQueryImpl.java:66)
	at io.apicurio.registry.storage.impl.kafkasql.sql.KafkaSqlStore.lambda$contentIdFromHash$7(KafkaSqlStore.java:172)
	at io.apicurio.registry.storage.impl.sql.HandleFactory.withHandle(HandleFactory.java:44)
	at io.apicurio.registry.storage.impl.sql.HandleFactory.withHandleNoException(HandleFactory.java:52)
	...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What does it mean? When creating a new artifact, we use multiple messages to perform the necessary operations.
In the case of adding a new artifact, we first make sure that the content of the artifact is stored,
before writing meta-data to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ARTIFACTS&lt;/code&gt; table. This ensures that we do not store multiple copies of the same content.
The error tells us, that when we perform the second step, the content we expected is not present in the database,
even though the message responsible for adding it &lt;em&gt;must&lt;/em&gt; have been processed before the error occurs.&lt;/p&gt;

&lt;p&gt;There may be multiple reasons why this could happen, but after some investigation we noticed a suspicious content of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEQUENCES&lt;/code&gt;
table:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+--------+---------+-----+
|TENANTID|NAME     |VALUE|
+--------+---------+-----+
|_       |contentId|2    |
|_       |globalId |2    |
+--------+---------+-----+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The data is wrong, since after we loaded the original data, there were 3 artifacts in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONTENT&lt;/code&gt; table.
This means that the next contentId in the sequence is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;, but content with that ID already exists,
and it belongs to an artifact that has been created earlier.&lt;/p&gt;

&lt;p&gt;To find the answer to the corrupted data, we can take a look at the topic dump we have originally used and notice 
that the messages for some offsets are missing. Furthermore, it seems that those messages are exactly those that
cause the sequence numbers to be incremented.&lt;/p&gt;

&lt;p&gt;After some investigation, we determined that the missing messages may have been deleted by a feature of Kafka, called Topic Log Compaction. 
Our hypothesis therefore is, that the messages that should have be kept have been accidentally deleted in this way.
To find out more, we will describe how Kafka Topic Log Compaction works.&lt;/p&gt;

&lt;h1 id=&quot;kafka-topic-log-compaction&quot;&gt;Kafka Topic Log Compaction&lt;/h1&gt;

&lt;p&gt;Topic log compaction is a feature in Apache Kafka that allows users to conserve storage space for the topic,
by automatically removing old messages in the log. 
It works by considering a sequence of messages that share the same key, and only keeping the last message in the sequence.&lt;/p&gt;

&lt;p&gt;This, however, requires an assumption that any message for the given key contains all state that the application wants to keep.
This is not necessarily true for each application, or for all messages, therefore it is important to carefully configure this feature, 
and disable it if your application depends on keeping every message, even if the key is repeated.&lt;/p&gt;

&lt;p&gt;An additional feature is to also remove messages that have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; value, allowing you to delete the whole sequence for the given key
when the topic is compacted. These kinds of messages are called &lt;em&gt;tombstone messages&lt;/em&gt;. 
In Apicurio Registry, we use tombstone messages when a piece of data, such as an artifact, is deleted.
The reason is that the past operations on that piece of data do not need to be replicated to other nodes,
or preserved after restart.&lt;/p&gt;

&lt;p&gt;The most important Kafka properties for configuring log compaction are as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cleanup.policy=compact&lt;/code&gt; enables log compaction and activates the following properties.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;min.cleanable.dirty.ratio&lt;/code&gt; this ratio bounds the maximum space wasted in the log by duplicates,
until the log compactor will attempt to clean the log.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;min.compaction.lag.ms&lt;/code&gt; the minimum amount of time that has to pass since a message is written before it can be compacted.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete.retention.ms&lt;/code&gt; The amount of time to retain delete tombstone markers. If a consumer starts to read the topic from the beginning,
they need to finish reading in this time if it wants to make sure it sees the markers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are using the Strimzi Operator to provision and manage your Kafka cluster on Kubernetes, 
you can provide these configuration options on a cluster or topic level (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kafka&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaTopic&lt;/code&gt; CRD) in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config&lt;/code&gt; section:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;

&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kafka.strimzi.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;KafkaTopic&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kafkasql-journal&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;strimzi.io/cluster&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-cluster&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-example-kafkasql&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;partitions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cleanup.policy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;compact&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;min.cleanable.dirty.ratio&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Strimzi Kafka CRD example&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: If you have not created the topic manually, Apicurio Registry will create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; automatically.
Make sure you create a CDR for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic, so you can manage it with Strimzi.&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;reproducer&quot;&gt;Reproducer&lt;/h1&gt;

&lt;p&gt;Let’s create a procedure to attempt to reproduce the issue and confirm our hypothesis. 
We are assuming that the Kafka cluster and the Apicurio Registry executable is already prepared by following steps in the previous sections.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Stop the Registry process.&lt;/li&gt;
  &lt;li&gt;Remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic, if it exists.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create the topic manually with aggressive log compaction settings:&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; bin/kafka-topics.sh &lt;span class=&quot;nt&quot;&gt;--bootstrap-server&lt;/span&gt; localhost:9092 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--create&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--topic&lt;/span&gt; kafkasql-journal &lt;span class=&quot;nt&quot;&gt;--partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;--replication-factor&lt;/span&gt; 1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; min.cleanable.dirty.ratio&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.000001 &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; cleanup.policy&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;compact &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; segment.ms&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;100 &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; delete.retention.ms&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create at least 3 AVRO schemas, for example:&lt;/p&gt;

    &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;record&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;namespace&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fields&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;active1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;boolean&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;&lt;em&gt;Sample Avro schema&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Restart the registry, so it will reload messages from the topic into its local database. 
Remove the database file if you are using the debug settings from earlier.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Add another AVRO schema to the Registry and observe the error.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;solution&quot;&gt;Solution&lt;/h1&gt;

&lt;p&gt;In order to fix the bug, we have to prevent messages, which control the database sequence, from being compacted. 
We are already doing this for other types of messages, by making sure that the key value is always unique. 
We can do this by including a sufficiently random nonce, such as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UUID&lt;/code&gt;, in the message key.&lt;/p&gt;

&lt;p&gt;The following is an example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentId&lt;/code&gt; message, that increments a sequence for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONTENT&lt;/code&gt; table, before the fix has been implemented:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;kafkasql-journal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;partition&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tstype&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;create&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1638894723320&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;headers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;req&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;9dff3fb8-7a1d-41b0-9083-684d9fbfa619&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tenantId&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;payload&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the same message after the bugfix:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;kafkasql-journal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;partition&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tstype&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;create&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1638894723320&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;headers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;req&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;9dff3fb8-7a1d-41b0-9083-684d9fbfa619&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tenantId&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;6875bbc9-92fe-4572-accc-7dab2bdcd84e&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;payload&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;temporary-resolution-for-older-apicurio-registry-versions&quot;&gt;Temporary Resolution for Older Apicurio Registry Versions&lt;/h1&gt;

&lt;p&gt;This issue has already been fixed in Apicurio Registry &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.1.4.Final&lt;/code&gt;. If you are not able to upgrade, follow this procedure:&lt;/p&gt;

&lt;p&gt;In order to make sure you are not affected by this bug, you need to disable log compaction 
for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql-journal&lt;/code&gt; topic in your cluster.
First, check if it is enabled:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bin/kafka-configs.sh &lt;span class=&quot;nt&quot;&gt;--describe&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--bootstrap-server&lt;/span&gt; localhost:9092 &lt;span class=&quot;nt&quot;&gt;--topic&lt;/span&gt; kafkasql-journal 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cleanup.policy=compact&lt;/code&gt;, you are affected. To disable log compaction, you need to switch to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete&lt;/code&gt;
cleanup policy. However, in case of Apicurio Registry, deleting old messages will result in data corruption. 
Therefore, you &lt;em&gt;must&lt;/em&gt; also prevent messages to be actually deleted, by setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retention.bytes=-1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retention.ms=-1&lt;/code&gt; 
(or their server-wide equivalents &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log.retention.bytes&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log.retention.ms&lt;/code&gt;). For example:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bin/kafka-configs.sh &lt;span class=&quot;nt&quot;&gt;--alter&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--bootstrap-server&lt;/span&gt; localhost:9092 &lt;span class=&quot;nt&quot;&gt;--topic&lt;/span&gt; kafkasql-journal &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--add-config&lt;/span&gt; cleanup.policy&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;delete &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--add-config&lt;/span&gt; retention.bytes&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--add-config&lt;/span&gt; retention.ms&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are using Strimzi, you can apply these settings in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config&lt;/code&gt; section of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KafkaTopic&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kafka&lt;/code&gt; CRD:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kafka.strimzi.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;KafkaTopic&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kafkasql-journal&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;strimzi.io/cluster&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-cluster&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-example-kafkasql&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;partitions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cleanup.policy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;delete&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;retention.bytes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;retention.ms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Strimzi KafkaTopic CRD example&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kafka.strimzi.io/v1beta2&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kafka&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-cluster&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry-example-kafkasql&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;kafka&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;log.cleanup.policy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;delete&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;log.retention.bytes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;log.retention.ms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2.7.0&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Strimzi Kafka CRD example&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, this configuration change does not fix the existing corrupted data in the database. 
Luckily, we can use the export-import feature of the Apicurio Registry to force a reset of the database sequences,
so the error no longer occurs. This will keep your existing data intact.&lt;/p&gt;

&lt;p&gt;To perform this reset, create an export file from an empty Apicurio Registry, using the following example command:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl localhost:8180/apis/registry/v2/admin/export &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; export.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and import it into the Apicurio Registry instance you want to fix:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PWD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;content-type: application/zip&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--data-binary&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/export.zip&quot;&lt;/span&gt; localhost:8180/apis/registry/v2/admin/import
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: If you do not want to provide your own export file, you can use &lt;a href=&quot;/files/export.zip&quot;&gt;this example one&lt;/a&gt;, 
generated from an empty Apicurio Registry instance, version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.1.3.Final&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Thank you for using and supporting Apicurio projects! As always, if you have any suggestions or encounter any problem, 
feel free to contact the team by &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues&quot;&gt;filing an issue in GitHub&lt;/a&gt;&lt;/p&gt;
</description><category term="blog" /><category term="registry" /><category term="kafkasql" /><pubDate>Thu, 09 Dec 2021 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2021/12/09/kafkasql-storage-and-security</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2021/12/09/kafkasql-storage-and-security</guid>
        </item><item>
            <title>Apicurio Registry 2.0.0.Final Migration Guide</title><dc:creator>Fabian Martinez Gonzalez</dc:creator>
            <author>famartin@redhat.com</author><description>&lt;p&gt;Apicurio Registry 2.x is our latest and greatest release to date. It has plenty of new features but it also comes with some breaking changes from the previous 1.3.x release. In this post we will explore the process to migrate data from Apicurio Registry 1.3.x to Apicurio Registry 2.x.&lt;/p&gt;

&lt;p&gt;Because of the breaking changes between 1.3.x and 2.x, there is no automatic upgrade and instead a migration process is required.  But don’t worry!  We promise the process is not hard.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;breaking-changes&quot;&gt;Breaking changes&lt;/h1&gt;

&lt;p&gt;There are three major changes in this release that must be taken into account to proceed with a migration.&lt;/p&gt;

&lt;h2 id=&quot;new-storage-options&quot;&gt;New storage options&lt;/h2&gt;
&lt;p&gt;This impacts your current Apicurio Registry deployment. In Apicurio Registry 1.3.x we had 3 storage options (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;streams&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jpa&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;infinispan&lt;/code&gt;).  Now in
Apicurio Registry 2.x we dropped all of them but provided new and improved alternatives that we believe will be better for the project in the long term.  These new storages allow for more robust and performant deployments while at the same time being more maintainable. The new storage options are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sql&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kafkasql&lt;/code&gt;).  There is a third one &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mem&lt;/code&gt; that is not suitable for production workloads. This document will not cover the process of deploying Apicurio Registry 2.x, you can find more details in the &lt;a href=&quot;https://www.apicur.io/registry/docs/apicurio-registry/2.0.0.Final/getting-started/assembly-installing-registry-storage-openshift.html&quot;&gt;Apicurio Registry user documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;new-rest-api&quot;&gt;New REST API&lt;/h2&gt;
&lt;p&gt;We changed the REST API layout to ensure better long term maintainability and because we refactored our API to have support for artifact groupings. Apicurio Registry still supports the original REST API (now known as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1&lt;/code&gt;) and the various compatibility APIs we already supported (for example, Confluent and IBM schema registry APIs).  Additionally, Apicurio Registry now also implements the Schema Registry spec provided in the CNCF Cloud Events spec.&lt;/p&gt;

&lt;h2 id=&quot;refactored-serdes-libraries&quot;&gt;Refactored SerDes libraries&lt;/h2&gt;
&lt;p&gt;The Serializer and Deserializer classes are now available as three different Maven modules (one for each data format). This makes it possible to use just the one you want without pulling in a bunch of transitive dependencies that you don’t care about.&lt;/p&gt;

&lt;h1 id=&quot;migrating-to-v2x&quot;&gt;Migrating to v2.x&lt;/h1&gt;

&lt;p&gt;The migration from Apicurio Registry 1.3.x to 2.x requires you to move the data in your existing registry to a new registry. You must also review your applications that interact with the registry and update their configuration to meet the new requirements.&lt;/p&gt;

&lt;h2 id=&quot;data-migration&quot;&gt;Data migration&lt;/h2&gt;

&lt;p&gt;The process of data migration to Apicurio Registry 2.x will require you to export all the data from your 1.3.x deployment and import it to the new 2.x based deployment.&lt;/p&gt;

&lt;p&gt;Data migration is a critical step in the migration to Apicurio Registry 2.x if you are using the registry as a schema registry for Kafka applications, because every Kafka message carries the global identifier for the schema stored in Apicurio Registry. It is critical that this identifier is kept between registry upgrades via data migration.&lt;/p&gt;

&lt;p&gt;For that purpose, Apicurio Registry 2.x provides a new import/export API to bulk import or export all the data from your registry deployment. This API guarantees that all the identifiers are kept when importing the data from your existing registry. The export API works by downloading a custom zip file containing all the information of your artifacts. The import API accepts that zip file, loading all of the artifacts into the registry in a single batch.&lt;/p&gt;

&lt;p&gt;Apicurio Registry 1.3.x does not provide an import/export API, but with the 2.x release we provided an export tool compatible with Apicurio Registry 1.3.x that allows you to export a compatible zip file, which can be imported to your 2.x registry. This special export tool uses common existing APIs to export all of the content in the registry. It is less performant than the built-in export API we have introduced in 2.x, and so should only be used when exporting from a 1.3.x registry.&lt;/p&gt;

&lt;p&gt;This export tool is available from the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/releases/download/2.0.0.Final/apicurio-registry-utils-exportV1-2.0.0.Final.jar&quot;&gt;Apicurio Registry GitHub release&lt;/a&gt;. It is a java application meant to be invoked from the command line.&lt;/p&gt;

&lt;p&gt;The migration steps for moving all your data from one version to another:&lt;/p&gt;

&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Have running Apicurio Registry instances for both the version 1.3.x server (exporting from) and the new 2.x server (importing to).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;procedure&quot;&gt;Procedure&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Export all the data from Apicurio Registry 1.3.x using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exportV1&lt;/code&gt; tool. This will generate a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-export.zip&lt;/code&gt; file in your current directory.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;java -jar apicurio-registry-utils-exportV1-2.0.0.Final.jar http://old-registry.my-company.com/api
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Import the zip file to Apicurio Registry 2.x using the import API. You can find more details in the &lt;a href=&quot;https://www.apicur.io/registry/docs/apicurio-registry/2.0.0.Final/getting-started/assembly-managing-registry-artifacts-api.html#exporting-importing-using-rest-api&quot;&gt;Apicurio Registry user documentation&lt;/a&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST &quot;http://new-registry.my-company.com/apis/registry/v2/admin/import&quot; \
  -H &quot;Accept: application/json&quot; -H &quot;Content-Type: application/zip&quot; \
  --data-binary @registry-export.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Check that all the artifacts are now imported to the new 2.x registry. Run these two commands and compare the count field.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &quot;http://old-registry.my-company.com/api/search/artifacts&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &quot;http://new-registry.my-company.com/apis/registry/v2/search/artifacts&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;application-migration&quot;&gt;Application migration&lt;/h2&gt;

&lt;h3 id=&quot;registry-serdes-libraries&quot;&gt;Registry SerDes libraries&lt;/h3&gt;

&lt;p&gt;If you are using the Apicurio Registry SerDes libraries, you must change the Maven dependencies you are using, because we have repackaged these classes.&lt;/p&gt;

&lt;p&gt;In Apicurio Registry 1.3.x, the SerDes libraries were provided all with just one Maven dependency:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.apicurio&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;apicurio-registry-utils-serde&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.3.2.Final&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In Apicurio Registry 2.x, this has been changed, and the SerDes libraries have been split into three Maven dependencies, one for each supported data format: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avro&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protobuf&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json schema&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.apicurio&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;apicurio-registry-serdes-avro-serde&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.0.0.Final&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.apicurio&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;apicurio-registry-serdes-protobuf-serde&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.0.0.Final&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.apicurio&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;apicurio-registry-serdes-jsonschema-serde&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.0.0.Final&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You must take into account these changes to Maven dependencies, and update your applications accordingly.&lt;/p&gt;

&lt;p&gt;The refactored SerDes libraries also include some changes in the configuration properties. You can find more details in the &lt;a href=&quot;https://www.apicur.io/registry/docs/apicurio-registry/2.0.0.Final/getting-started/assembly-using-kafka-client-serdes.html&quot;&gt;Apicurio Registry user documentation&lt;/a&gt;. You can find plenty of examples in the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry-examples/tree/2.0.x&quot;&gt;Apicurio Registry 2.0.0.Final SerDes libraries&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;registry-url-configuration&quot;&gt;Registry URL configuration&lt;/h3&gt;

&lt;p&gt;But the most important thing you must change is the registry URL configuration, which you must change from pointing to the existing API path to the new one. For example:&lt;/p&gt;

&lt;p&gt;Existing registry API path:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;props.putIfAbsent(AbstractKafkaSerDe.REGISTRY_URL_CONFIG_PARAM, &quot;http://localhost:8080/api&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;New registry API path:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;props.putIfAbsent(SerdeConfig.REGISTRY_URL, &quot;http://localhost:8080/apis/registry/v2&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;These are the most important things to take into account when doing the update to Apicurio Registry 2.0.0.Final. The process is not complex but there are some critical steps that, thanks to our APIs and tooling, should be no problem.&lt;/p&gt;

&lt;p&gt;As always, if you have any suggestions or encounter any problem feel free to contact the team by &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues&quot;&gt;filling an issue in GitHub&lt;/a&gt;&lt;/p&gt;
</description><category term="registry" /><category term="guide" /><pubDate>Tue, 01 Jun 2021 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2021/06/01/migration-v1-v2</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2021/06/01/migration-v1-v2</guid>
        </item><item>
            <title>Securing Apicurio Registry 2.0 using Keycloak</title><dc:creator>Carles Arnal Castello</dc:creator>
            <author>carles.arnal@redhat.com</author><description>&lt;p&gt;Hey everyone, in Apicurio Registry 2.0.0. Finally we’ve introduced support for some new security features. In this blog post, I will explain these concepts and show you how to configure them.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;core-features&quot;&gt;Core features&lt;/h1&gt;
&lt;p&gt;Essentially in this blog post, we will cover the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Authentication based on Keycloak&lt;/strong&gt; - optionally protect the registry such that the registry API and web console require users to authenticate (BASIC and OAuth supported)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Role-based authorization&lt;/strong&gt; - when authentication is enabled, you can also enable this feature, and users must have at least one role of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-admin&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-developer&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-readonly&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Creator-only authorization option&lt;/strong&gt; - option to prevent changes to artifacts unless the authenticated user is the user who originally created the artifact&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;keycloak-configuration&quot;&gt;Keycloak configuration&lt;/h1&gt;

&lt;h3 id=&quot;authentication-based-on-keycloak&quot;&gt;Authentication based on Keycloak&lt;/h3&gt;
&lt;p&gt;To secure Apicurio Registry you will need a Keycloak server up and running along with some specific configuration.
Essentially, you must create a Keycloak realm, two clients, and some roles (if you want to use roles).  By default, Apicurio Registry expects the name of the realm to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-local&lt;/code&gt;.
When security is enabled, Apicurio Registry will expect two Keycloak clients.  By default, these clients should be named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-api&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry&lt;/code&gt; (but don’t worry, you will be able to customize everything by using environment variables!)
Below you have the required configuration for the clients.&lt;/p&gt;

&lt;h4 id=&quot;api-client-configuration&quot;&gt;API client configuration&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/images/guides/registry-auth-client-api-config.png&quot; alt=&quot;Configure Apicurio Registry API_Client&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;web-console-client-configuration&quot;&gt;Web console client configuration&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/images/guides/registry-auth-client-ui-config.png&quot; alt=&quot;Configure Apicurio Registry_UI Client&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You will need to pay attention to the configuration - especially to the access types and the configuration URLs. 
For the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-api&lt;/code&gt; client, make sure to configure it as bearer-only, in the latest keycloak releases you will have to uncheck all the Authentication flows in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;capability config&lt;/code&gt;. For the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apicurio-registry&lt;/code&gt; client, you will need to configure it as public, to allow users to connect to your UI.
You will also need to configure Web Origins, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; to allow Web Origins coming from the redirect URL.&lt;/p&gt;

&lt;p&gt;Now, you will need to tweak the Apicurio Registry configuration to get this to work.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/guides/registry-auth-properties-config.png&quot; alt=&quot;Configure Apicurio Registry Auth_Properties&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The first property that you must set is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTH_ENABLED&lt;/code&gt;.  You will need to set it to true to enable the integration.  The second property you need to set is the Keycloak server URL: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEYCLOAK_URL&lt;/code&gt;.
We provide some sensible defaults for most of the configuration, but please, note that you can customize most of them by using environment variables.&lt;/p&gt;

&lt;h3 id=&quot;role-based-authorization&quot;&gt;Role-based authorization&lt;/h3&gt;

&lt;p&gt;To enable roles, you need to set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ROLE_BASED_AUTHZ_ENABLED&lt;/code&gt; property to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Of course, if you enable roles in Apicurio Registry, you must also create them in Keycloak as realm roles. The default roles expected by Apicurio Registry are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-admin&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-developer&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sr-readonly&lt;/code&gt;.
When you enable roles, this will result in some HTTP paths and operations available just for certain roles. 
You have the full configuration in the image above, but there are some operations like those around rules, logs levels, and so on that are restricted to the admin role. 
The developer role can perform most of the operations, and the read-only role can read or search artifacts.&lt;/p&gt;

&lt;h3 id=&quot;creator-only-authorization-option&quot;&gt;Creator-only authorization option&lt;/h3&gt;

&lt;p&gt;Finally, you also have the option to restrict certain operations just to the creator of the artifact. To do so, you can set the environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OWNER_ONLY_AUTHZ_ENABLED&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Last, but not least, we still have a lot of things to do!  As always, you can see the
stuff we’re tracking by viewing the &lt;a href=&quot;https://github.com/Apicurio/apicurio-registry/issues&quot;&gt;GitHub issues&lt;/a&gt; for the project.&lt;/p&gt;

&lt;p&gt;If you find bugs or want to request a new feature, that’s a great place to start!&lt;/p&gt;
</description><category term="blog" /><category term="registry" /><category term="security" /><pubDate>Fri, 28 May 2021 12:00:00 +0000</pubDate>
            <link>https://www.apicur.io/blog/2021/05/28/registry-security</link>
            <guid isPermaLink="true">https://www.apicur.io/blog/2021/05/28/registry-security</guid>
        </item><lastBuildDate>Sun, 10 May 2026 19:26:06 +0000</lastBuildDate>
    </channel>
</rss>
