A Kubernetes operator for managing Synadia Control Plane and Synadia Cloud resources declaratively. Synack reconciles CRDs in your cluster against the Control Plane API, providing management for accounts, streams, consumers, KV buckets, object stores, NATS users, teams, service accounts, and role bindings.
Set URL and Token values
config:
controlPlaneBaseURL: https://control-plane.example.com
token: my-access-tokenInstall with Helm
helm repo add synadia https://synadia-io.github.io/helm-charts
helm repo update
helm upgrade --install -f values.yaml synack synadia/synackThe operator binary accepts the following flags:
| Flag | Default | Description |
|---|---|---|
--control-plane-base-url |
https://cloud.synadia.com |
Control Plane API base URL |
--token-var |
SYNACK_TOKEN |
Environment variable name containing the API token |
--token-file |
empty | File containing the API token; takes precedence over --token-var |
--reconcile-interval |
1m |
Interval between drift-detection reconciliations |
--timeout |
30s |
Timeout for Control Plane API requests |
--leader-elect |
false |
Enable leader election for HA deployments |
--metrics-bind-address |
:8080 |
Metrics endpoint bind address |
--health-probe-bind-address |
:8081 |
Health/readiness probe bind address |
Synack validates the configured Control Plane token through its readiness check. The pod stays alive but does not become Ready until the token can authenticate.
All resources use apiVersion: synack.synadia.io/v1alpha1 and are namespace-scoped.
Manages a NATS account within a system.
apiVersion: synack.synadia.io/v1alpha1
kind: Account
metadata:
name: app-prod
spec:
systemId: <system-id>
name: app-prodTo adopt an existing account, set accountId to the Control Plane account ID:
spec:
accountId: <existing-account-id>
systemId: <system-id>
name: app-testAccount can be used by reference in other resources. Deletion will be blocked until all dependent resources (Streams, KeyValues, ObjectStores, NatsUsers) referencing an Account are removed.
Manages a NATS JetStream stream within an account.
Account selection -- Streams target an account using one of three methods:
| Method | Fields | Use case |
|---|---|---|
| Account CR reference | accountRef.name |
Account managed in the same namespace |
| Direct account ID | accountId |
Account managed externally |
| Public NKey lookup | accountPublicNKey + systemId |
Resolve account by NKey at runtime |
These are mutually exclusive. When using accountRef, the operator waits for the referenced Account CR to have a status.accountId before proceeding.
apiVersion: synack.synadia.io/v1alpha1
kind: Stream
metadata:
name: orders
spec:
accountRef:
name: account-foo
name: ORDERS
subjects:
- "orders.>"
retention: limits
storage: file
replicas: 3
maxAge: "720h"Additional spec fields: description, maxConsumers, maxMsgs, maxMsgsPerSubject, maxBytes, maxMsgSize, discard, noAck, duplicateWindow, placement, sources, compression, subjectTransform, republish, sealed, denyDelete, denyPurge, allowDirect, allowRollup, discardPerSubject, firstSequence, metadata.
To adopt an existing stream, set streamId in the spec.
Streams can be used by reference in Consumer resources. Deletion will be blocked until all dependent Consumers referencing this Stream are removed.
Manages a NATS JetStream Key/Value bucket.
apiVersion: synack.synadia.io/v1alpha1
kind: KeyValue
metadata:
name: app-config
spec:
accountId: <account-id>
bucket: app-config
history: 5
storage: fileAdditional spec fields: description, ttl, maxBytes, maxValueSize, replicas, compression, placement, republish, mirror, sources.
To adopt an existing KV bucket, set keyValueId in the spec.
Manages a NATS JetStream Object Store.
apiVersion: synack.synadia.io/v1alpha1
kind: ObjectStore
metadata:
name: app-assets
spec:
accountId: <account-id>
bucket: app-assets
storage: fileAdditional spec fields: description, ttl, maxBytes, replicas, compression, placement, metadata.
To adopt an existing object store, set objectStoreId in the spec.
Manages a NATS JetStream consumer on a stream. Consumers reference their parent stream using either streamRef (a Stream CR in the same namespace) or streamId (a direct Control Plane stream ID). These are mutually exclusive.
Pull consumer:
apiVersion: synack.synadia.io/v1alpha1
kind: Consumer
metadata:
name: orders-consumer
spec:
streamRef:
name: orders
name: ORDERS_CONSUMER
ackPolicy: explicit
deliverPolicy: all
maxDeliver: 5
maxAckPending: 1000Push consumer (set deliverSubject to create a push consumer):
apiVersion: synack.synadia.io/v1alpha1
kind: Consumer
metadata:
name: orders-notifier
spec:
streamRef:
name: orders
name: ORDERS_NOTIFIER
deliverSubject: notify.orders
deliverGroup: notifiers
ackPolicy: explicit
deliverPolicy: new
maxDeliver: 3Additional spec fields: description, ackWait, durableName, filterSubjects, inactiveThreshold, memStorage, replicas, optStartSeq, optStartTime, replayPolicy, sampleFreq, backoff, direct, metadata, maxRequestBatch, maxRequestMaxBytes, maxRequestExpires, maxWaiting, flowControl, headersOnly, heartbeatInterval, rateLimitBps.
To adopt an existing consumer, set consumerId in the spec.
Manages a NATS user within an account. Supports the same account selection methods as Stream.
apiVersion: synack.synadia.io/v1alpha1
kind: NatsUser
metadata:
name: app-user
spec:
accountRef:
name: app-team
name: app-user
signingKeyGroupId: Default
credentialsSecret:
name: app-user-creds
key: credsCredential injection -- when credentialsSecret is configured, the operator downloads the user's NATS credentials from Control Plane and writes them into a Kubernetes Secret. The Secret is owned by the NatsUser CR and cleaned up automatically. If the secret name changes, the old Secret is deleted and a new one is created.
Additional spec fields: jwtExpiresInSecs, bearerToken, data, payload, subs, allowedConnectionTypes, tags.
To adopt an existing user, set natsUserId in the spec.
Manages a Control Plane team.
apiVersion: synack.synadia.io/v1alpha1
kind: Team
metadata:
name: engineering
spec:
name: EngineeringTo adopt an existing team, set teamId in the spec.
Manages a service account within a team. References its parent team using either teamRef (a Team CR in the same namespace) or teamId (a direct Control Plane team ID). These are mutually exclusive.
apiVersion: synack.synadia.io/v1alpha1
kind: TeamServiceAccount
metadata:
name: ci-bot
spec:
teamRef:
name: engineering
name: CI Bot
teamRoleId: <role-id>To adopt an existing service account, set serviceAccountId in the spec.
Assigns a role to a team app user (human or service account) scoped to a specific resource. This resource has two independent reference resolution paths.
Subject (who gets the role) -- use either:
subjectRef.name-- references a TeamServiceAccount CR in the same namespace; the operator waits for itsstatus.teamAppUserId.teamAppUserId-- a direct Control Plane team app user ID for pre-existing service accounts or human users.
Target (what the role is scoped to) -- use either:
targetRef.name-- references a Team or Account CR in the same namespace (depending onscope). Only supported forTeamandAccountscopes.targetId-- a direct Control Plane resource ID. Required forSystemandNatsUserscopes.
Scopes: Team, Account, System, NatsUser.
apiVersion: synack.synadia.io/v1alpha1
kind: AppUserRoleBinding
metadata:
name: bot-account-access
spec:
subjectRef:
name: bot
scope: Account
targetRef:
name: app-team
roleId: <role-id>Direct ID example:
apiVersion: synack.synadia.io/v1alpha1
kind: AppUserRoleBinding
metadata:
name: admin-system-access
spec:
teamAppUserId: <team-app-user-id>
scope: System
targetId: <system-id>
roleId: <role-id>The operator enforces ordering through reference resolution and deletion guards:
Account
|-- Stream
| \-- Consumer
|-- KeyValue
|-- ObjectStore
\-- NatsUser
Team
\-- TeamServiceAccount
\-- AppUserRoleBinding (subject)
Account / Team (target)
\-- AppUserRoleBinding (target)
- Resources using
accountRef,streamRef,teamRef, orsubjectRef/targetRefwill wait (requeue every 5s) until the referenced resource has a status ID. - Account deletion is blocked until all referencing Streams, KeyValues, ObjectStores, and NatsUsers are removed.
- Stream deletion is blocked until all referencing Consumers are removed.
Every resource reports its reconciliation state in .status:
| Field | Description |
|---|---|
message |
"applied" on success, or an error description |
lastSynced |
RFC 3339 timestamp of last successful reconciliation |
| Resource ID (varies) | The Control Plane ID assigned after creation (e.g. streamId, accountId) |
kubectl create namespace synack-system
kubectl -n synack-system create secret generic synack-token \
--from-literal=SYNACK_TOKEN=<your-control-plane-token>
kubectl apply -k config/defaultThe default manifests deploy registry.synadia.io/synack:v0.1.0, enable leader election, and expect the Control Plane token in the synack-token Secret. By default the Deployment reads that Secret through the SYNACK_TOKEN environment variable.
make docker-build IMG=synack:dev
kubectl -n synack-system set image deployment/synack-controller-manager manager=synack:devTo deploy from another registry with Kustomize, create an overlay and set the image there:
mkdir -p config/local
printf 'resources:\n- ../default\n' > config/local/kustomization.yaml
cd config/local
kustomize edit set image synack=example.com/synack:v0.1.0
kubectl apply -k .