NATS and JetStream Factories
TypeKro installs the official NATS Helm chart with persistent JetStream storage, installs the official NACK controller, and manages Stream and Consumer resources through NACK CRDs.
import { jetStreamConsumer, jetStreamStream, natsBootstrap } from 'typekro/nats';Platform installation
const factory = natsBootstrap.factory('kro', {
namespace: 'typekro-system', // KRO control-plane namespace
});
await factory.deploy({
name: 'nats',
namespace: 'nats-system',
namespaceOwnership: 'external', // pre-create this Namespace
replicas: 3,
storageSize: '100Gi',
pvcRetentionPolicy: 'retain',
});The control-plane and target namespaces must differ in KRO mode when TypeKro owns the target Namespace. TypeKro rejects an instance that lives in a Namespace its graph owns, preventing a namespace/finalizer deadlock. Shared platform installations should be singleton-owned. The bootstrap pins the official nats and nack charts; values and nackValues are graph-aware passthrough maps merged after safe defaults.
JetStream is enabled with file-backed PVC storage. One replica is suitable for development. A production cluster normally uses three NATS replicas, three stream replicas, fast local storage, resource limits, authentication, TLS, disruption budgets, monitoring, and tested backup/recovery procedures.
pvcRetentionPolicy configures the StatefulSet's persistentVolumeClaimRetentionPolicy: PVCs are retained across StatefulSet deletion and scaling by default. It cannot preserve PVCs when their Namespace is deleted. To retain data across deletion of the complete NATS installation, pre-create the target Namespace and set namespaceOwnership: 'external'; TypeKro then removes the installation but leaves that Namespace and its retained PVCs intact. With the default namespaceOwnership: 'owned', deleting the complete installation deletes the Namespace and therefore its PVCs regardless of this StatefulSet setting. In direct mode, complete owned-Namespace teardown is explicit: factory.deleteInstance('nats', { scopes: ['cluster'] }).
Set pvcRetentionPolicy: 'delete' for explicitly ephemeral installations, such as disposable integration environments. Scaling retains PVCs in both modes.
Streams and consumers
const events = jetStreamStream({
id: 'events',
name: 'application-events', // Kubernetes object name
streamName: 'APPLIK8S_EVENTS', // JetStream stream name
namespace: 'apps',
subjects: ['applik8s.events.>'],
storage: 'file',
retention: 'limits',
replicas: 3,
maxAge: '168h',
duplicateWindow: '2m',
preventDelete: true,
servers: ['nats://nats.nats-system.svc:4222'],
});
const processor = jetStreamConsumer({
id: 'accountProcessor',
name: 'account-commands',
namespace: 'apps',
streamName: 'APPLIK8S_EVENTS',
ackPolicy: 'explicit',
ackWait: '30s',
maxDeliver: 10,
filterSubject: 'applik8s.events.account-changed.>',
servers: ['nats://nats.nats-system.svc:4222'],
});NACK owns the declarative Stream/Consumer lifecycle. Applications own message envelopes, idempotency, command results, retry classification, dead-letter semantics, and safe handler behavior. JetStream publication is at-least-once; message-ID deduplication is bounded by the configured duplicate window and does not replace an application inbox.
For authenticated installations, configure the official charts through passthrough values and mount credentials into NACK. Stream and Consumer factories expose creds, nkey, servers, and jsDomain; these are paths or routing settings, never inline credential contents.