<a id="schema-linking-cp-overview"></a>

# Schema Linking on Confluent Platform

Use Schema Linking to keep schemas in sync across two Schema Registry clusters,
typically alongside Cluster Linking for full data synchronization. The
quick start below walks through creating and using exporters for Schema
Linking on your clusters, followed by reference material for schema
contexts and exporters. Contexts are also useful outside of Schema Linking
to organize schemas into purpose-specific groups and create virtual
“sub-registries.”

![An exporter copies a schema from source to destination |sr|.](images/schema-linking.png)

## Contexts and exporters

Schema Linking uses two Schema Registry concepts, schema contexts and schema exporters:

- **Contexts** - A [context](#schema-contexts) is an independent scope
  in Schema Registry. Use contexts to create separate “sub-registries” within one Schema Registry
  cluster, each with its own schema IDs and subject names. The same schema
  ID in different contexts can represent different schemas.

  A single dot, `.`, represents the default context. An explicit context
  starts with a dot and uses more dots as separators, such as
  `.mycontext.subcontext`, like absolute Unix paths. Two contexts that
  share a prefix have no implicit relationship.
- **Exporters** - A [schema exporter](#schema-exporters) runs inside
  Schema Registry and exports schemas from one Schema Registry cluster to another. Use the Schema Registry
  REST API to create, pause, resume, and destroy exporters. An exporter
  acts like a “mini-connector” that performs change data capture for
  schemas.

## Limitations and considerations

- On Confluent Cloud, the exporter limit per environment is 10 with the Essentials
  package and 100 with the Advanced package. Refer to the
  [Packages documentation](/cloud/current/stream-governance/packages.html#overview).
- A single exporter can transfer any number of schemas; there is no upper
  limit on schemas per exporter.

For in-depth descriptions, refer to [Contexts](#schema-contexts) and
[Exporters](#schema-exporters).

## Prerequisites

Schema Linking is supported on Confluent Platform 7.0 and later versions, and on Confluent Cloud as described in [Schema Linking on Confluent Cloud](/cloud/current/sr/schema-linking.html).
Schema Linking is not supported on Confluent Community editions. To learn more, see [Confluent Platform Packages](../installation/available_packages.md#available-packages).

<a id="schema-linking-cp-quick-start"></a>

## Quick Start

If you’d like to jump in and try out Schema Linking now, follow the steps below.
At the end of the Quick Start, you’ll find deep dives on contexts, exporters, command options,
and APIs, which may make more sense after you’ve experimented with some hands-on examples.

### KRaft and ZooKeeper

#### IMPORTANT
As of Confluent Platform 8.0, ZooKeeper is no longer available for new deployments. Confluent recommends migrating to KRaft mode for new deployments.
To learn more about running Kafka in KRaft mode, see [KRaft Overview for Confluent Platform](../kafka-metadata/kraft.md#kraft-overview), [KRaft Configuration for Confluent Platform](../kafka-metadata/config-kraft.md#configure-kraft), and the KRaft steps in the [Platform Quick Start](../get-started/platform-quickstart.md#cp-quickstart-step-1).
To learn about migrating from older versions, see [Migrate from ZooKeeper to KRaft on Confluent Platform](../installation/migrate-zk-kraft.md#migrate-zk-kraft).

This tutorial provides examples for KRaft mode only. Earlier versions of this documentation provide examples for both KRaft and ZooKeeper.

For KRaft, the examples show a *combined mode* configuration, where for each cluster the broker and controller run on the same server.
Currently, combined mode is for local experimentation only and is not supported by Confluent.
It is shown here to simplify the tutorial.
If you want to run controllers and brokers on separate servers, use KRaft in isolated mode. To learn more, see [KRaft Overview for Confluent Platform](../kafka-metadata/kraft.md#kraft-overview)
and the Kafka section under [Configure Confluent Platform for production](../installation/installing_cp/zip-tar.md#config-cp-for-production).

### Configuration snapshot preview

This tutorial guides you through setup of two KRaft controllers, Confluent Servers (brokers), and Schema Registries, to serve as source and destination,
along with a single Confluent Control Center with access to both.

Following is a summary of the configurations for each of these files. The steps in the next sections guide you through a quick way to set up these files,
using existing properties files as a basis for your specialized ones.

| Files                              | Configurations                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| server0.properties                 | - `listeners=PLAINTEXT://:9092`, `CONTROLLER://:9094` (KRaft combined mode)<br/>  - `log.dirs=/tmp/kraft-combined-logs`<br/>  - `confluent.metrics.reporter.bootstrap.servers=localhost:9092`<br/><br/>  The following configurations are specific to multi-cluster Schema Registry setup for this broker:<br/>  - `confluent.http.server.listeners=http://0.0.0.0:8090`<br/>  - `confluent.schema.registry.url=http://localhost:8081`                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| server1.properties                 | - `listeners=PLAINTEXT://:9093`, `CONTROLLER://:9095` (KRaft combined mode)<br/>  - `log.dirs=/tmp/kraft-combined-logs-1`<br/>  - `confluent.metrics.reporter.bootstrap.servers=localhost:9093`<br/><br/>  The following configurations are specific to multi-cluster Schema Registry setup for this broker:<br/>  - `confluent.http.server.listeners=http://0.0.0.0:8091`<br/>  - `confluent.schema.registry.url=http://localhost:8082`                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| schema-registry0.properties        | - `listeners=PLAINTEXT://:8081`<br/>  - `kafkastore.bootstrap.servers=localhost:9092`<br/><br/>  The following configurations are specific to Schema Linking; they should be the same in both Schema Registry properties files:<br/>  - `resource.extension.class=io.confluent.schema.exporter.SchemaExporterResourceExtension`<br/>  - `kafkastore.update.handlers=io.confluent.schema.exporter.storage.SchemaExporterUpdateHandler`<br/>  - `password.encoder.secret=mysecret`                                                                                                                                                                                                                                                                                                                                                                                                                |
| schema-registry1.properties        | - `listeners=PLAINTEXT://:8082`<br/>  - `kafkastore.bootstrap.servers=localhost:9093`<br/>  - `kafkastore.topic=_schemas1` (must be different from the default `_schemas` in `schema-registry0.properties` so that<br/>  the registries do not overwrite each other <sup>[1](#f1)</sup>)<br/>  - `schema.registry.group.id=schema-registry-dest` (must be different from the group ID used by `schema-registry0.properties`,<br/>  which takes the default ID `schema-registry` <sup>[1](#f1)</sup>)<br/><br/>  The following configurations are specific to Schema Linking; they should be the same in both Schema Registry properties files:<br/>  - `resource.extension.class=io.confluent.schema.exporter.SchemaExporterResourceExtension`<br/>  - `kafkastore.update.handlers=io.confluent.schema.exporter.storage.SchemaExporterUpdateHandler`<br/>  - `password.encoder.secret=mysecret` |
| control-center-multi-sr.properties | - `bootstrap.servers=localhost:9092` (the KRaft broker is aware of its controller `9094`, so no additional config is needed)<br/>  - `confluent.controlcenter.kafka.AK1.bootstrap.servers=localhost:9093` (the KRaft broker is aware of its controller `9095`, so no additional config is needed)<br/>  - `confluent.controlcenter.streams.cprest.url=http://0.0.0.0:8090`<br/>  - `confluent.controlcenter.schema.registry.url=http://localhost:8081`<br/>  - `confluent.controlcenter.kafka.AK1.cprest.url=http://0.0.0.0:8091`<br/>  - `confluent.controlcenter.schema.registry.SR-AK1.url=http://localhost:8082`<br/><br/>  The configurations for `cpcrest.url`, `confluent.controlcenter.kafka.AK1.cprest.url`, and<br/>  `confluent.controlcenter.schema.registry.SR-AK1.url` are new properties, specific to multi-cluster Schema Registry.                                             |

### Footnotes

* <a id='f1'>**[1]**</a> The values for `kafkastore.topic` and `schema.registry.group.id` must be unique for each Schema Registry properties file because in this example the two registries are colocated on `localhost`. If the Schema Registry clusters were on different hosts, you would not need to make these changes.

#### Set environment variables

Add these two lines to your `.bashrc` or `.bash-profile` so that they are executed whenever you open a new command window.

The rest of the tutorial expects these environment variables to be set for KRaft mode:

```bash
export CONFLUENT_HOME=<CP installation directory>
```

```bash
export CONFLUENT_CONFIG=$CONFLUENT_HOME/etc/kafka
```

#### Configure the Kafka servers

1. Copy the default Kafka properties file to use as a basis for a specialized KRaft *combined mode* properties file:
   ```bash
   cp $CONFLUENT_CONFIG/server.properties $CONFLUENT_CONFIG/server0.properties
   ```
2. Update the controller on server0 to use port `9094`.
   ```bash
   sed -i '' -e "s/9093/9094/g" $CONFLUENT_CONFIG/server0.properties
   ```
3. Copy `server0.properties` (the file you just created) and to use as the basis for server1:
   ```bash
   cp $CONFLUENT_CONFIG/server0.properties $CONFLUENT_CONFIG/server1.properties
   ```
4. Update the Kafka data directory for the server1.
   ```bash
   sed -i '' -e "s/kraft-combined-logs/kraft-combined-logs-1/g" $CONFLUENT_CONFIG/server1.properties
   ```
5. Update the port numbers for server1.
   ```bash
   sed -i '' -e "s/9092/9093/g"  $CONFLUENT_CONFIG/server1.properties
   ```

   ```bash
   sed -i '' -e "s/9094/9095/g" $CONFLUENT_CONFIG/server1.properties
   ```

   ```bash
   sed -i '' -e "s/8090/8091/g" $CONFLUENT_CONFIG/server1.properties
   ```
6. Append the following lines at the end of `server0.properties`, specific to the multi-cluster Schema Registry setup for this broker:
   ```bash
   echo "confluent.http.server.listeners=http://0.0.0.0:8090" >> $CONFLUENT_CONFIG/server0.properties
   ```

   ```bash
   echo "confluent.schema.registry.url=http://localhost:8081" >> $CONFLUENT_CONFIG/server0.properties
   ```
7. Append the following lines at the end of `server1.properties`, specific to the multi-cluster Schema Registry setup for this broker:
   ```bash
   echo "confluent.http.server.listeners=http://0.0.0.0:8091" >> $CONFLUENT_CONFIG/server1.properties
   ```

   ```bash
   echo "confluent.schema.registry.url=http://localhost:8082" >> $CONFLUENT_CONFIG/server1.properties
   ```
8. In a new command window, change directories into `$CONFLUENT_HOME` to run the following KRaft setup commands for server0.
   ```bash
   cd $CONFLUENT_HOME
   ```
9. Generate a `random-uuid` for server0 using the kafka-storage tool.
   ```bash
   KAFKA_CLUSTER_ID="$(bin/kafka-storage random-uuid)"
   ```
10. Format the log directories for server0:
    ```bash
    ./bin/kafka-storage format -t $KAFKA_CLUSTER_ID -c $CONFLUENT_CONFIG/server0.properties --ignore-formatted --standalone
    ```

    This is the window in which you will run server0.
11. In a new command window, change directories into `$CONFLUENT_HOME` to run the following KRaft setup commands for server1.
    ```bash
    cd $CONFLUENT_HOME
    ```
12. Generate a `random-uuid` for server1 using the kafka-storage tool.
    ```bash
    KAFKA_CLUSTER_ID="$(bin/kafka-storage random-uuid)"
    ```
13. Format the log directories for server1:
    ```bash
    ./bin/kafka-storage format -t $KAFKA_CLUSTER_ID -c $CONFLUENT_CONFIG/server1.properties --ignore-formatted --standalone
    ```

    This is the window in which you will run server1.

#### Configure the Schema Registry properties files

1. Copy the default Schema Registry properties files to use as a basis for two specialized Schema Registry properties files:
   ```bash
   cp $CONFLUENT_HOME/etc/schema-registry/schema-registry.properties $CONFLUENT_HOME/etc/schema-registry/schema-registry0.properties
   ```

   ```bash
   cp $CONFLUENT_HOME/etc/schema-registry/schema-registry.properties $CONFLUENT_HOME/etc/schema-registry/schema-registry1.properties
   ```
2. Append the following same lines to the end of both `schema-registry0.properties`:
   ```bash
   echo "resource.extension.class=io.confluent.schema.exporter.SchemaExporterResourceExtension" >> $CONFLUENT_HOME/etc/schema-registry/schema-registry0.properties
   ```

   ```bash
   echo "kafkastore.update.handlers=io.confluent.schema.exporter.storage.SchemaExporterUpdateHandler" >> $CONFLUENT_HOME/etc/schema-registry/schema-registry0.properties
   ```

   ```bash
   echo "password.encoder.secret=mysecret" >> $CONFLUENT_HOME/etc/schema-registry/schema-registry0.properties
   ```
3. Add the same lines to the end of `schema-registry1.properties`:
   ```bash
   echo "resource.extension.class=io.confluent.schema.exporter.SchemaExporterResourceExtension" >> $CONFLUENT_HOME/etc/schema-registry/schema-registry1.properties
   ```

   ```bash
   echo "kafkastore.update.handlers=io.confluent.schema.exporter.storage.SchemaExporterUpdateHandler" >> $CONFLUENT_HOME/etc/schema-registry/schema-registry1.properties
   ```

   ```bash
   echo "password.encoder.secret=mysecret" >> $CONFLUENT_HOME/etc/schema-registry/schema-registry1.properties
   ```
4. Update the port numbers and configs for `schema-registry1.properties`.
   - Update the `listeners` port:
     ```bash
     sed -i '' -e "s/8081/8082/g" $CONFLUENT_HOME/etc/schema-registry/schema-registry1.properties
     ```
   - Update the `kafkastore.bootstrap.servers` port:
     ```bash
     sed -i '' -e "s/9092/9093/g" $CONFLUENT_HOME/etc/schema-registry/schema-registry1.properties
     ```
   - Update the `kafkastore.topic` name. This must be different from the default `_schemas` in server0.properties so that the registries do not overwrite each other:
     ```bash
     sed -i '' -e "s/kafkastore.topic=_schemas/kafkastore.topic=_schemas1/g" $CONFLUENT_HOME/etc/schema-registry/schema-registry1.properties
     ```

#### Configure the Confluent Control Center properties files

In the Control Center properties file, you will use the default ports for `bootstrap.servers` and `zookeeper.connect`, but modify and add several other configurations.

1. Copy the default Control Center properties file to use as a basis for a specialized Control Center properties file for this tutorial:
   ```bash
   cp $CONFLUENT_HOME/etc/confluent-control-center/control-center-dev.properties $CONFLUENT_HOME/etc/confluent-control-center/control-center-multi-sr.properties
   ```
2. Append the following lines to the end of the file. These update some defaults and add new configurations to match the server and Schema Registry setups in previous steps:
   ```bash
   echo "confluent.controlcenter.kafka.AK1.bootstrap.servers=localhost:9093" >> $CONFLUENT_HOME/etc/confluent-control-center/control-center-multi-sr.properties
   ```

   ```bash
   echo "confluent.controlcenter.streams.cprest.url=http://0.0.0.0:8090" >> $CONFLUENT_HOME/etc/confluent-control-center/control-center-multi-sr.properties
   ```

   ```bash
   echo "confluent.controlcenter.kafka.AK1.cprest.url=http://0.0.0.0:8091" >> $CONFLUENT_HOME/etc/confluent-control-center/control-center-multi-sr.properties
   ```

   ```bash
   echo "confluent.controlcenter.schema.registry.SR-AK1.url=http://localhost:8082" >> $CONFLUENT_HOME/etc/confluent-control-center/control-center-multi-sr.properties
   ```

### Start the clusters

1. Start the Confluent Server brokers
2. Start the Schema Registry clusters.
3. Start Confluent Control Center.

**Start the Kafka brokers**

```bash
kafka-server-start $CONFLUENT_CONFIG/server0.properties
```

```bash
kafka-server-start $CONFLUENT_CONFIG/server1.properties
```

**Start Schema Registry clusters**

```bash
schema-registry-start $CONFLUENT_HOME/etc/schema-registry/schema-registry0.properties
```

```bash
schema-registry-start $CONFLUENT_HOME/etc/schema-registry/schema-registry1.properties
```

**Start Control Center**

```bash
control-center-start $CONFLUENT_HOME/etc/confluent-control-center/control-center-multi-sr.properties
```

### Create schemas on the source

Create at least two or three schemas in the source environment; at least one of which has a qualified subject name.

1. Create topics and associated schemas in Confluent Control Center (Control Center runs at `http://localhost:9021/`).

   This will produce schema subjects with the naming scheme `<topic>-value`. (You cannot create qualified subjects from Control Center. Use the Schema Registry API for this.)
2. Create schemas with both qualified and unqualified subject names, with the syntax: `:.<context-name>:<subject-name>`.
   - To create a schema with an unqualified subject name, simply provide a name such as `coffee` or `donuts`.
   - To create a schema with a qualified subject name in a specified context, use the [REST API](develop/api.md#schemaregistry-api) with the syntax: `:.<context-name>:<subject-name>`.
     For example: `:.snowcones:sales` or `:.burgers:locations`

   Here is an example of using the [Schema Registry API](develop/api.md#schemaregistry-api) to create a schema with an unqualified subject name.
   The Schema Registry URL given is for the “source” Schema Registry in our example. The `--data` path must be the full path to the schema file (`test.avro`),
   such as `--data @/Users/sam/test.avro`.
   ```bash
   curl -v -X POST -H "Content-Type: application/json" --data @/path/to/test.avro  http://localhost:8081/subjects/donuts/versions
   ```

   The above `curl` command calls the file that contains this Avro schema.
   ```bash
   {
         "schema":
           "{
                  \"type\": \"record\",
                  \"connect-name\": \"myname\",
                  \"connect-donuts\": \"mydonut\",
                  \"name\": \"test\",
                  \"doc\": \"some doc info\",
                    \"fields\":
                      [
                        {
                          \"type\": \"string\",
                          \"doc\": \"doc for field1\",
                          \"name\": \"field1\"
                        },
                        {
                          \"type\": \"int\",
                          \"doc\": \"doc for field2\",
                          \"name\": \"field2\"
                        }
                      ]
                  }"
        }
   ```

   The output of this command will be similar to the following:
   ```none
   *   Trying 127.0.0.1:8081...
   * Connected to localhost (127.0.0.1) port 8081 (#0)
   > POST /subjects/donuts/versions HTTP/1.1
   > Host: localhost:8081
   > User-Agent: curl/8.1.2
   > Accept: */*
   > Content-Type: application/json
   > Content-Length: 682
   >
   < HTTP/1.1 200 OK
   < Date: Mon, 04 Sep 2023 22:38:49 GMT
   < X-Request-ID: e1ec7cfa-c714-4566-97e3-0c26f6557af2
   < Content-Type: application/vnd.schemaregistry.v1+json
   < Vary: Accept-Encoding, User-Agent
   < Content-Length: 8
   <
   * Connection #0 to host localhost left intact
   {"id":1}
   ```
3. Use the Schema Registry API to list subjects on the source, passing in the prefix.
   ```bash
   curl --silent -X GET <source sr url>/subjects?subjectPrefix=":.<context-name>:<subject-name>" | jq
   ```

   For example:
   ```bash
   curl --silent -X GET http://localhost:8081/subjects?subjectPrefix=":*:" | jq
   ```

   Your output should resemble:
   ```bash
   ":.snowcones:sales",
     "coffee-value",
     "donuts"
   ```

You are ready to create and test exporters for Schema Linking across your two
clusters and registries. Run the exporter commands shown below from `$CONFLUENT_HOME`;
that is, from the top level of your Confluent Platform directory.

### Create a configuration file for the exporter

Your schema exporter will read the schemas in the SOURCE environment and
export linked copies to the destination.

Create `~/config.txt` which you will use to create exporters, and fill in
the URL the exporter needs to access the DESTINATION cluster:

```bash
schema.registry.url=<destination sr url>
```

#### Using credentials (optional)

To test a local instance of Confluent Platform for this tutorial, you do not need credentials.

If you want to require authentication, you must first [configure Schema Registry for Basic HTTP authentication](security/index.md#schema-registry-basic-http-auth),
and ideally, to also [use HTTPS for secure communication](security/index.md#schema-registry-http-https).

Once Schema Registry is configured to use basic authentication per the above instructions, you would add credentials to end of your `~/config.txt` file as shown:

```bash
schema.registry.url=<destination sr url>
basic.auth.credentials.source=USER_INFO
basic.auth.user.info=fred:letmein
```

The above example, and the required configurations in Schema Registry to support it, are described in the steps to [configure Schema Registry for Basic HTTP authentication](security/index.md#schema-registry-basic-http-auth).

With the credentials in this file, each time you call `--config-file ~/config.txt` in the commands described below,
you would then automatically pass these credentials in along with the Schema Registry URL.

In addition to the parameters for basic HTTP authentication, you can use the config file to pass any
of the client configurations described in [Clients to Schema Registry](security/index.md#clients-to-sr-security-configs).

To communicate with a source Schema Registry cluster configured with Basic HTTP authentication using the Confluent CLI,
pass `--basic.auth.credentials.source` and `--basic.auth.user.info` with proper credentials.
For bearer token authentication, provide the credentials in `--bearer.auth.credentials.source` and `--bearer.auth.token` flags.

#### IMPORTANT
Exporters continue running even if the creator’s access is revoked, suspended, or deleted. The system does not perform continuous
authorization checks after creation. To prevent unauthorized access, regularly monitor and aggregate the REST API access logs for
your Schema Registry instances. Trace and alert on any unauthorized POST, PUT, or DELETE requests directed at the `/exporters` configuration endpoint
to identify potential schema data exfiltration.

#### Get full list of client-side configuration options (optional)

Exporters read the schemas in one environment (source) and export linked copies to the another (destination).
As such, exporters are clients to Schema Registry. To learn more about configuration options for Schema Registry clients, see [Configuration Reference for Schema Registry Clients on Confluent Platform](sr-client-configs.md#sr-client-configs).

### Create the exporter on the source

Use the Confluent Platform CLI to create an exporter on the source.

Create a new exporter using the `schema-exporter --create` command.

```bash
./bin/schema-exporter --create --name <name-of-exporter> --subjects ":*:" \
 --config-file ~/config.txt
 --schema.registry.url <source sr url>
```

For example, this command creates an exporter called “my-first-exporter” that will export all schemas (`":*:"`),
including those in specific contexts as well as those in the default context:

```bash
./bin/schema-exporter --create --name my-first-exporter --subjects ":*:" \
 --config-file ~/config.txt \
 --schema.registry.url  http://localhost:8081/
```

The following command syntax creates an exporter that exports only the subjects `donuts` and `coffee` in a custom context, `context1`.

```bash
schema-exporter --create --name exporter1 --subjects donuts,coffee \
 --context-type CUSTOM --context-name context1 \
 --config-file ~/config.txt \
 --schema.registry.url <source sr url>
```

#### More options for exporters

If you used the first example above, then the exporter you just created is
relatively basic, in that it just exports everything. As you’ll see in the next
section, this is an efficient way to get an understanding of how you might
organize, export, and navigate schemas with qualified and unqualified subject names.

Keep in mind that you can create exporters that specify to export only specific subjects and contexts using this syntax:

```bash
schema-exporter --create <exporterName> --subjects <subjectName1>,<subjectName2> \
--context-type CUSTOM --context-name <contextName> \
--config-file ~/config.txt
```

- Replace anything within `<>` with a name you like.
- `subjects` are listed as a comma-separated string list, such as “pizzas,sales,customers”.
- `subjects`, `context-type`, and `context-name` are all optional. `context-name` is specified if `context-type` is CUSTOM.
- `subjects` defaults to `*`, and `context-type` defaults to AUTO.

Alternatively, if you take all the defaults and do not specify `--subjects` when you create an exporter,
you will get an exporter that exports schemas in all contexts/subjects, including the default context

```bash
./bin/schema-exporter --create --name my-first-exporter \
 --config-file ~/config.txt \
 --schema.registry.url  http://localhost:8081/
```

If you want to export the default context only, specify `--subjects` to be `:.:*`
With this type of exporter, schemas on the source that have qualified subject names will not be exported to the destination.

Another optional parameter you can use with `schema-exporter --create` and
`schema-exporter --update` is `--subject-format`. This specifies a format
for the subject name in the destination cluster, and may contain `${subject}`
as a placeholder which will be replaced with the default subject name. For
example, `dc_${subject}` for the subject `orders` will map to the
destination subject name `dc_orders`.

You can create and run multiple exporters at once, so feel free to circle back at the
end of the Quick Start to create and test more exporters with different parameters.

### Verify the exporter is running and view information about it

1. List available exporters.
   ```bash
   ./bin/schema-exporter --list \
    --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --list --schema.registry.url http://localhost:8081
   ```

   Your exporter will show in the list.
   ```bash
   [my-first-exporter]
   ```
2. Describe the exporter.
   ```bash
   ./bin/schema-exporter --describe --name <exporterName> \
    --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --describe --schema.registry.url http://localhost:8081 --name my-first-exporter
   ```

   Your output should resemble:
   ```bash
   {"name":"my-first-exporter","subjects":[":*:"],"contextType":"AUTO","context":".","config":{"schema.registry.url":"http://localhost:8082"}}
   ```
3. Get configurations for the exporter.
   ```bash
   ./bin/schema-exporter --get-config --name <exporterName> \
    --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --get-config --name my-first-exporter --schema.registry.url http://localhost:8081
   ```

   Your output should resemble:
   ```bash
   {schema.registry.url=http://localhost:8082}
   ```
4. Get the status of exporter.
   ```bash
   ./bin/schema-exporter --get-status --name <exporterName> \
    --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --get-status --name my-first-exporter --schema.registry.url http://localhost:8081
   ```

   Your output should resemble:
   ```bash
   {"name":"my-first-exporter","state":"RUNNING","offset":9,"ts":1635890864106}
   ```
5. Finally, as a check, get a list of schema subjects on the source.

   To do this, use the API call with the subject prefix, as shown:
   ```bash
   curl --silent -X GET http://localhost:8081/subjects?subjectPrefix=":*:" | jq
   ```

   ```bash
   ":.snowcones:sales",
     "coffee-value",
     "donuts"
   ```

### Check that the schemas were exported

Now that you have verified that the exporter is running, and you know which schemas
you created on the source, check to see that your schemas were exported to the destination.

1. Run the following API call to view schema subjects on the destination.
   ```bash
   curl --silent -X GET http://localhost:8082/subjects?subjectPrefix=":*:" | jq
   ```

   ```bash
   ":.QWE7LDvySmeV6Sg81B3jUg-schema-registry.snowcones:sales",
   ":.QWE7LDvySmeV6Sg81B3jUg-schema-registry:coffee-value",
   ":.QWE7LDvySmeV6Sg81B3jUg-schema-registry:donuts"
   ```
2. List only schemas in particular contexts.
   ```bash
   curl --silent -X GET '<destination sr url>/subjects?subjectPrefix=:.<context-name>:<subject-name>' | jq
   ```

   - For example, to find all subjects under the context `snowcones` on the source, use the following command:
     ```bash
     curl --silent -X GET 'http://localhost:8081/subjects?subjectPrefix=:.snowcones:' | jq
     ```

     If you have a single subject under the `snowcones` context, your output will resemble:
     ```bash
     ":.snowcones:sales"
     ```
   - To list all subjects under the context `snowcones` on the destination, use the same command syntax. Note that you must include
     the long IDs at the beginning of subject names on the destination because these are part of the prefixes:
     ```bash
     curl --silent -X GET http://localhost:8082/subjects?subjectPrefix=":.QWE7LDvySmeV6Sg81B3jUg-schema-registry.snowcones:" | jq
     ```

     Your output will resemble:
     ```bash
     ":.QWE7LDvySmeV6Sg81B3jUg-schema-registry.snowcones:sales"
     ```

### Pause the exporter and make changes

1. Pause the exporter.

   Switch back to the SOURCE, and run the following command to pause the exporter.
   ```bash
   ./bin/schema-exporter --pause --name <exporterName> \
    --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --pause --name my-first-exporter --schema.registry.url http://localhost:8081
   ```

   You should get output verifying that the command was successful.
   For example: `Successfully paused exporter my-first-exporter.`

   Check the status, just to be sure.
   ```bash
   ./bin/schema-exporter --get-status --name <exporterName> \
    --schema.registry.url <source sr url>
   ```

   Your output should resemble:
   ```bash
   {"name":"my-first-exporter","state":"PAUSED","offset":9,"ts":1635890864106}
   ```
2. Reset schema exporter offset, then get the status.
   ```bash
   ./bin/schema-exporter --reset --name <exporterName> \
    --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --reset --name my-first-exporter --schema.registry.url http://localhost:8081
   ```

   The status will show that the offset is reset. For examples:
   ```bash
   Successfully reset exporter my-first-exporter
   ```
3. Update exporter configurations or information.

   You can choose to update any of `subjects`, `context-type`, `context-name`, or `config-file`.
   For example:
   ```bash
   ./bin/schema-exporter --update --name <exporterName> --context-name <newContextName>
   ```
4. Resume schema exporter.
   ```bash
   ./bin/schema-exporter --resume --name <exporterName> --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --resume --name my-first-exporter --schema.registry.url http://localhost:8081
   ```

   Your output should resemble:
   ```bash
   Successfully resumed exporter my-first-exporter
   ```

### Delete the exporter

When you are ready to wrap up your testing, pause and then delete the exporter(s) as follows.

1. Pause the exporter.
   ```bash
   ./bin/schema-exporter --pause --name <exporterName> \
    --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --pause --name my-first-exporter --schema.registry.url http://localhost:8081
   ```
2. Delete the exporter.
   ```bash
   ./bin/schema-exporter --delete --name <exporterName> \
   --schema.registry.url <source sr url>
   ```

   For example:
   ```bash
   ./bin/schema-exporter --delete --name my-first-exporter --schema.registry.url http://localhost:8081
   ```

   Your output should resemble:
   ```bash
   Successfully deleted exporter my-first-exporter
   ```

This concludes the Quick Start. The next sections are a deep dive into
Schema Linking concepts and tools you just tried out.

<a id="schema-contexts"></a>

## Contexts

### What is a context?

A schema context, or simply context, is essentially a grouping of subject
names and schema IDs. A single Schema Registry cluster can host any number of
contexts. Each context can be thought of as a separate “sub-registry”.  A
context can also be copied to another Schema Registry cluster, using a [schema exporter](#schema-exporters).

### How contexts work

Following are a few key aspects of contexts and how they help to organize schemas.

#### Schemas and schema IDs are scoped by context

Subject names and schema IDs are scoped by context so that two contexts in the
same Schema Registry cluster can each have a schema with the same ID, such as `123`, or a
subject with the same name, such as `mytopic-value`, without any problem.

To put this another way, subject names and schema IDs are unique per context. You can have
schema ID `123` in context `.mycontext` and schema ID `123` in context `.yourcontext`
and these can be different from one another.

#### Default context

Any schema ID or subject name without an explicit context lives in the default
context, which is represented as a single dot `.`. An explicit context starts
with a dot and can contain any parts separated by additional dots, such as
`.mycontext.subcontext`. You can think of context names as similar to absolute
Unix paths, but with dots instead of forward slashes (in this analogy, the
default schema context is like the root Unix path). However, there is no relationship
between two contexts that share a prefix.

#### Global context

Schema Registry supports a global configuration context using the special context name `:.__GLOBAL:`. When looking up a configuration or mode
with `defaultToGlobal`, the lookup proceeds from the subject to the context, and then to the global context (`:.__GLOBAL:`).
The global context is only valid for configuration and mode settings; it cannot be used to store subjects or schemas. For contexts
without global configuration, compatibility is handled per context. To learn more, see [Schema Evolution and Compatibility for Schema Registry on Confluent Platform](fundamentals/schema-evolution.md#schema-evolution-and-compatibility).

The global context provides a hierarchical configuration lookup mechanism:

1. **Subject level**: Configuration is first checked at the specific subject level.
2. **Context level**: If not found at the subject level, configuration is checked at the context level.
3. **Global level**: Finally, if not found at the context level, configuration falls back to the global context (`:.__GLOBAL:`).

This hierarchy allows you to:

- Set default configurations that apply across all contexts using the global context.
- Override global defaults with context-specific configurations.
- Further override with subject-specific configurations for fine-grained control.

The global context is particularly useful for establishing organization-wide defaults
for compatibility modes while maintaining the flexibility to customize behavior for specific contexts or subjects as needed.

<a id="schema-link-qualified-subject"></a>

#### Qualified subjects

A subject name can be qualified with a context, in which case it is called a
qualified subject. When a context qualifies a subject, the context must be
surrounded by colons. An example is `:.mycontext:mysubject`. A subject
name that is unqualified is assumed to be in the default context, so that
`mysubject` is the same as `:.:mysubject` (the dot representing the default context).

The one exception to this rule is in the case of a subject name specified for a schema
reference. In this case, if the subject name of the schema reference is not qualified
with an explicit context, it inherits the context in which the root schema resides.

For example, if the following set of schema references
are associated with a schema in a subject in the `.prod` context:

```json
[
  {
    "name": "io.confluent.examples.avro.Customer",
    "subject": "customer",
    "version": 1
  },
  {
    "name": "io.confluent.examples.avro.Product",
    "subject": "product",
    "version": 1
  },
  {
    "name": "io.confluent.examples.avro.Order",
    "subject": "order",
    "version": 1
  }
]
```

then, the subject names above, `customer`, `product`, and `order`, will refer to the subjects `:.prod:customer`, `:.prod:product`, and `:.prod:order`, respectively.

There are two ways to pass a context to the REST APIs.

- [Using a qualified subject](#qualified-subject)
- [Using a base context path](#base-context-path)

<a id="qualified-subject"></a>

### Using a qualified subject

A qualified subject can be passed anywhere that a subject name is expected.
Most REST APIs take a subject name, such as `POST /subjects/{subject}/versions`.

There are a few REST APIs that don’t take a subject name as part of the URL path:

- `/schemas/ids/{id}`
- `/schemas/ids/{id}/subjects`
- `/schemas/ids/{id}/versions`

The three APIs above can now take a query parameter named  “subject” (written as `?subject`),
so you can pass a qualified subject name, such as `/schemas/ids/{id}?subject=:.mycontext:mysubject`,
and the given context is then used to look up the schema ID.

<a id="base-context-path"></a>

### Using a base context path

As mentioned, all APIs that specify an unqualified subject operate in the
default context.  Besides passing a qualified subject wherever a subject name is
expected, a second way to pass the context is by using a base context path.  A
base context path takes the form `/contexts/{context}` and can be prepended to
any existing Schema Registry path.  Therefore, to look up a schema ID in a specific context,
you could also use the URL `/contexts/.mycontext/schemas/ids/{id}`.

A base context path can also be used to operate with the default context.  In this case,
the base context path takes the form “/contexts/:.:/”; for example, `/contexts/:.:/schemas/ids/{id}`.
A single dot cannot be used because it is omitted by some URL parsers.

### Multi-Context APIs

All the examples so far operate in a single context.  There are three APIs that return results for multiple contexts.

- `/contexts`
- `/subjects`
- `/schemas?subjectPrefix=:*:`

The first two APIs, `/contexts` and `/subjects`, return a list of all contexts and subjects, respectively.
The other API, `/schemas`, normally only operates in the default context. This API can be used to query all contexts
by passing a `subjectPrefix` with the value `:*:`, called the *context wildcard*.  The context wildcard matches all contexts.

<a id="schema-link-specify-a-context-name-for-clients"></a>

### Specifying a context name for clients

When using a client to talk to Schema Registry, you may want the client to use a particular context.
An example of this scenario is when migrating a client from communicating with one Schema Registry to another.
You can achieve this by using a base context path, as defined above.  To do this, simply change
the Schema Registry URL used by the client from `https://<host1>` to `https://<host2>/contexts/.mycontext`.

Note that by using a base context path in the Schema Registry URL, the client will use the same
schema context for every Schema Registry request.  However, an advanced scenario might involve a
client using different contexts for different topics. To achieve this, you can specify
a context name strategy to the serializer or deserializer:

- `context.name.strategy=com.acme.MyContextNameStrategy`

The context name strategy is a class that must implement the following interface:

```none
/**
 * A {@link ContextNameStrategy} is used by a serializer or deserializer to determine
 * the context name used with the schema registry.
 */
public interface ContextNameStrategy extends Configurable {

  /**
   * For a given topic, returns the context name to use.
   *
   * @param topic The Kafka topic name.
   * @return The context name to use
   */
  String contextName(String topic);
}
```

Again, the use of a context name strategy should not be common. Specifying the
base context path in the Schema Registry URL should serve most needs.

<a id="schema-exporters"></a>

## Exporters

### What is an Exporter?

Previously, [Confluent Replicator](https://docs.confluent.io/platform/current/multi-dc-deployments/replicator/index.html)
was the primary means of [migrating schemas](https://docs.confluent.io/platform/current/schema-registry/installation/migrate.html)
from one Schema Registry cluster to another, as long as the source Schema Registry cluster was on-premise. To support
schema migration using this method, the destination Schema Registry is placed in IMPORT mode, either globally or for a specific subject.

The new schema exporter functionality replaces and extends the schema migration
functionality of Replicator. Schema exporters reside within a Schema Registry cluster, and can
be used to replicate schemas between two Schema Registry clusters in Confluent Cloud.

### Schema Linking

You use schema exporters to accomplish Schema Linking, using contexts and/or
qualified subject names to sync schemas across registries. Schema contexts
provide the conceptual basis and namespace framework, while the exporter does
the heavy-lift work of the linking.

#### Schemas export from the source default context to a new context on the destination

By default, a schema exporter exports schemas from the default context in
the source Schema Registry to a new context in the destination Schema Registry. The destination
context (or a subject within the destination context) is placed in IMPORT
mode. This allows the destination Schema Registry to use its default context as usual,
without affecting any clients of its default context.

The new context created by default in the destination Schema Registry will have the form `.lsrc-xxxxxx`,
taken from the logical name of the source.

#### Schema Registry clusters can export schemas to each other

Two Schema Registry clusters can each have a schema exporter that exports schemas
from the default context to the other Schema Registry. In this setup, each side can read
from or write to the default context, and each side can read from (but not write
to) the exported context. This allows you to match the setup of [Cluster Linking](https://docs.confluent.io/cloud/current/multi-cloud/cluster-linking/index.html),
where you might have a source topic and a read-only mirror topic on each side.

#### Customizing exporters

There are various ways to customize which contexts are exported from the source
Schema Registry, and which contexts are used in the destination Schema Registry. The full list of
configuration properties is shown below.

#### How many exporters are allowed per Schema Registry?

The limit on the number of exporters allowed at any one time per Schema Registry is 10.

<a id="exporter-configs"></a>

### Configuration options

An exporter has these main configuration properties:

`name`
: A unique name for the exporter.

`subjects`
: This can take several forms:
  <br/>
  - A list of subject names, for example, `[ "subject1", "subject2" ]`
  - A singleton list containing a subject name prefix that ends in a wildcard, such as `["mytopic*"]`
  - A singleton list containing a lone wildcard, `["*"]`, that indicates all subjects in the default context. This is the default.
  - A singleton list containing the context wildcard, `[":*:"]`, that indicates all contexts.
  - A singleton list containing a context prefix that ends in a wildcard, such as `[":.mycontext:*"]`, to export subjects from multiple contexts that match the pattern (for example, `.mycontext1` and `.mycontext2`).
  - A singleton list containing a subject prefix within a specific context, such as `[":.mycontext:mysubjects-*"]`, to export subjects that match the prefix within that context.

`subject-format`
: This is an optional parameter you can use to specify a format for the subject name in the destination cluster. You can specify `${subject}`
  as a placeholder, which will be replaced with the default subject name. For example, `dc_${subject}` for the subject `orders` will map to the
  destination subject name `dc_orders`.

`context-type`
: One of:
  <br/>
  - **AUTO** - Prepends the source context with an automatically generated context,
    which is `.lsrc-xxxxxx` for Confluent Cloud. This is the default.
  - CUSTOM - Prepends the source context with a custom context name, specified in `context` below.
  - NONE - Copies the source context as-is, without prepending anything. This is useful to make an exact copy of the source Schema Registry in the destination.
  - DEFAULT - Replaces the source context with the default context. This is useful for copying schemas to the default context in the destination.
    (**Note:** DEFAULT is available on Confluent Cloud as of July 2023, and on Confluent Platform starting with version 7.4.2.)
  - REPLACE - Replaces the source context with the context name specified in `context` below.
    REPLACE drops the source context.
    All exported subjects land in the destination under the replacement context, regardless of which context they came from in the source.
  <br/>
  For example, if the source has subjects `:.:orders-value` and `:.nondefault:products-value`
  and you set `context-type: REPLACE` with `context-name: .archive`, they land in the
  destination as `:.archive:orders-value` and `:.archive:products-value`.
  <br/>
  Using the same source subjects, the other context types map them as follows:
  <br/>
  - With `context-type: AUTO`, they land as `:.lsrc-xxxxxx:orders-value` and
    `:.lsrc-xxxxxx.nondefault:products-value`. AUTO prepends the generated
    context and keeps the source context for non-default subjects.
  - With `context-type: CUSTOM` and `context-name: .archive`, they land as
    `:.archive:orders-value` and `:.archive.nondefault:products-value`.
    CUSTOM prepends the custom context and keeps the source context for
    non-default subjects.
  - With `context-type: NONE`, they land unchanged as `:.:orders-value` and
    `:.nondefault:products-value`.

`context-name`
: A context name to be used with the CUSTOM or REPLACE `contextType` above.

`config`
: A set of configurations for creating a client to talk to the destination Schema Registry, which can be passed in a config file (for example, `--config-file  ~/<my-config>.txt`). Typically, this includes:
  <br/>
  - `schema.registry.url` - The URL of the destination Schema Registry. The `schema.registry.url` must contain a valid, fully-qualified domain name and not just an IP address. For example, `https://my-registry.example.com` would meet this requirement but `https://192.168.1.1` would not.
  - `basic.auth.credentials.source` - Typically “USER_INFO”
  - `basic.auth.user.info` - Typically of the form `<api-key>:<api-secret>`

### System topics and security configurations

The following configurations for system topics are available:

- `exporter.config.topic` - Stores configurations for the exporters. The default name for this topic is `_exporter_configs`, and its default/required configuration is: `numPartitions=1`, `replicationFactor=3`, and `cleanup.policy=compact`.
- `exporter.state.topic` - Stores the status of the exporters. The default name for this topic is `_exporter_states`, and its default/required configuration is: `numPartitions=1`, `replicationFactor=3`, and `cleanup.policy=compact`.

If you are using role-based access control (RBAC), `exporter.config.topic` and `exporter.state.topic` require `ResourceOwner` on these topics, as does the `_schemas` internal topic.
See also, [Use Role-Based Access Control (RBAC) in Confluent Cloud](https://docs.confluent.io/cloud/current/access-management/access-control/cloud-rbac.html#)
and [Configuring Role-Based Access Control for Schema Registry on Confluent Platform](https://docs.confluent.io/platform/current/schema-registry/security/rbac-schema-registry.html).

If you are configuring Schema Registry on Confluent Platform using the [Schema Registry Security Plugin](https://docs.confluent.io/platform/current/confluent-security-plugins/schema-registry/install.html),
you must activate both the exporter and the [Schema Registry security plugin](https://docs.confluent.io/platform/current/confluent-security-plugins/schema-registry/install.html#activate-the-plugins)
by specifying both extension classes in the `$CONFLUENT_HOME/etc/schema-registry/schema-registry.properties` files:

```bash
resource.extension.class=io.confluent.kafka.schemaregistry.security.SchemaRegistrySecurityResourceExtension,io.confluent.schema.exporter.SchemaExporterResourceExtension
```

The configuration for the exporter resource extension class in the `schema-registry.properties` is described in [Set up source and destination environments](https://docs.confluent.io/platform/current/schema-registry/schema-linking-cp.html#set-up-source-and-destination-environments)
in Schema Linking on Confluent Platform.

### Lifecycle and states

Schema Registry stores schemas in a Kafka topic. A schema exporter uses the topic offset to determine its progress.

When a schema exporter is created, it begins in the STARTING state. While in
this state, it finds and exports all applicable schemas already written to the topic.
After exporting previously registered schemas, the exporter then enters the RUNNING state,
during which it will be notified of any new schemas, which it can export if applicable.
As schemas are exported, the exporter will save its progress by recording the latest topic offset.

If you want to make changes to the schema exporter, you must first “pause” it, which
causes it to enter the PAUSED state. You can pause an exporter at any point in its lifecycle.
The exporter can then be resumed after the proper changes are made.
Upon resuming, the exporter will find and export any applicable schemas since the last offset that it recorded.

While an exporter is paused, it can also be “reset”, which will cause it to clear
its saved offset and re-export all applicable schemas when it resumes. To accomplish
this, the exporter starts off again in STARTING state after a reset, and follows the same lifecycle.

To update an exporter’s configuration, such as adding subjects to its subject
list, pause the exporter, apply the update, and reset it before resuming. An
exporter tracks its progress as an offset and exports only schemas registered
after that offset. Schemas already registered for the newly added subjects fall
before that offset. Without a reset, the exporter skips them and exports only
schemas registered after it resumes. A reset clears the recorded offset. The
exporter then re-scans from the start and exports all matching schemas,
including the existing ones for the newly added subjects.

The states of a schema exporter at various stages in its lifecycle are summarized below.

| State    | Description                                                                                                                                                    |
|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| STARTING | The exporter finds and exports all applicable previously registered schemas for the topic. This is the starting state, or the state after a reset.             |
| RUNNING  | The exporter is notified of new schemas, exports them if applicable, and tracks progress by recording last topic offset.                                       |
| PAUSED   | An exporter can be paused; for example, to make configuration changes. When it resumes, the exporter finds and exports schemas since the last recorded offset. |
<!-- .. [#f1] The exporter can be paused in the STARTING state on |cp| 8.1.0 and later, and in |ccloud|. On earlier versions of |cp|, the exporter had to complete STARTING state before it could be paused. -->

<a id="schema-linking-apis"></a>

### REST APIs

Schema Registry supports the following REST APIs, as fully detailed in [Exporters](https://docs.confluent.io/platform/current/schema-registry/develop/api.html#schemaregistry-api-exporters) in the
[Schema Registry API documentation](https://docs.confluent.io/platform/current/schema-registry/develop/api.html):

| Task                                    | API                          |
|-----------------------------------------|------------------------------|
| Gets a list of exporters for a tenant   | GET /exporters               |
| Creates a new exporter                  | POST /exporters              |
| Gets info about an exporter             | GET /exporters/{name}        |
| Gets the config for an exporter         | GET /exporters/{name}/config |
| Gets the status of an exporter          | GET /exporters/{name}/status |
| Updates the information for an exporter | PUT /exporters/{name}/config |
| Pauses an exporter                      | PUT /exporters/{name}/pause  |
| Resumes an exporter                     | PUT /exporters/{name}/resume |
| Resets an exporter, clears offsets      | PUT /exporters/{name}/reset  |
| Deletes an exporter                     | DELETE /exporters/{name}     |

<a id="schema-linking-deployment-strategies"></a>

## Deployment strategies and Schema Linking

Schema Linking can replicate schemas between Schema Registry clusters as follows:

A schema link sends data from a “source cluster” to a “destination cluster”. The supported cluster types are shown in the table below.

| Source Schema Registry Cluster Options   | Destination Schema Registry Cluster Options                                    |
|------------------------------------------|--------------------------------------------------------------------------------|
| Confluent Cloud with internet networking | Confluent Cloud with internet networking                                       |
| Confluent Cloud with internet networking | Confluent Platform 7.0+ with an IP address accessible over the public internet |
| Confluent Platform 7.0+                  | Confluent Platform 7.0+                                                        |
| Confluent Platform 7.0+                  | Confluent Cloud with internet networking                                       |

Schema Linking can also be used in both directions between two clusters if
each direction links to a different context, allowing each side to continue
to receive both reads and writes for schemas.

With regard to Confluent Cloud and Confluent Platform solutions, you would use Schema Linking with Cluster Linking to mirror from one instance to the other.
Any use of Confluent Platform in these setups require Confluent Platform 7.0.+ or later.

To learn more about Cluster Linking and mirror topics, see [Cluster Linking for Confluent Platform](/platform/current/multi-dc-deployments/cluster-linking/index.html) and
[Geo-replication with Cluster Linking on Confluent Cloud](/cloud/current/multi-cloud/cluster-linking/index.html).

## Schema Linking on private networks

You can use the private endpoints for a Schema Registry when creating schema exporters on Confluent Platform or Confluent Cloud Schema Registry.
You can use this Confluent CLI command to retrieve the endpoint, [confluent schema-registry cluster describe](https://docs.confluent.io/confluent-cli/current/command-reference/schema-registry/cluster/confluent_schema-registry_cluster_describe.html).
To learn more, see [Use Schema Linking with private endpoints](/cloud/current/sr/fundamentals/sr-private-link.html#schema-linking-private-endpoints).

## Link schemas from Confluent Platform to Confluent Cloud

You can link schemas from Confluent Platform to Confluent Cloud clusters by using a proxy. Define the proxy settings in the exporter configuration, setting both the proxy host and port.

### Example proxy setup in the exporter configuration file

Typically, you’ll create an exporter configuration file at the time you create the exporter.
The file is typically located at `$CONFLUENT_HOME/etc/schema-registry/exporter-config.json`,
and contains the following fields:

- The name of the exporter
- The subjects to export
- The context type
- The context name
- The proxy settings

For a Confluent Platform to Confluent Cloud exporter, configure proxy settings as follows.

```json
"config": {
   "schema.registry.url": "${CC_SR_ENDPOINT}",
   "basic.auth.credentials.source": "USER_INFO",
   "basic.auth.user.info": "${SR_API_KEY}:${SR_API_SECRET}",
   "proxy.host": "{HTTP_OR_HTTPS}://{PROXY_HOST_FQDN}",
   "proxy.port": "{PORT}"
 }
```

### Test connectivity

Verify that the Confluent Platform Schema Registry can communicate with the Confluent Cloud Schema Registry through the proxy. You can use `curl` commands to test connectivity to the proxy and the endpoint,
as shown in this example:

```bash
curl -u ${SR_API_KEY}:${SR_API_SECRET} -k 'https://{PSRC}.{REGION}.{PROVIDER}.confluent.cloud/schemas/ids/{SCHEMA_ID}' --proxy {HTTP_OR_HTTPS}://{PROXY_HOST_FQDN}:{PORT}
```

### Error handling

If the exporter state returns as PAUSED or fails with connection errors, check for the following.

- DNS resolution issues for the proxy host.
- Ensure the proxy allows bidirectional communication.
- Review logs for specific error messages that can guide troubleshooting.

### Where to learn more

To learn more about this use case, see the following topics:

- [Configure a proxy](/cloud/current/networking/ccloud-console-access.html#configure-a-proxy)
- [Deployment strategies and Schema Linking](#schema-linking-deployment-strategies)

<a id="schema-linking-dr"></a>

## Manage Schema Linking in Disaster Recovery Failover Scenarios

Confluent provides disaster recovery (DR) failover strategies with Cluster Linking and [mirror topics](../multi-dc-deployments/cluster-linking/mirror-topics-cp.md#mirror-topics-concepts).
Failover can be achieved by using commands to [Reverse a source and mirror topic](../multi-dc-deployments/cluster-linking/mirror-topics-cp.md#reverse-source-and-mirror-topic), [Convert a mirror topic to a normal topic](../multi-dc-deployments/cluster-linking/mirror-topics-cp.md#convert-mirror-topic-to-normal-topic), or a combination of both.
DR failover is available on [Confluent Cloud](/cloud/current/multi-cloud/cluster-linking/index.html#use-cases), [Confluent Platform](/platform/current/multi-dc-deployments/cluster-linking/index.html#disaster-recovery),
and [hybrid deployments](/platform/current/multi-dc-deployments/cluster-linking/hybrid-cp.html); a full tutorial is provided in the Confluent Cloud documentation at
[Cluster Linking Disaster Recovery and Failover on Confluent Cloud](/cloud/current/multi-cloud/cluster-linking/dr-failover.html),
as the concepts and workflows are applicable to both Confluent Cloud and Confluent Platform. Also, you can refer to the white paper
[Best Practices for Multi-Region Apache Kafka Disaster Recovery in the Cloud (Active/Passive)](https://assets.confluent.io/m/5be60b2223a8801e/original/20240703-WP-Disaster_Recovery_in_the_Cloud.pdf), which that covers similar information.

### Schemas and mirror topics

Cluster Linking preserves the schema IDs stored in messages, allowing Schema Registry and Schema Linking to work seamlessly with mirror topics.
To consume from a mirror topic that is using schemas, the consumer clients must use a Schema Registry context with the same schema IDs as on the Schema Registry context used by the producers to the source topic.
This setup is described in further detail in [How schemas work with mirror topics](../multi-dc-deployments/cluster-linking/mirror-topics-cp.md#schemas-and-mirror-topics).

### Configure a Schema Registry failover strategy

Given one Schema Registry per environment in a single region, you can set up Schema Registry for DR failover and recovery with Schema Linking, concurrent with your Cluster Linking strategies.
To prepare for failover, you must replicate schemas to the DR region’s Schema Registry. This allows the Kafka applications, connectors, and stream processing applications to
continue reading and writing schema-based data formats even during a disaster scenario. As a best practice, place each Schema Registry in the same region as the Kafka cluster it serves.

In steady state, Schema Linking requires the primary Schema Registry to be in `READWRITE` mode and the secondary Schema Registry to be in `IMPORT` mode, which allows new schemas to be written only by Schema Linking.
This creates an Active/Passive topology for Schema Linking:

- an active Schema Registry on one side that can accept new schemas
- a passive Schema Registry on the other side being synced by means of Schema Linking for DR.

![image](schema-registry/images/sr-dr.png)

### Reverse the schema link as a failover response

In the event of a failover, you must reverse the schema link. This will be necessary in any DR scenario to prevent data loss around schemas during the outage:

- Fail forward (the secondary cluster becomes the primary cluster, the primary becomes the secondary).
- Fail back to the original primary region/cluster.

Note that the secondary cluster is sometimes referred to as the “DR”, “standby”, or “destination” cluster. In this section, it is referred to as “secondary”.
For Cluster Linking, a “secondary” would typically hosts mirror topics based on primary topics (in some Scenarios both clusters mirror).
For Schema Linking, the secondary cluster imports schemas and metadata from the primary as a backup in case of DR.

The primary Schema Registry must be caught up with changes that occurred during the outage. Once the primary Schema Registry is back up, first do the following.

1. Pause the original schema link (which linked from primary to secondary).
2. Put the original primary Schema Registry into `IMPORT` mode. If there were new or modified schemas during the outage, copy over all schemas from the DR (secondary) to the primary.
3. Wait until all schemas registered during the outage are migrated back to the primary (by means of the IMPORT).

At this point, you will follow one or the other set of steps below, depending on whether you are failing forward (original primary and secondary switch places) or failing forward,
where the original primary is once again the primary and original secondary is once again the secondary.

#### Failing forward

If you are failing forward:

1. Create a schema link from the original secondary Schema Registry to the original primary Schema Registry.
2. Put the original secondary (now primary) into `READWRITE` mode. (The original primary is already appropriately in `IMPORT` mode, as it is now the secondary).

#### Failing back

If you are failing back, you must reactivate the original primary Schema Registry. To do this:

1. Stop the active schema link.
2. Set the original primary Schema Registry to `READWRITE` mode.
3. Set the original secondary Schema Registry to `IMPORT` mode.
4. Resume the original schema link from primary to secondary.

<a id="csfle-for-schema-linking"></a>

## Using Client Side Field Level Encryption

Client-side field level encryption (CSFLE) uses a technique called
[envelope encryption](../_glossary.md#term-envelope-encryption), where a [key encryption key (KEK)](../_glossary.md#term-key-encryption-key-KEK) is used to
encrypt [data encryption keys (DEKs)](../_glossary.md#term-data-encryption-key-DEK).

[Schema Registry](../_glossary.md#term-Schema-Registry) exposes a subcomponent called the DEK Registry, which provides
APIs for managing KEKs and DEKs.  The key from the KMS is registered as a KEK to
the DEK Registry, and the DEK Registry will also hold the encrypted DEKs used for
CSFLE.  DEKs are scoped by schema subject.

When CSFLE is configured, Schema Linking copies the DEKs, pointers to the KEKs,
along with rulesets and metadata, from one Schema Registry cluster to another.
Confluent never has access to the KEK itself. The DEK Registry
holds only a reference to it, such as an Amazon Resource Name
(ARN). The key vault uses the KEK to encrypt and decrypt the DEKs.
Confluent supports several different key management services (KMS) and key vaults, including AWS KMS.

To learn more about CSFLE on Confluent Cloud and Confluent Platform, see the following topics:

- [CSFLE on Confluent Cloud](/cloud/current/security/encrypt/csfle/client-side.html)
- [CSFLE on Confluent Platform](/platform/current/security/protect-data/csfle/client-side.html)

### CSFLE considerations for Schema Linking exporters

Schema Linking exporters that copy CSFLE-protected subjects have additional
RBAC and troubleshooting considerations beyond plain subject replication.

<a id="csfle-schema-linking-rbac"></a>

#### RBAC for Schema Linking exporters with CSFLE

Exporters that copy CSFLE-protected subjects require RBAC permissions on
both the subjects and the KEKs that those subjects reference. Subject-level
roles alone are not enough. When an exporter encounters a DEK whose KEK does
not yet exist on the destination, it calls the destination DEK Registry to
register the KEK. Without KEK-scoped permissions on the destination, that call
fails.

On the destination Schema Registry cluster, the exporter principal needs:

- `DeveloperRead` and `DeveloperWrite` (or `ResourceOwner`) on the target
  subjects, scoped to `Subject:<subject-name>` or a prefix.
- `DeveloperWrite` (or `ResourceOwner`) on each KEK referenced by those
  subjects, scoped to `Kek:<kek-name>`.

The exporter principal is the owner of the destination Schema Registry API key
configured on the exporter, or the identity pool or workload identity used
when the exporter authenticates with OAuth. To find the owner of an existing
Confluent Cloud Schema Registry API key, run:

```bash
confluent api-key describe ${DEST_SR_API_KEY} --environment ${DEST_ENV_ID}
```

The `Owner` field in the response is the service account that needs the KEK
role bindings. Create the KEK binding with:

```bash
confluent iam rbac role-binding create \
  --principal User:${EXPORTER_SA} \
  --role DeveloperWrite \
  --environment ${DEST_ENV_ID} \
  --schema-registry-cluster ${DEST_SR_ID} \
  --resource "Kek:${KEK_NAME}"
```

For the full set of KEK-scoped roles and how they apply to CSFLE operations,
see [Access control (RBAC) for CSFLE](/cloud/current/security/encrypt/csfle/client-side.html#access-control-rbac-for-csfle).

<a id="csfle-schema-linking-troubleshooting"></a>

#### Troubleshooting CSFLE-related exporter failures

An exporter that copies CSFLE-protected subjects can enter the `PAUSED`
state with the following error:

```text
User is denied operation Register on Kek: <kek-name>; error code: 40301
```

This error means the exporter principal lacks `DeveloperWrite` on the named
KEK in the destination Schema Registry. Diagnose the failure by following these steps:

1. Identify the principal the exporter authenticates as on the destination
   (the Schema Registry API key owner, or the OAuth identity pool).
2. List the principal’s current role bindings on the destination Schema Registry cluster:
   ```bash
   confluent iam rbac role-binding list \
     --principal User:${EXPORTER_SA} \
     --environment ${DEST_ENV_ID} \
     --schema-registry-cluster ${DEST_SR_ID}
   ```
3. Confirm the output includes a binding with `Resource Type: Kek`, `Name`
   matching the failing KEK (or `*`), and `Role` set to `DeveloperWrite`
   or `ResourceOwner`.

To fix the failure, grant `DeveloperWrite` on the affected KEK as shown
above, then resume the exporter:

```bash
confluent schema-registry exporter resume ${EXPORTER_NAME} \
  --schema-registry-endpoint ${SOURCE_SR_ENDPOINT}
```

<a id="cp-schema-linking-rbac"></a>

## Access Control (RBAC) for Schema Linking Exporters

role-based access control (RBAC) for Schema Linking is available on Confluent Platform.

You must set the following property to enforce RBAC for Schema Linking on Confluent Platform: `schema.linking.rbac.enable=true`.
To learn more about this configuration option, see the entry for `schema.linking.rbac.enable` in [Schema Registry Configuration Reference for Confluent Platform](installation/config.md#schemaregistry-config).

RBAC role bindings for schema exporters on Confluent Platform are shown below. To learn more, see [Use Predefined RBAC Roles in Confluent Platform](../security/authorization/rbac/rbac-predefined-roles.md#rbac-predefined-roles) and [Configure Role-Based Access Control for Schema Registry in Confluent Platform](security/rbac-schema-registry.md#schemaregistry-rbac).

| Role            | All access to exporter endpoints   |
|-----------------|------------------------------------|
| SystemAdmin     | ✔                                  |
| UserAdmin       |                                    |
| ClusterAdmin    | ✔                                  |
| Operator        |                                    |
| SecurityAdmin   |                                    |
| ResourceOwner   | ✔ (only if Subject = \* )          |
| DeveloperRead   |                                    |
| DeveloperWrite  |                                    |
| DeveloperManage |                                    |

Table Legend:

- ✔  = Yes
- Blank space = No

#### NOTE
- If you have schema exporters running, removing permissions for one or more subjects
  for an account will not prevent that user account from accessing these subjects in the DESTINATION
  if the DESTINATION Schema Registry is different from the source. Therefore, as a precaution you should also
  remove permissions for these subjects for the account in the DESTINATION Schema Registry.
- The schema exporter will stop running if permissions are removed from the DESTINATION Schema Registry for the account that created the schema exporter.
- If you want to grant permissions to specific schema contexts, you can do so using the Prefix rule and grant permissions with prefix as `:.schema_context:*`.
- RBAC is the only way to implement access control for managing schema exporters. The [Schema Registry ACL Authorizer for Confluent Platform](../confluent-security-plugins/schema-registry/authorization/sracl_authorizer.md#confluentsecurityplugins-sracl-authorizer) does not authorize exporter management operations. ACLs still govern the schemas the exporter reads and writes. The user that runs the exporter needs subject permissions on both the source and destination Schema Registry (see [ACLs for the schema exporter running user](#cp-schema-linking-acls)).

<a id="cp-schema-linking-acls"></a>

## ACLs for the schema exporter running user

On the source Schema Registry, grant the exporter’s running user read access to subjects and compatibility settings:

```bash
sr-acl-cli --config <source-sr.properties> \
  --add -s '*' \
  -p <principal> \
  -o SUBJECT_READ:SUBJECT_COMPATIBILITY_READ:GLOBAL_COMPATIBILITY_READ
```

On the destination Schema Registry, grant the running user write access and the ability to read and set per-subject compatibility:

```bash
sr-acl-cli --config <dest-sr.properties> \
  --add -s '*' \
  -p <principal> \
  -o SUBJECT_WRITE:SUBJECT_COMPATIBILITY_WRITE:SUBJECT_COMPATIBILITY_READ
```

## OAuth on Schema Registry server

Starting with Confluent Platform version 7.7, the Schema Registry server can be configured for Open Authentication (OAuth) to
secure Schema Registry services and clients. To learn more about these broker, Schema Registry server, and client configurations,
see [Configure OAuth for Schema Registry in Confluent Platform](security/oauth-schema-registry.md#schemaregistry-oauth).

<a id="sr-license-schema-linking"></a>

## License

A Confluent Enterprise license is required for exporters and Schema Linking. To learn more, see [License](../installation/license.md#sr-license) on the Schema Registry documentation.

Starting with Confluent Platform version 8.1, licensing restrictions enable you to set up an exporter on Schema Registry only if one of these conditions is met:
- You have a [Customer-Managed Confluent Platform for Confluent Cloud](../installation/license.md#customer-managed-cp-cc-license) subscription.
- You have a Confluent Enterprise license.
- You are connecting to Confluent Cloud Schema Registry only.

This license allows you to use self-managed Confluent Platform components exclusively with Confluent Cloud services, but not connect to a Confluent Platform destination registry.

## Related content

- To learn more about configuring exporters, see [Configuration Reference for Schema Registry Clients on Confluent Platform](sr-client-configs.md#sr-client-configs).
- To learn more about exporter-specific configurations on the Schema Registry server, see the entries prefixed with `exporter.` and `password.encoder.` in [Schema Registry Configuration Reference for Confluent Platform](installation/config.md#schemaregistry-config).
- For API usage examples, see [Exporters](develop/api.md#schemaregistry-api-exporters) in the [Schema Registry API Reference for Confluent Platform](develop/api.md#schemaregistry-api).
- To learn about using Schema Linking on Confluent Cloud, see [Schema Linking on Confluent Cloud](/cloud/current/sr/schema-linking.html).
