Skip to main content

Custom Bundles & Extensibility

Custom bundles are how you deploy your own adapter definitions, plugins, and artifacts to a running Conncentric installation. A bundle is a single .zip file hosted at any URL your Kubernetes cluster can reach. The Installer job downloads and applies it automatically during helm install or helm upgrade.

For the end-to-end build pipeline that produces the .zip, see CI/CD Integration.

Bundle anatomy

A bundle is a .zip archive with up to three top-level directories. The directory names must be exact: definitions, plugins, and artifacts. Any other entries are ignored.

my-bundle.zip
definitions/ # Adapter definition files (JSON)
plugins/ # Compiled plugin JARs
artifacts/ # Reference files (data dictionaries, schemas)
DirectoryWhat it containsRequired
definitions/JSON adapter definitions. Each file defines one adapter and its pipeline. These are the same files you export from the Portal.At least one of the three directories must be present.
plugins/Compiled .jar files built with the Plugin SDK. Uploaded to the Orchestrator and made available to every adapter pod.Optional.
artifacts/Non-sensitive reference files (data dictionaries, schema definitions). Uploaded to the Orchestrator's artifact store. Do not include certificates or private keys here.Optional.

Adapter definitions and plugins are linked by plugin key, not by filename. Each definition lists the plugins it depends on in a requiredPluginKeys array, and each plugin declares its own key in its JAR manifest as Plugin-Id (set in the plugin's shadowJar manifest block). The installer matches the two by this key, so a definition loads only when a plugin whose Plugin-Id equals every entry in its requiredPluginKeys is present. The JAR filename plays no part in this match.

The Two-Pass Installer

The Installer job runs after every helm install and helm upgrade. It applies configuration in two ordered passes so the official baseline is always present before anything custom layers on top.

PassSourceWhat it does
Pass 1: Base distributionThe official plugins listed in installer.plugins.Always runs first. Loads the protocol plugins shipped with the platform image.
Pass 2 and beyond: Custom bundlesEach URL in installer.customBundleUrls, applied in the order listed.Downloads each bundle and applies its definitions/, plugins/, and artifacts/ on top of the base. Multiple bundles layer sequentially.

Pass ordering means a bundle can rely on every official plugin being installed already. It also means a later custom bundle can override a value set by an earlier custom bundle, which is the supported way to compose a shared corporate configuration with team-specific overrides.

Idempotency guarantees

The installer is idempotent. Running helm upgrade against an unchanged bundle URL is safe and produces no observable churn.

Item stateInstaller action
Already present and unchangedLeft alone. No write is issued.
New (not yet on the cluster)Created.
Present but modified in the bundleUpdated in place.
Plugin JARRe-uploaded on every run, so the latest version of the JAR is always applied. There is no version comparison; the latest upload wins.

Because every pass is idempotent, restoring a previous configuration is a URL change plus helm upgrade. The installer re-applies that bundle's contents: unchanged items are skipped, changed items are updated.

Promoting across environments

The bundle .zip is immutable, and its definitions are applied exactly as they appear: there is no placeholder substitution at apply time. An unresolved ${VAR} token in a definition fails the import before anything is created, with an error naming every offending token and the file it sits in.

Imports are also gated on SDK compatibility. A bundle whose bundle-meta.json records that it was built against a different SDK major, or a newer SDK minor than the runtime, is rejected before anything is applied, with an error naming both versions; this heads off plugins that would otherwise fail at load. The same check runs per plugin against the Built-Against-SDK attribute stamped in each jar, so directly uploaded plugins are covered too. Artifacts built against an older minor import normally (newer runtimes aim to keep older plugins working), and artifacts without version metadata (for example a bundle exported from the Portal, or a jar built before stamping) import normally with the check skipped.

Environment-specific values are handled by kind:

Credentials stay out of the bundle entirely. A ${env:VAR_NAME} reference is the one token that imports as text on purpose: it resolves inside the adapter pod at wiring time from a Secret you mount via adapter.extraEnvFromSecrets, so the value never appears in bundles, Helm values, exports, or support bundles. Every environment supplies its own value. See Security.

Topology values (hosts, ports, venue endpoints) are concrete in the definitions. Where they differ between environments, produce the per-environment definitions in your promotion pipeline, either by exporting from each environment or by templating a shared source before packaging (for example with envsubst). Baked values stay visible in the Portal and validated by the platform, which is what you want for the values you will be reading during an incident. Each environment pins its own bundle URL.

PropertyGuarantee
Bundle immutabilityEach environment's bundle.zip is a fixed, versioned artifact.
Promotion auditabilityThe artifact deployed to Production is the artifact that was tested in Staging, identified by its versioned URL.
Configuration driftLimited to Helm values and the bundle contents, both versioned per environment in your GitOps repository.

Plugin versioning

Custom plugins are uploaded on every install and upgrade; the latest upload wins. Custom plugins are additive over official plugins (Pass 1 loads official plugins, Pass 2 layers customs on top), so the two cannot conflict.

WorkflowSteps
Update a custom pluginBuild a new JAR, package it in a new bundle, update the bundle URL in Helm values, run helm upgrade.
Restore a previous plugin versionPoint the bundle URL at the previous version, run helm upgrade.

Version your bundle URLs explicitly (for example, custom-bundle-v1.2.0.zip) so restoring a previous version is a single URL change. See CI/CD Integration for automating the build and upload steps.

The bundle .zip must be hosted at a URL the Installer job can reach from inside the cluster.

EnvironmentRecommended hostingWhy
Cloud (AWS, GCP, Azure)Cloud object storage (S3, GCS, Azure Blob Storage) with workload identity access.Versioned, durable, no extra infrastructure. The Installer pod authenticates via your cloud's native workload identity (IRSA, Workload Identity, Pod Identity) with no credentials to manage.
On-premises or air-gappedInternal HTTP server or artifact repository (Artifactory, Nexus).Reachable from the cluster without internet access. Use whatever your organization already uses for internal artifact distribution.

In both cases, use explicit version identifiers in the URL. Mutable URLs such as custom-bundle-latest.zip prevent restoring a previous version because there is no earlier URL to point back to.

Helm configuration

installer:
enabled: true
customBundleUrls:
- "https://your-hosting/custom-bundle-v1.2.0.zip"
env:
FIX_HOST: "prod-fix-gateway.exchange.com"
KAFKA_BOOTSTRAP_SERVERS: "prod-kafka-1:9092,prod-kafka-2:9092"

Multiple bundle URLs are applied in order. Use this for modular configurations, for example a shared corporate-security bundle followed by a desk-specific bundle.

Monitoring the installer

The installer runs as a Helm post-upgrade hook. Its Job is named conncentric-installer-<revision>, where <revision> is the Helm release revision number. List the jobs to find the current one, then follow its logs.

kubectl get jobs -n conncentric
kubectl logs -f job/conncentric-installer-<revision> -n conncentric

The installer logs each phase: plugin uploads, artifact uploads, adapter definition upserts, and variable substitutions. Check these logs first if adapters are missing from the Portal after deployment.

Troubleshooting

SymptomLikely causeAction
Installer fails at Pass 2 with a download errorBundle URL unreachable from inside the cluster.Test from a pod: kubectl run curl --rm -it --image=curlimages/curl -- curl -I <URL>.
Pass 2 succeeds but adapters do not appeardefinitions/ directory missing or empty in the zip.Unzip locally and verify the folder structure.
Adapters appear but fail with "Plugin not found"Plugin JAR missing from plugins/, or a plugin key mismatch.Confirm the plugin's Plugin-Id (read from its JAR manifest, not the filename) matches every entry in the adapter definition's requiredPluginKeys. Renaming the JAR file does not change its Plugin-Id, so rename nothing; fix the Plugin-Id or the requiredPluginKeys entry so they agree.
Import rejected with "unresolved ${VAR} placeholders"A templating token survived into the bundle.Substitute it in your pipeline before packaging (or bake the value into the definition) and re-apply. If the value is a credential meant to resolve at runtime inside the adapter, write it as ${env:VAR_NAME} instead.

See also

  • CI/CD Integration for automating the build, package, and deploy pipeline.
  • Plugin SDK Getting Started for building custom plugins.
  • Component Manifest Schema for the SDK component manifest contract authored by plugin developers. (This documents the SDK component manifest, distinct from the adapter definition format exported from the Portal.)