Functionality
Cirata Symphony extensions can use common functionality of Cirata Symphony to take full advantage of the platform. These include:
Capabilities : Control fine-grained access to the services and endpoints exposed or use by an extension with capabilities.
Routes : Routes are URLs serviced by the Cirata Symphony user interface. Provide custom user interface for specific paths.
Pages : Extensions can provide custom user interfaces with React and Typescript code using any third-party ECMAScript Module.
Menus : Provide quick access to specific routes and pages with menu entries.
Widgets : Dashboard views customized by a user can include UI components from extensions called widgets.
Services : Service endpoints from extensions are made available through Cirata Symphony with security, horizontal scaling and fault-tolerance.
APIs : Extension services require only an OpenAPI specification to be presented as REST endpoints on Symphony.
Metrics : Cirata Symphony aggregates metrics from extensions, and ensures that a client sees only the metrics that their API Key allows.
Usage Reporting : Extensions report usage dimensions to Cirata Symphony for licensing, enforcement, and chargeback. Dimensions are converted to license units using vendor-defined rates.
Storage : Shared storage can be used by extensions to retain information that can be accessed and updated over time. They also benefit from the ease with which changes to storage state can be reflected in immediate updates to their user interfaces.
Capabilities
Cirata Symphony uses capabilities to control access to extensions. Each API Key defines what its token holder can do: whether they can publish or subscribe to the messaging channels that extensions use to communicate. This unified messaging approach provides consistent security that scales as new extensions are added.
Extensions do not expose functionality directly to clients, but rely on the Symphony server to mediate interactions. API Keys limit what a client can access, and what an extension provides. These controls are enforced consistently whether access to the functionality of extensions occurs through the REST APIs exposed from Cirata Symphony, from client libraries used for programmatic access, or between extensions when they interact with one another.
Capabilities can be defined for specific subjects, such
as cirata.extensions.intelligence.mcp, or for wildcard
definitions of subjects, allowing for expansion of their scope
dynamically. The methods for defining capabilities are provided by the
"allow" mechanism of
the subject-based messaging of
the NATS authorization model.
Extensions define their capabilities as subject-level "allow" permissions for the subjects that can be subscribed and published to. They are used during the extension registration process to inform the user of the recommended capabilities for operation of the extension to make the creation of an API Key simple, and during operation to allow for dynamic updates by extensions that modify behavior and functionality at runtime.
Routes
Users can interact with Cirata Symphony through their web browser. The Symphony user interface includes a set of paths associated with common functionality, and extensions can add additional paths using Routes. Each route must map to a Page so that the user interface can present functionality from the extension.
Pages
One means for an extension to provide user interface functionality is with pages, which can be associated with a Route to be presented when the user navigates to the route's location.
Pages are provided by extensions to Cirata Symphony as React and Typescript code,
which Symphony transpiles so that they can be rendered in the user's
browser. This includes resolving and obtaining ECMAScript Modules that
are referenced from a page's implementation. These references are mapped
to the /npm endpoint exposed from the Symphony service,
which delegates their retrieval to a content delivery network hosting ESM
packages. In this way, all browser communication is to the Cirata Symphony
instance, while arbitrary third-party modules can be used by extensions
for the implementation of their user interfaces.
Menus
The Cirata Symphony user interface includes navigation menus that present a selection of locations for easy access. Extensions can extend the menus presented by describing additional items to include. Extensions provide information about each additional menu item grouping, name, target and icon to augment the common navigation menu items.
Widgets
The dashboard view in Cirata Symphony presents a selection of widgets that can be arranged by the user to their particular needs. Extensions can expose widgets to Symphony for use on the dashboard, so that the user can compose their own custom view that includes functionality provided by extensions. As with pages, widgets are composed of React and Typescript code from extensions that is transpiled by Cirata Symphony for display.
The Cirata Symphony dashboard widget selection and layout is persisted for each user, providing a simple customization of their default view in Symphony.
Services
Extensions can implement any functionality required for use by other extensions or by clients of Cirata Symphony by providing services. By exposing functionality as a service, an extension gains a range of benefits from Symphony:
- Discovery - Services can be discovered dynamically, allowing functionality that expands at runtime, such as the Cirata Intelligence extension, which uses service discovery to present MCP tools from the services provided by extensions.
- Security - Cirata Symphony enforces a consistent security model that uses API Keys to define specific capabilities, and which entities can access or provide them.
- Monitoring - Symphony captures metrics on service operation and exposes them with the standard functionality of NATS.
- APIs - Symphony automates the generation of REST APIs from services, so that clients can invoke a REST API on the Symphony server that is serviced by the implementation in an extension.
Individual instances of an extension runtime can coordinate their responses to service invocations, allowing a variety of communication models that include horizontal scalability, shared queues, publish/subscribe, request/reply, and distributed consensus.
APIs
By providing an OpenAPI specification, extensions can expose their services as REST APIs accessible from the Cirata Symphony server. Symphony routes extension requests to their services and enforces consistent security by requiring a Bearer token in the authorization header.
Other extensions can also use the metadata that may be included in an
OpenAPI specification provided by an extension to augment their own
functionality. For example, the Cirata Intelligence extension uses
the description property of an OpenAPI operation as a
natural language representation of the service as an MCP tool.
Extensions also benefit from being able to operate in environments for which there is no inbound network connectivity available, but can still expose their functionality for external use through the Symphony server. Consumers of their APIs need only establish an HTTPS connection to the Cirata Symphony service and present a token for an API Key with the capabilities to use the services required.
REST API Response Format
When an extension endpoint is invoked through the Symphony REST API proxy, Symphony accepts two response conventions. Handlers can choose whichever fits their use case.
Option 1: Raw response (simplest)
The handler responds with the content directly—no wrapper required.
Symphony auto-detects the content type: valid JSON is served as
application/json, and anything else is served as text/plain.
Python—returning JSON:
async def my_handler(req):
result = {"status": "ok", "items": [1, 2, 3]}
await req.respond(json.dumps(result).encode("utf-8"))
Java—returning plain text:
private void pongHandler(ServiceMessage msg) {
Connection nc = runtime.getUserConnection();
msg.respond(nc, "pong".getBytes());
}
Go—returning JSON:
func processHandler(req micro.Request) {
result := map[string]string{"status": "ok"}
data, _ := json.Marshal(result)
req.Respond(data)
}
Option 2: Wrapped response (full control)
For control over the HTTP status code, content type, or response headers,
wrap the response in a JSON object with a body field:
{
"body": <any JSON value or string>,
"statusCode": 200,
"contentType": "text/plain",
"headers": {"X-Custom-Header": "value"}
}
| Field | Required | Description |
|---|---|---|
body | Yes | The response payload. For JSON content types, this can be any JSON value. For other content types (e.g. text/plain), use a string. |
statusCode | No | HTTP status code (100–599). Defaults to 200 if omitted. |
contentType | No | MIME type for the response. Defaults to application/json if omitted. |
headers | No | Additional HTTP headers to include in the response. |
Both camelCase (statusCode, contentType) and lowercase
(statuscode, contenttype) field names are accepted.
Python example—text/plain with custom status:
async def ping_handler(req):
response = json.dumps({
"body": "pong",
"statusCode": 200,
"contentType": "text/plain"
})
await req.respond(response.encode("utf-8"))
Python example—JSON with custom headers:
async def my_api_handler(req):
request_data = json.loads(req.data.decode("utf-8"))
result = process(request_data)
response = json.dumps({
"body": {"status": "ok", "data": result},
"statusCode": 200,
"contentType": "application/json",
"headers": {"Cache-Control": "no-cache"}
})
await req.respond(response.encode("utf-8"))
How detection works
Symphony determines which format is in use by checking whether the
response is a JSON object containing a body key. If it is, the response
is treated as a wrapped response and the body value is extracted. If
not, the entire response is treated as raw content.
Both formats work identically whether the client reaches the endpoint through the REST API or directly over NATS.
Metrics
Cirata Symphony and its extensions emit OTLP (OpenTelemetry) metrics that the Observability Extension collects over NATS. Collected metrics can be queried via the CLI or exported to external backends such as Prometheus, Grafana, or InfluxDB.
cirata observability find_metrics
cirata observability query_scalar_metrics http.requests.total
See Observability for how to instrument your extension with metrics, and Monitoring for the platform's built-in metrics.
Usage Reporting
Extensions that consume licensed resources should report their usage to Symphony for licensing, enforcement, and chargeback. Usage is reported as dimensions—named measurements that are meaningful to the extension (e.g., tables replicated, bytes transferred, queries executed). Symphony converts these dimensions into license units using the rates embedded in the active license.
How It Works
- The extension calls a
report_usage/reportUsage/ReportUsagemethod on the extension runtime, passing a map of dimension names to numeric values. - Symphony receives the report via the
cirata.services.usage.reportNATS subject. - Symphony selects the best license to charge—only licenses that define a unit rate for the reporting extension are considered, and the soonest-expiring license with remaining capacity is chosen first. Each dimension is converted to units (dimension value multiplied by the rate for that dimension).
- The usage record is stored with both the raw dimensions and the computed units.
- The response includes the computed units and the current enforcement state.
Dimensions
Dimensions are extension-specific measurements. Choose dimension names that are meaningful for the work your extension performs:
| Extension Type | Example Dimensions |
|---|---|
| Data replication | tables_replicated, bytes_transferred_gb |
| Query engine | queries_executed, rows_scanned |
| AI/ML | inference_requests, tokens_processed |
| Storage | objects_managed, storage_gb |
Dimension names must match the keys defined in the license's unit rates
for the extension. If any reported dimension does not have a corresponding
rate—or if no license or unit rate covers the extension at all —
Symphony records the usage for audit purposes and adds the extension
to the disabled_extensions list. This is per-extension enforcement:
the global enforcement state (OK, Warning, etc.) is unaffected and other
extensions continue to operate normally.
Enforcement
Symphony manages enforcement through two complementary mechanisms:
1. Response-based—Every report_usage response includes the
current enforcement state, so extensions can check it inline after
each report.
2. Broadcast-based—Symphony publishes the enforcement state
on the cirata.symphony.enforcement NATS subject whenever it changes.
Extensions can subscribe to this subject to receive updates in real time,
including changes triggered by other extensions, license uploads, or
administrative actions.
3. On-demand—Extensions can request the current enforcement state
at any time (e.g., on startup) by sending a request to
cirata.services.usage.enforcement.
| Enforcement Status | Meaning |
|---|---|
enforcement_ok | Normal operation—units available |
enforcement_warning | 90% or more of licensed units consumed |
enforcement_grace | Units exhausted or license expired, within vendor-set grace period |
enforcement_enforced | Grace period expired or no valid license—stop licensed operations |
Responding to Enforcement
Extensions should respond appropriately to enforcement state changes. What this means depends on the extension:
- Stop or throttle work—A data replication extension might
pause replication when
enforcement_enforcedis received and resume when the state returns toenforcement_ok. - Warn the user—A UI-based extension might display a banner
when
enforcement_warningorenforcement_graceis received. - Log for audit—All extensions should log enforcement state changes for troubleshooting.
The enforcement state broadcast includes:
| Field | Type | Description |
|---|---|---|
status | string | One of the enforcement statuses above |
message | string | Human-readable description of the enforcement reason |
remaining_units | number | Remaining licensed units (may be negative) |
timestamp | string | ISO 8601 timestamp of the state computation |
grace_expires_at | string | ISO 8601 timestamp of grace period expiry (only present during grace) |
disabled_extensions | string[] | Extensions individually disabled due to missing license coverage |
grace_extensions | string[] | Extensions covered only by grace-period licenses, not by any fully active license |
Extensions should check the global status, the disabled_extensions list,
and the grace_extensions list to determine their own operational state. An
extension may be disabled via disabled_extensions even when the global status
is enforcement_ok. An extension in grace_extensions is still operational
but should expect enforcement if the grace period expires without a new license.
NATS Subjects
| Subject | Type | Description |
|---|---|---|
cirata.services.usage.report | Request/Reply | Report usage dimensions, receive units and enforcement state |
cirata.services.usage.enforcement | Request/Reply | Fetch current enforcement state without reporting usage |
cirata.symphony.enforcement | Publish/Subscribe | Real-time enforcement state broadcasts |
See the language-specific extension guides for API details and code examples: Python, Java, Go.
For administrator documentation on licensing and usage, see the Administration Guide.
Storage
Extensions can use functionality provided by Cirata Symphony to store information
in a
NATS Key/Value store.
Extensions can create and manage their own KV buckets for persistent state,
using the SDK's addBucket() method to declare buckets during registration.
Storage Security
Extension API keys are scoped so that extensions cannot access sensitive platform buckets such as user records, API keys, RBAC roles, or license data. Extensions have full access to:
- Their own custom buckets (created via
addBucket()) - Platform configuration buckets (configurations, dashboards, enforcement state, etc.)
Extensions cannot access or enumerate:
symphony_api_keys— credential storagesymphony_users— user accounts and PIIsymphony_roles/symphony_role_assignments— RBAC definitionssymphony_licenses,symphony_usage,symphony_settings— administrative data- JetStream stream listings (stream enumeration is blocked)
These restrictions are enforced at the NATS permission level via the extension's API key JWT and cannot be bypassed by extension code.