Skip to content

Managed NATS

Managed NATS provides private messaging between applications in the same SatuSky organization. This guide creates a Core NATS instance, downloads its connection credentials without displaying them, and verifies publish/subscribe behavior from a Node.js application.

Core NATS is for live messaging, request/reply, and event fan-out. Messages are not persisted when no subscriber is listening.

  • 1ctl auth status shows the expected account and organization
  • A deployed application in the same organization
  • Node.js 20 or later for the example client

The active 1ctl organization determines where NATS is created. Managed NATS is private; it is not exposed through a public domain.

Choose a name that is unique within your organization:

Terminal window
NATS_NAME=events
1ctl nats create "$NATS_NAME"

Creation is asynchronous. Inspect the durable deployment record:

Terminal window
1ctl nats get "$NATS_NAME"
1ctl nats list

Check live readiness:

Terminal window
1ctl nats status "$NATS_NAME"

If status briefly reports Deployment status not found immediately after creation, wait and try again. Do not create a second instance with a different name just to work around a readiness lookup.

Create a local credentials directory that is excluded from source control:

Terminal window
printf '\n.secrets/\n' >> .gitignore
1ctl nats credentials "$NATS_NAME" --output-dir .secrets/nats

The CLI creates the directory with mode 0700 and writes client-url.txt and client-token.txt with mode 0600. It refuses existing files and symbolic links rather than silently overwriting credentials.

Never commit this directory, paste the token into satusky.toml, or use --stdout in CI logs.

From the application directory, store the private URL and token as application secrets:

Terminal window
1ctl secret create \
--kv NATS_URL="$(cat .secrets/nats/client-url.txt)" \
--kv NATS_TOKEN="$(cat .secrets/nats/client-token.txt)"

Restart the application so its next container receives the new environment values:

Terminal window
1ctl app restart YOUR_APP_NAME

Replace YOUR_APP_NAME with the [app].name from that application’s satusky.toml.

Once the secrets are attached, remove the local copies using your operating system’s secure file-removal procedure. The values remain available to the deployed application through NATS_URL and NATS_TOKEN.

Install the official JavaScript client in your Node.js application:

Terminal window
npm install nats

Create nats-check.mjs:

import { connect, StringCodec } from 'nats';
const connection = await connect({
servers: process.env.NATS_URL,
token: process.env.NATS_TOKEN,
});
const codec = StringCodec();
const subject = 'orders.created';
const subscription = connection.subscribe(subject, { max: 1 });
connection.publish(subject, codec.encode('order-123'));
for await (const message of subscription) {
console.log(codec.decode(message.data));
}
await connection.drain();

Run this check inside the deployed application or as part of its startup smoke test:

Terminal window
node nats-check.mjs

Expected output:

order-123

This verifies authentication, private service discovery, publishing, and subscription. It does not verify persistence: Core NATS delivers live messages and does not retain them for a later consumer.

Inspect the managed service first:

Terminal window
1ctl nats get "$NATS_NAME"
1ctl nats status "$NATS_NAME"

Then inspect the application receiving the credentials:

Terminal window
1ctl app status YOUR_APP_NAME
1ctl logs --tail 50

Check these application-level causes before rotating credentials:

  • The NATS service and client application use the same SatuSky organization.
  • The application was restarted after its NATS secrets were attached.
  • The client uses both NATS_URL and NATS_TOKEN.
  • Publishers and subscribers use the same subject spelling.

Do not expose the client port with a custom domain. NATS traffic stays on the organization’s private network.

JetStream is intended for durable streams, replay, and consumers that receive messages published while they were offline. The CLI exposes a three-replica profile:

Terminal window
JETSTREAM_NAME=events-ha
1ctl nats create "$JETSTREAM_NAME" \
--jetstream \
--storage-size 20Gi \
--storage-class ceph-block

Wait for the durable record and live service to become ready:

Terminal window
1ctl nats get "$JETSTREAM_NAME"
1ctl nats status "$JETSTREAM_NAME"

The deployment uses three NATS replicas and three persistent volumes. Download and attach its credentials using the same procedure as Core NATS:

Terminal window
1ctl nats credentials "$JETSTREAM_NAME" --output-dir .secrets/nats-ha
1ctl secret create \
--kv NATS_URL="$(cat .secrets/nats-ha/client-url.txt)" \
--kv NATS_TOKEN="$(cat .secrets/nats-ha/client-token.txt)"
1ctl app restart YOUR_APP_NAME

Create jetstream-check.mjs in the Node.js application:

import { connect, StorageType, StringCodec } from 'nats';
const connection = await connect({
servers: process.env.NATS_URL,
token: process.env.NATS_TOKEN,
});
const manager = await connection.jetstreamManager();
const jetstream = connection.jetstream();
const codec = StringCodec();
try {
await manager.streams.add({
name: 'GUIDE_EVENTS',
subjects: ['guide.events'],
storage: StorageType.File,
num_replicas: 3,
});
} catch (error) {
if (error.api_error?.err_code !== 10058) {
throw error;
}
}
await jetstream.publish('guide.events', codec.encode('persisted-event'));
const info = await manager.streams.info('GUIDE_EVENTS');
console.log(`stored messages: ${info.state.messages}`);
await connection.drain();

Run it inside the deployed application:

Terminal window
node jetstream-check.mjs

Expected output includes:

stored messages: 1

Run the script again later and the count increases because file-backed messages survive client and NATS pod restarts. Platform verification also confirms that a published message remains consumable after a JetStream replica is replaced.

Delete a Core NATS instance when no application depends on it:

Terminal window
1ctl nats delete "$NATS_NAME" --yes

Core NATS has no persistent message volume.

JetStream deletion retains persistent volumes by default so stream data can be recovered. To permanently delete the deployment and its generated volumes:

Terminal window
1ctl nats delete "$JETSTREAM_NAME" --purge-retained --yes

--purge-retained permanently deletes the retained volumes. The platform scopes the purge to volumes owned by that exact deployment. Use it only after confirming that no stream data must be recovered.

  • Core NATS is a private, token-authenticated organization service.
  • Credentials belong in application secrets, not source control or satusky.toml.
  • Publish/subscribe proves live messaging but not persistence.
  • JetStream HA uses three replicas and file-backed persistent volumes for durable streams.
  • NATS deletion retains JetStream data unless --purge-retained is explicitly selected.

See 1ctl nats and the generated references for create, credentials, and delete.