Data Sharing
The C3 Agentic AI Platform provides the ability to share data across applications and environments within the same cluster.
The C3 Agentic AI Platform ensures data is segregated for each application. This happens by default, so you are not required to perform extra steps when implementing the data model of your application. You can modify the default behavior for data sharing between applications and environments.
For applications or environments to share data, they must be on the same cluster.
To enable data sharing, the C3 Agentic AI Platform introduces the concept of a Db.Domain to help manage how shared data is organized.
A Db.Domain is a logical grouping of data. It refers to a collection of interrelated data pertaining to a common purpose, object, or concept. A Db.Domain may span one or more application packages. However, a Db.Domain can only be deployed in a single application.
For more information about the file naming conventions and where files in a package belong, see Package Management Overview.
Db.Domain metadata
A Db.Domain is defined using metadata and must be authored in the package that implements the domain or data to be shared. Type definitions that are a member of the Db.Domain reference the Db.Domain name in their implementation. The json file below contains a single key/value pair representing the name of the Db.Domain, datalake. The json file must be stored in the metadata folder of a package:
<pkg>/metadata/Db.Domain/*.json.
{
"name": "datalake"
}The corresponding Type definition is below and must be in the src folder of your package.
@db(domain='datalake')
entity type DataLakeRepository mixes Obj schema name 'DTLKRPST' {
fieldID: string
}The @db(domain='datalake') annotation declares that this Type belongs to the datalake Db.Domain.
Db.Domain configuration
Applications that consume data from a Db.Domain must declare their dependency on the Db.Domain in two ways:
- By defining a dependency on the
Db.Domainapplication package. - Authoring a
Db.Domain.Configand either seeding it into the application package or setting theDb.Domainconfiguration after the application is deployed. When defining theDb.Domain.Config, set thenamekey to the name of theDb.Domainand theappkey to the name or the identifier of the hostingDb.Domainapplication, shown below:
{
"name": "datalake",
"app": "datalake"
}If the Db.Domain application and consuming application are deployed in the same environment, the Db.Domain.Config can specify the application name only (for example, datalake). If the Db.Domain application resides in a different environment, the fully qualified application identifier must be specified. For example, {cluster}-{env}-{app}
To set the Db.Domain configuration, run the command:
Db.Domain.Config.forName('datalake').setConfigValue('app', '{cluster}-{env}-{app}').
In the above command, datalake is the name of the Db.Domain and {cluster}-{env}-{app} is the fully qualified application identifier.
Recommendations for data sharing and management
In the C3 Agentic AI Platform, data-shared Types are designed to synchronize and share data across multiple instances. While the platform supports both read and write access to data-shared Types at the permission level, write access should be treated as a governed, advanced scenario, not the default.
Default to read-only access for consuming applications: By default, roles assigned to consuming applications should grant read-only access to data-shared Types. This helps preserve data integrity, reduce the risk of unintended modifications, and ensure predictable cross-environment synchronization.
Grant write access only to trusted or designated owner applications: Read-write access to data-shared Types should be granted only to explicitly designated applications or services that act as data owners. These scenarios should meet the following criteria:
Data ownership and responsibilities are clearly defined
Cross-application write patterns are well understood and controlled
Governance, validation, and lifecycle management for the shared Types are established
Avoid join queries between data-shared and non-data-shared Types: Performing join queries that span a data-shared Type and a non-data-shared Type can result in data inconsistencies. The non-data-shared Type's data may not be in sync with the data-shared Type, leading to unreliable query results.
Split cross-domain queries into separate steps: If you need data from both a data-shared Type and a non-data-shared Type, avoid combining them in a single join query. Instead, first query the data-shared Type, then use those results to query the non-data-shared Type in a second call. This two-step approach ensures consistent results.
Restrict modifications of data shared Types across packages: Do not remix data-shared Types. To maintain data consistency across the system, data-shared Types within a package should remain unmodified in other packages. Instead, consider expanding the common data-shareable data model in data lake by adding fields and relationships as a superset.
Develop applications with data lake setup: Ensure that applications are developed with a data lake configuration to support efficient data sharing.
Enable data sharing in non-production environments: Configure data sharing in non-production environments so that data is ingested once and all team members (across multiple applications or instances) work with a complete, shared dataset rather than duplicating ingestion per app.
Enable data sharing in production environments: Configure data sharing in production environments so that applications with a common data model load data once, avoiding redundant storage and ingestion across multiple applications.
Limitations and considerations
Incompatibility exists between shared and non-shared data in certain scenarios. For example, in C3 AI Demand Forecasting, updating the Type definition of
DemandForecastSubjectwith a stored calculation that references demand information (for example,link SalesOrderLine) can cause issues. This scenario is incompatible with data sharing because the stored calculation would attempt to join a shared table (likeSalesOrderLine) with a non-shared table (DemandForecastSubject).The C3 Agentic AI Platform’s data sharing architecture currently prevents certain operations, such as filtering on a non-shared Type when it involves data from shared domains.
Data sharing is designed to facilitate efficient resource use, particularly in environments with multiple non-production scenarios. However, the current lack of support for performing joins across shared and non-shared
Db domainscan affect complex data access logic. This limitation may require rethinking the use of data sharing in certain scenarios, particularly when planning to implement intricate data operations across domains.If you have a fixed data model, data sharing works well for development. If you have a data model that's in flux and being edited by multiple developers, data sharing is not recommended.
Do not remix data shared Types in other packages to add fields and relationships.
Avoid loading the same data with a common data model multiple times for different applications.
Do not data share Types without proper validation.
Do not fetch or access non-data-shared Types from data shared Types.
The @db(domain=...) annotation
The @db annotation accepts a domain field that specifies which Db.Domain the Type belongs to. Any entity type annotated with @db(domain='<name>') will have its data stored and accessed through the named Db.Domain.
Sharing data between applications
In the following example there are two applications, datalake and WindTurbine. The datalake application manages measurement, event, and vibration data coming from various historians. The WindTurbine application manages asset, asset hierarchy, and work order data, and has a dependency on the datalake application.
The C3 Agentic AI Platform does not support joins between shared and non-shared tables. This also extends to joins across Db.Domain boundaries. While stored calculations, which are refreshed and not based on SQL joins, can imply a joining of shared and non-shared tables, this is not the case. The C3 Agentic AI Platform's design ensures that stored calculations are run during an earlier stage of the workflow without errors related to data sharing constraints.
Datalake application
Measurement data modeled by the WindTurbineMeasurement resides in the datalake Db.Domain, and is deployed in the datalake application.
@db(domain='datalake'
datastore='kv',
persistenceOrder='timestamp',
partitionKeyField='turbineId')
entity type WindTurbineMeasurement {
turbineId: string
gearOilTemperature: double
generatorRotationSpeed: double
timestamp: datetime
activePower: double
}Db.Domain metadata, in the <pkg>/metadata/Db.Domain/ folder:
{
"name": "datalake"
}The datalake application produces data.
WindTurbine application
The WindTurbine application consumes measurement data from the datalake application, so two things must be configured:
- A package dependency on
datalakeinWindTurbine.c3pkg.json:
See Package Management Overview documentation for more information about dependencies and the folder structure within a package.
{
"name" : "WindTurbine",
"version": "1.0.0",
"dependencies": {
"datalake": "*"
}
}- A
Db.Domain.Configfile in the<pkg>/config/Db.Domain.Config/folder that references the target application:
{
"name": "datalake",
"app": "datalake"
}With the above configurations set and applications deployed, the WindTurbine application is almost ready to access data from the datalake application. However, before data access can occur, you must first authorize the WindTurbine application.
Authorization
Authorization for data sharing utilizes the C3 Agentic AI Platform's role based access control security framework. When sharing data, the Db.Domain owner is responsible for granting access and permissions to client applications. Access is granted to an application using the identity of the application. To grant an application access to a Db.Domain, execute the command below as an environment administrator in the Db.Domain application.
C3.app().allowAccess("<client_app_id>", [<roles to assign to the client app>]);It is recommended that application specific roles be used to grant access to client applications. This ensures that access is narrowly scoped to the data that client application(s) require and the operations they need to perform on the data.
It is recommended that the role below be used as the basis for application specific roles. The role below provides read-only access to client applications requiring access to Measurement and MeasurementSeries data (illustrative example only). The permissions listed below are required to access ingested and normalized time series data and header records.
{
"id" : "Measurements.ReadOnly.Role",
"permissions" : [
"allow:Cassandra::get",
"allow:Cassandra::config",
"allow:Cluster::env",
"allow:ContentValue::readObjs",
"allow:DataPartitionBucketValue::readObjs",
"allow:Db.Domain::config",
"allow:Db.Domain::hasArbitraryFolderHierarchy",
"allow:Db.Domain::isValidMetadataJson",
"allow:Db.Domain::metadataFolder",
"allow:Env::app",
"allow:KvDataPartition::canBucket",
"allow:Measurement:read:",
"allow:MeasurementSeries::fetchObjStream",
"allow:MeasurementSeries::fetchNormalizedData",
"allow:MeasurementSeries:read:",
"allow:SqlKvStore::get",
"allow:SqlKvStore::config"
]
}More information about roles and permissions can be found in the C3 AI Security Guide.
Example application with package structure
This example highlights the package structure of a working WindTurbine application. The main folder structure includes two packages, assets and measurements. The measurements is a dependency.
The assets package consumes the data, and the measurements package produces the data.
Assets
assets/assets.c3pkg.json: The package structure file.assets/config/Db.Domain.Config/measurements.json: Configuration data.assets/src/WindTurbine.c3typ: The main WindTurbine*.c3typfile.
The folder structure for the assets package:
assets (folder)
|
|--- config
| |
| |--- Db.Domain.Config
| | |
| | |-- measurements.json
| |
|--- src
| |
| |-- WindTurbine.c3typ
|
|--- assets.c3pkg.json assets/assets.c3pkg.json
The assets.c3pkg.json includes a dependency.
{
"name" : "assets",
"description" : "Demo workshop package",
"version": "1.0.0",
"dependencies": {
"measurements": "*"
}
}assets/config/Db.Domain.Config/measurements.json is the Db.Domain.Config configuration file.
{
"name": "measurements",
"app": "measurements"
}assets/src/WindTurbine.c3typ is the Type file.
entity type WindTurbine schema name 'WNDT' {
turbineId: string
location: string
power: int
manufacturer: string
measurements: [WindTurbineMeasurement] (turbineId, turbineId)
}Measurements
The dependency for the above assets package is below in a folder called measurements.
measurements/measurements.c3pkg.json: The package structure file for the dependency.measurements/config/Db.Domain.Config/measurements.json: Configuration file.measurements/metadata/Db.Domain/Measurements.json: Domain file.measurements/metadata/Role/Measurements.ReadOnly.Role.json: Role required to read measurement data. This role will be granted to applications that require read-only access to measurement data.measurements/src/WindTurbineEvent.c3typ:*.c3typ filemeasurements/src/WindTurbineMeasurement.c3typ:*.c3typ filemeasurements/src/WindTurbineVibration.c3typ:*.c3typ file
The folder structure for the measurements package:
measurements (folder)
|
|--- config
| |
| |--- Db.Domain.Config
| | |
| | |-- measurements.json
| |
|--- metadata
| |
| |-- Db.Domain
| |
| |-- Measurements.json
|
| |-- Role
| |-- Measurements.ReadOnly.Role.json
|
|--- src
| |
| |-- WindTurbineEvent.c3typ
| |-- WindTurbineMeasurement.c3typ
| |-- WindTurbineVibration.c3typ
|
|--- measurements.c3pkg.json measurements/measurements.c3pkg.json
{
"name" : "measurements",
"description" : "Demo workshop package"
}measurements/config/Db.Domain.Config/measurements.json
{
"name": "measurements",
"app": "measurements"
}measurements/metadata/Db.Domain/Measurements.json
{
"name": "measurements"
}measurements/metadata/Role/Measurements.ReadOnly.Role.json
{
"id" : "Measurements.ReadOnly.Role",
"permissions" : [
"allow:Cassandra::get",
"allow:Cassandra::config",
"allow:Cluster::env",
"allow:ContentValue::readObjs",
"allow:DataPartitionBucketValue::readObjs",
"allow:Db.Domain::config",
"allow:Db.Domain::hasArbitraryFolderHierarchy",
"allow:Db.Domain::isValidMetadataJson",
"allow:Db.Domain::metadataFolder",
"allow:Env::app",
"allow:KvDataPartition::canBucket",
"allow:SqlKvStore::get",
"allow:SqlKvStore::config",
"allow:WindTurbineMeasurement:read:",
"allow:WindTurbineMeasurementSeries::fetchNormalizedData",
"allow:WindTurbineMeasurementSeries::fetchObjStream"
]
}measurements/src/WindTurbineEvent.c3typ
entity type WindTurbineEvent mixes Obj schema name 'EVENT' {
start: datetime
turbineId: string
end: datetime
event_code: string
}The measurements/src/WindTurbineMeasurement.c3typ type uses the @db annotation to indicate the Db.Domain the Type is a member of, in this case measurements.
@db(domain='measurements')
entity type WindTurbineMeasurement mixes Obj schema name 'WT_BLB_MSRMNT' {
gearOilTemperature: double
turbineId: string
generatorRotationSpeed: double
timestamp: datetime
activePower: double
}measurements/src/WindTurbineVibration.c3typ
@db(domain='measurements')
entity type WindTurbineVibration mixes Obj schema name 'WINDTURBINEVIBRATION' {
turbineId: string
energyNear2xRpm: double
energyNear1xRpm: double
measurement_date: datetime
}Local App JVM Execution for Db.Domain
In certain deployment scenarios, remoting calls to the target app over the network introduces unnecessary latency. The callAppInLocalJvm option on Db.Domain.Config allows the consumer to execute operations on the target app's types within its own JVM process, loading the target app's packages locally rather than making network calls.
Prerequisites
- The target app must grant access to the consumer app's identity using
C3.app().allowAccess(...)(see Authorization above). This must be done from the target app's console directly; it is not currently possible to configure this from the consumer app. - In a multi-node environment,
App.allowOtherAppsPkgsLocallymust be enabled on the consumer app's configuration. Without this, the target app's packages will not be loadable by the consumer and operations on this domain will fail.
Configuration
To enable local JVM execution for a Db.Domain, add callAppInLocalJvm: true to the Db.Domain.Config:
{
"name": "datalake",
"app": "datalake",
"callAppInLocalJvm": true
}How it works
When callAppInLocalJvm is enabled:
- The consumer loads the target app's packages into its own JVM process
- Operations on types in the configured
Db.Domainexecute locally instead of being remoted over the network - Results are still serialized/deserialized to maintain proper data isolation
- The original context is restored after the call completes
When to use
This feature is appropriate when:
- Network latency between the consumer and target app is a concern
- The consumer and target app are deployed in the same cluster
Limitations
This feature is marked as @beta. Use with caution in production environments.
- Only supported for data sharing within the same cluster. Cross-cluster sharing is not supported with this option.
- The metadata of the target app and the consumer app may differ
- There are currently no guards to prevent data corruption if metadata is conflicting
- This setting only affects the
appremoting, notdatastoresconfiguration