NGS

NGS Day 1

This guide illustrates non-trivial solutions that are easily and quickly accomplished with NGS. With your new account in-hand, you can implement the below scenario in your account in less than 20 minutes.

Scenario: The Battery Spa

The Battery Spa Inc. provides EV recharging stations in Asia, Europe, and the United States. They charge all makes and models of EV and provide added-value services such as real-time weather reports, mileage and efficiency log, and instant vehicle-to-vehicle "car chat." As a world-wide charging infrastructure and communications provider to diverse EV brands and types, The Battery Spa requires only that vehicles can connect over public network to NGS.

The Battery Spa Scenario

Charging technology, core competency of The Battery Spa, is installed and operated at more than 1,000 autonomous "edge stations." Payment authorization and capture processing for "spa visits" are contracted to service providers who operate in their own clouds. Service connectivity from edge stations is via NGS as well.

The Battery Spa invests in continuous improvement and innovation inspired by constant transaction analysis and machine learning applied over business event streams from customer vehicles and charging stations.

In the following steps we use the NATS CLI tool. The NATS CLI is an excellent "multi-tool" but has no special super powers. Every step here could be accomplished (and often would be) in your own application leveraging highly-productive NATS client development libraries.

Pre-requisites and setup

  • NGS account and user
  • Linux or MacOS
  • bash shell
  • NATS CLI tool v0.0.31 or greater

Scenario Actors: NGS Clients

For demo, we'll set up several NATS.io login contexts with the NGS user credential created automatically at NGS sign up.

Set or replace $NGSCONTEXT in the following commands to the name of the NATS context created automatically at sign up. Here's an example:

export NGSCONTEXT="synadia_peaceful_shockley_default"

Use nats context ls to show existing contexts on your terminal. NGS new account signup automatically creates a context called synadia_{your account name}_default.

nats context copy $NGSCONTEXT vehicle1
nats context copy $NGSCONTEXT vehicle2
nats context copy $NGSCONTEXT vehicle3
nats context copy $NGSCONTEXT payauthorizer
nats context copy $NGSCONTEXT payworker
nats context copy $NGSCONTEXT cloudadmin
nats context copy $NGSCONTEXT datascience
nats context copy $NGSCONTEXT ngsleafnode

We'll add a few more scenario actors later when we configure an NGS-connected server at the Edge.

Use nats help {your CLI command} to get options specific to each CLI command.

The Zen of NGS: Dynamic Message Network

The Battery Spa initially selected NGS for its unique and innate abilities as a dynamic message network providing:

  • Participant Location-Transparency
  • Interest-Based Message Routing
  • Bi-Directional Asynchronous Messaging
  • Low-Latency
  • Extreme-Speed
  • Access-Policy

Their first implementation on NGS was "car chat."

Vehicle 1 publishes a message to subscribing vehicles by simple subject

nats sub vehicle --context vehicle2
nats pub vehicle 'Stay in your lane please!' --context vehicle1

We published and received messages on the subject "vehicle". NGS automatically routed the message from vehicle1 to vehicle2 based on subscription-interest graph.

NGS also validated entitlement for vehicle1 to publish on subject "vehicle" and vehicle2 to subscribe and receive messages on subject "vehicle".

By default, all users of an account may publish and subscribe to subjects within their own account's subject namespace.

Vehicle 1 publishes a message specifically to Vehicle 2 subscribers

nats sub 'vehicle.2.>' --context vehicle2
nats pub vehicle.2.tickle 'Hello to vehicle 2 specifically' --context vehicle1

We published to a subject hierarchy enabling subscription interest and message routing to be as specific as we need for our solution.

Vehicle 1 publishes a message received by one subscriber (of a group of subscribers)

nats sub vehicle.cool --queue coolcars --context vehicle3
nats sub vehicle.cool --queue coolcars --context vehicle2
nats pub vehicle.cool 'Hello one of two coolcars workers' --context vehicle1

Message delivery to one member of a group of subscribers acting as a processing pool (shared queue).

Vehicle 1 sends a request (or action) to Vehicle 2 and expects to receive a reply

nats reply 'vehicle.2.>' 'Vehicle 2 got poked [Response {{Count}}]' --context vehicle2
nats request vehicle.2.poke 'I am poking you' --context vehicle1

API invocation using native Request-Reply asynchronous messaging with automatic reply subject.

Vehicle 2 and Vehicle 3 both may reply to "carpool" requests, but each request should go to only one vehicle

nats reply 'vehicle.*.>' --queue carpool --context vehicle2 \
   'Vehicle 2 got poked [Response {{Count}}]'
nats reply 'vehicle.*.>' --queue carpool --context vehicle3 \
   'Vehicle 3 got poked [Response {{Count}}]'
nats request vehicle.any.poke 'I am poking you' --context vehicle1

Instant load balancing for API services!

Life is but a Stream: Reliable and Durable Messaging

Vehicles and Stations emit business event notifications which The Battery Spa captures for analysis and ML

The Battery Spa configures two NGS JetStreams with limits policy to retain events for up to 7 days. They also put caps on the total size and number of messages for each stream to smooth retention spikes when vehicles get really chatty.

JetStream captures messages published to subjects that The Battery Spa architects designed for easy filtering and routing of event notifications including the ability to easily see events for a particular vehicle (or station) only or for all event emitters in a specific business region and sub-area.

NGS takes care of message reliability through persistence. The Battery Spa initially specifies only one copy -- or JetStream replica -- of event data but can expand to high-availability storage later (three replicas).

If your NGS subscription level includes JetStream high-availability (HA) storage, you may set the number of replicas to 3 when creating new streams.

The most active customers are in the Western US currently so The Battery Spa cloud administrator places the vehicle event stream in the NGS geographic region us-west using placement tags.

Any messages published matching the subject hierarchy vehicle.*.*.*.event.> will be ingested in the VEHICLE-EVENTS JetStream and available for later consumption by subscribers.

nats stream add VEHICLE-EVENTS --context cloudadmin \
   --description 'Capture vehicle events in an NGS stream' \
   --tags 'geo:us-west' \
   --subjects 'vehicle.*.*.*.event.>' \
   --retention limits --discard old --dupe-window 2m \
   --max-age 7d --max-bytes 10485760 --max-msgs 1000 --max-msgs-per-subject=-1 --max-msg-size 131072 \
   --replicas 1 --storage file \
   --no-deny-delete --no-deny-purge --no-allow-rollup

We specify various growth limits on our VEHICLE-EVENTS stream:

<ul> <li>Maximum 10 MiB total for all messages</li> <li>Maximum 1,000 total messages</li> <li>Maximum 128 KiB a message</li> <li>Maximum age of 7 days for a message</li> <li>Duplicate message detection in a 2 minute window</li> </ul>

Likewise, any messages published matching the subject hierarchy station.*.*.*.event.> are ingested in the STATION-EVENTS JetStream. The cloud administrator places this stream in NGS geographic region us-east.

nats stream add STATION-EVENTS --context cloudadmin \
   --description 'Capture station events in an NGS stream' \
   --tags 'geo:us-east' \
   --subjects 'station.*.*.*.event.>' \
   --retention limits --discard old --dupe-window 2m \
   --max-age 7d --max-bytes 10485760 --max-msgs 1000 --max-msgs-per-subject=-1 --max-msg-size 131072 \
   --replicas 1 --storage file \
   --no-deny-delete --no-deny-purge --no-allow-rollup

Vehicle 1 and Vehicle 2 each publish 10 low battery event messages

nats pub vehicle.1.R9.A7.event.lowbattery --count 10 --context vehicle1 \
   'Low battery, remaining 15% [Measure: {{Count}}]'
nats pub vehicle.3.R3.A9.event.lowbattery --count 10 --context vehicle3 \
   'Low battery, remaining 2% RED [Measure: {{Count}}]'

The cloud administrator verifies VEHICLE-EVENTS stream configuration

nats stream view VEHICLE-EVENTS --since 1h --subject 'vehicle.*.*.*.event.lowbattery' --context cloudadmin

We are able to instantly capture messages published to a given subject (with or without wildcarding) and persist them in named JetStreams to be consumed (and re-consumed) as needed by subscribers later.

In this example, no modification to publishing clients was required (stream ingestion happens silently). In a later example, we'll show that a publishing client can receive an ingestion acknowledgement from JetStream by doing a request instead of a publish, in this way providing reliable publishing.

We were able to influence where our streams physically (geographically in this case) reside in NGS to optimize round-trip network latency. In NGS, location-transparency is inherent so the streams are accessible from any NGS-connected client.

Configuring limits on streams allows us to control stream growth at aggragate and individual message levels and what should happen when limits are reached -- such as old messages deleted (most common) or new messages rejected.

We implemented a stream pattern (Limits retention policy) in which messages can be consumed (and re-consumed or "replayed") any number of times by multiple subscribers.

The Battery Spa data scientists have multiple ML engines that read and import combined vehicle and station events

The Battery Spa cloud administrator creates a third JetStream to combine STATION-EVENTS and VEHICLE-EVENTS to single stream, ALL-EVENTS, for ML engine usage. After the ML engines consume messages from this stream they are no longer needed so the administrator sets an Interest retention policy on the new stream.

The ALL-EVENTS stream is placed in the European NGS geographic region as most of The Battery Spa analytics and machine learning is conducted by a team in Germany.

nats stream add ALL-EVENTS --context cloudadmin \
   --description 'Aggragate events in an NGS stream' \
   --tags 'geo:europe' \
   --source '{ "name": "STATION-EVENTS" }' \
   --source '{ "name": "VEHICLE-EVENTS" }' \
   --retention interest --discard old --dupe-window 2m \
   --max-age 7d --max-bytes 10485760 --max-msgs 1000 --max-msgs-per-subject=-1 --max-msg-size 131072 \
   --replicas 1 --storage file \
   --no-deny-delete --no-deny-purge --no-allow-rollup

We can optionally provide a subject filter for each stream source.

If desired, you may substitute --source '{ "name": "VEHICLE-EVENTS", "filter_subject": "vehicle.3.*.*.event.>" }' to only source vehicle3 events.

A JetStream (JS) Consumer is created for ML engine consumption of ALL-EVENTS stream

ML engine developers will pull messages from a JS Consumer service created for them. The JS Consumer establishes interest in new ALL-EVENTS messages and tracks reliable delivery to the ML engine clients.

nats consumer add ALL-EVENTS ALL-EVENTS-CONSUMER --context cloudadmin \
   --description 'JS Consumer that reads everything from ALL-EVENTS stream' \
   --pull --filter "" --ack explicit --replay instant --deliver all \
   --max-deliver 10 --max-pending 500 --no-headers-only --backoff none

As the ALL-EVENTS stream was defined with Interest-based retention policy, you will see zero messages pending at first as previously there was no consumer interest so messages were immediately deleted from the stream.

Publish new "lowbattery" events after creating the ALL-EVENTS-CONSUMER to see pending messages.

The cloud administrator verifies that the JS Consumer is ready to use

nats consumer info ALL-EVENTS ALL-EVENTS-CONSUMER --context cloudadmin

The datascience platform can now consume messages

nats consumer next ALL-EVENTS ALL-EVENTS-CONSUMER --ack --count 1 --context datascience

Using JetStream we combined (or multiplexed) two "upstreams" into a new stream.

Through optional SOURCE subject filters we are able to split (or demultiplex) one upstream into smaller streams.

By specifying Interest policy we implemented a multi-consumer durable topic pattern allowing multiple consumer "queues" on the same message stream, each receiving a copy of a message before ultimately being deleted from the JetStream.

The data science team at The Battery Spa regularly needs to train new ML engine versions on smaller event sets

The data science team at The Battery Spa is evaluating a new ML technology and wish to train it with a controlled set of vehicle events emitted just from business area 9. A second JS Consumer on ALL-EVENTS is created just for this trial.

nats consumer add ALL-EVENTS AREA9-SPECIAL-CONSUMER --context cloudadmin \
   --description 'Read only new events from Area 9 from ALL-EVENTS stream' \
   --pull --filter 'vehicle.*.*.A9.event.>' --ack explicit --replay instant --deliver new \
   --max-deliver 10 --max-pending 500 --no-headers-only --backoff none

The ML technology engine under evaluation gets only Area 9-filtered messages from the new JS Consumer.

Vehicle 3 is moving through Area 9 when it reports low battery events:

nats pub vehicle.3.R3.A9.event.lowbattery --count 10 --context vehicle3 \
   'Low battery, remaining 2% RED [Measure: {{Count}}]'

The ML engine will see these events:

nats consumer next ALL-EVENTS AREA9-SPECIAL-CONSUMER --ack --count 4 --context datascience

The evaluation ML engine can process up to 4 events at a time so we asked the AREA9-SPECIAL-CONSUMER to deliver a micro stream of up to 4 events in this client request.

We created multiple consumers on a single JetStream to demonstrate Interest policy. Each JetStream consumer will have opportunity to read the events it is interested in before the messages are ultimately deleted from the underlying stream.

The development team at The Battery Spa has an offline environment for initial engineering work

From time to time, the data science engineering team at The Battery Spa needs an event data set for early development and unit test that they can import into their offline (in respect to NGS) engineering platform.

Currently, they need a copy of vehicle events.

The engineering teams reads VEHICLE-EVENTS to file on their workstation

nats stream backup VEHICLE-EVENTS '/tmp' --consumers --progress --context datascience

They subsequently, restore the stream from their workstation into their JetStream-enabled NATS Server instance

nats stream restore '/tmp' --progress --context '{offline engineering platform context}'

A JetStream called VEHICLE-EVENTS is created in their offline NATS Server environment, populated with all the event messages present at backup time. Any named durable JS Consumers defined against VEHICLE-EVENTS at backup time are also created.

We readily made backup copies of JetStreams (and JetStream Consumers if present) from NGS to a local system.

We are able to restore the copies into a separate NATS Server environment. If we deleted the existing VEHICLE-EVENTS from NGS we could restore our copy back to NGS as well!

Keys to Success: Key-Store Materialized View

The Battery Spa implements an efficiency log for customers that records edge station visits

The product team at The Battery Spa approached engineering with a new in-vehicle app concept "Your Tracker" that would be available to all customers to log mileage and charge detail for every edge station visit.

In response, engineering and cloud administration created an NGS Key-Value (KV) store that is used to persist the last 10 charge visits for each customer vehicle. The latest charge visit and previous history are instantly available to The Battery Spa "Your Tracker" app hosted on vehicles anywhere in the world.

nats kv add VEHICLE-LOG --context cloudadmin \
   --description 'KV store for misc vehicle information' \
   --replicas 1 --storage file --history 10 --max-bucket-size 10485760 --max-value-size 131072

Each station visit by a vehicle records a "last charge" data record

nats kv put VEHICLE-LOG vehicle.1.lastcharge --context vehicle1 \
   '{ "mileage": "23456", "timestamp": "1648583305", "station": "station1.R9.A7" }'
nats kv put VEHICLE-LOG vehicle.1.lastcharge --context vehicle1 \
   '{ "mileage": "23890", "timestamp": "1648647305", "station": "station345.R11.A1" }'

On-demand, a customer can recall their vehicle log (latest and historic log entries)

nats kv get VEHICLE-LOG vehicle.1.lastcharge --context vehicle1
nats kv history VEHICLE-LOG vehicle.1.lastcharge --context vehicle1

Some features of the "Your Tracker" App UX leverage real-time record updates

nats kv watch VEHICLE-LOG vehicle.1.lastcharge --context vehicle1

Leveraging the flexibility of JetStream, we materialized a Key-Value Store (bucket) with key-based put, get, history, and watch features. Each vehicle has a unique key.

Object of Desire: Object Store Materialized View

Object Store materialized views is a preview feature.

The Battery Spa displays targeted ads at spa locations to delight customers as they recharge their vehicles

To spice up recharging, The Battery Spa product designers specified audio-visual capabilities at each autonomous edge station.

Battery Spa cloud administrators created an object store in NGS to house advertisement images and videos that may easily be accessed by the audio-visual experience service at each station for recharge-time entertainment.

We'll use a simple image file (OG.jpg) for demonstration. Feel free to substitute any local file you like.

curl -s https://synadia.com/static/OG.jpg > /tmp/ad1234.jpg

Cloud administrators create an object store in NGS called STATION-ADS and upload audio and video files.

nats object add STATION-ADS --context cloudadmin \
   --description 'Station advertisement artifact store' \
   --max-bucket-size 10485760

We specified that our STATION-ADS object store will hold up to 10 MiB (10,485,760 bytes).

nats object put STATION-ADS --context cloudadmin \
   '/tmp/ad1234.jpg' --name 'ad1234.jpg'
nats object ls STATION-ADS --context cloudadmin

Edge stations pull down audio and video files from the STATION-ADS object store.

nats object get STATION-ADS 'ad1234.jpg' --context station1 \
   --output '/tmp/edgecache-ad1234.jpg'

We leveraged object storage in NGS, extending anywhere-to-anywhere messaging to storing and sharing binary objects of arbitrary type! Object store takes care of automatically chunking and reassembling binary objects such that size is limited only by the maximum size specified for the object store.

Living on the Edge: NGS Leaf Nodes

Each charging station of The Battery Spa is an autonomous physical installation

The Battery Spa prides itself on experience, particularly never failing a customer when they pull up to the "spa" in desparate need of a "restorative recharge." Several critical services that orchestrate near-field vehicle communication, payment acceptance, and recharge workflow all run as microservices locally at each station. Each station has real-time bi-directional communication with The Battery Spa services, each of which is hosted in multiple clouds and geographic regions; however, if a station is offline from cloud for any reason, it can continue to function until connectivity is restored.

Each station has a local NATS Server supporting microservice communication at the station and NGS connectivity

For demonstration, we can instantly start a local NATS server with the NATS CLI tool. We use the --jetstream and --extend options to specify that we will be configuring local streams and connecting to NGS using the ngsleafnode context (NGS connection URL and credential).

nats server run --context ngsleafnode --jetstream --extend

Edge-local NATS login contexts are automatically created in the above command. We use context nats_development in subsequent steps.

Let's create some edge station actors for this part of the scenario

# These contexts are for edge-local connections to NATS server
nats context copy nats_development edgeadmin
nats context copy nats_development station1

Vehicles may request real-time weather reports from specific The Battery Spa stations

The weather microservice at a station connects to its local NATS Server and listens for weather requests

nats reply station.1.NA.NA.request.weather --context station1 \
   --command 'curl -s wttr.in/?format=3'

Vehicles make weather requests to a given station number; both vehicle and station can be anywhere in the world

nats request station.1.NA.NA.request.weather --context vehicle1 \
   'Weather report at your station please'

Stations make payment authorization requests to cloud-hosted payment authorizer services

The payment authorizer services listen for authorization requests via NGS from their payment cloud in Asia.

nats reply 'cloud.payment.authorize.>' --context payauthorizer \
   --command 'echo "Request {{3}} for {{4}} authorized"'

Each edge station makes payment authorization API calls (when connected to cloud services) to reduce payment fraud.

nats request cloud.payment.authorize.1234.vehicle1 --context station1 \
   'Payment auth request 1234 for $75.00'

Stations report a successful authorization request as a business event

The edge payment authorization microservice client publishes events locally which are available to other services at the edge as well as captured in the NGS stream

nats pub station.1.R9.A7.event.payauthsuccess --context station1 \
   'Payment authorization [1234]:[75.00]:[SUCCESS]'

We started a local NATS server instance and used Leaf Node to connect to NGS, thus securely extending communication to your hosted environments and services. We showed that applications at edge may connect locally to their NATS servers, but function and act globally!

The Battery Spa transactions must be captured reliably for post-visit payment capture processing in cloud

The edge administrator configures a JetStream (STATIONTX-LOCAL) at Station 1 to persist local transactions for up to 2 days

nats stream add STATIONTX-LOCAL --context edgeadmin \
   --description 'Capture station transactions locally for reliability' \
   --subjects 'station.1.*.*.tx.>' \
   --retention limits --discard old --dupe-window 10m \
   --max-age 2d --max-bytes 5242880 --max-msgs 500 --max-msgs-per-subject=-1 --max-msg-size 131072 \
   --replicas 1 --storage file \
   --no-deny-delete --no-deny-purge --no-allow-rollup

Leaf node JetStreams can be remotely administed from NGS by setting the --js-domain option in the NATS CLI (or programmatically by specifying JS domain in your code).

Every transaction that is captured locally at edge is automatically and reliably mirrored to NGS cloud

To make sure that we have an exact copy of every transaction captured at Station 1, we create JetStream STATIONTX-HISTORY in NGS as a mirror of STATIONTX-LOCAL. Each message of the STATIONTX-LOCAL stream will be copied -- sequence number and order preserved -- and available at STATIONTX-HISTORY. Breaks in connectivity between the mirror source and target JetStream are not an issue. The target JetStream will be caught up when connectivity is restored.

nats stream add STATIONTX-HISTORY --context cloudadmin \
   --description 'Mirror edge station transactions in an NGS cloud stream' \
   --tags 'geo:us' \
   --mirror '{ "name": "STATIONTX-LOCAL", "external": { "api": "$JS.NATS_DEVELOPMENT.API" } }' \
   --retention limits --discard old \
   --max-age=-1 --max-bytes 10485760 --max-msgs=-1 --max-msgs-per-subject=-1 --max-msg-size 131072 \
   --replicas 1 --storage file \
   --no-deny-delete --no-deny-purge --no-allow-rollup

Note the additional configuration of an external JetStream domain "NATS_DEVELOPMENT" to tell NGS that the mirror source is in a JetStream Domain different from the NGS domain, i.e. our edge Leaf Node's JS Domain.

With JetStream we can continuously replicate streams by mirroring one JetStream to another, preserving both messaging sequence and unique IDs. Mirroring can be completely within NGS or can be used in combination with NGS-connected Leaf Nodes.

The retention and other policies of the target JetStream need not be the same as its mirror source creating great flexibility in solutioning, e.g. short-lived work streams and long-lived archive streams.

The Battery Spa contracts with a payment processor in Asia to do credit payment capture

The Battery Spa cloud administrator creates a JetStream in NGS, STATIONTX-PROCESS, and places it in Asia to be geographically co-located with the payment processor's cloud. This JetStream is a mirror of STATIONTX-HISTORY (a complete history of station transactions), but with a WorkQueue retention policy insuring that each message is consumed and acknowledged only once (a traditional queue).

nats stream add STATIONTX-PROCESS --context cloudadmin \
   --description 'Edge station transactions work queue in NGS cloud' \
   --mirror '{ "name": "STATIONTX-HISTORY" }' \
   --tags 'geo:asia' \
   --retention workq --discard old \
   --max-age=-1 --max-bytes 10485760 --max-msgs=-1 --max-msgs-per-subject=-1 --max-msg-size 131072 \
   --replicas 1 --storage file \
   --no-deny-delete --no-deny-purge --no-allow-rollup

The payment processor microservices use a durable JS Consumer to "dequeue" from STATIONTX-PROCESS

nats consumer add STATIONTX-PROCESS PAYMENT-CONSUMER --context cloudadmin \
   --description 'JS Consumer that reads process queue' \
   --pull --filter "" --ack explicit --replay instant --deliver all \
   --max-deliver 10 --max-pending 500 --no-headers-only --backoff none

This JS Consumer is configured to allow a maximum of 500 payment transactions to be in-flight (not yet acknowledged) at any one time. Also, that a particular payment transaction will be attempted a maximum of 10 times.

Edge station 1 completes a customer recharge order

To guarantee the order transactions are captured (and never lost) the station's order microservice makes a publish request to JetStream and requires an acknowledgement (ACK) message from JetStream in reply. If no acknowledgement is received it will re-publish the transaction until ACK is received. The order microservice adds a Nats-Msg-Id header to the order transaction message with a unique transaction id value. JetStream will automatically detect any duplicate transaction publishes within a 10 minute window to avoid duplicate transaction messages from traveling downstream to the payment processor.

nats request station.1.R9.A7.tx.order --wait --context station1 \
   --header 'Nats-Msg-Id:order567' \
   'Order 567 for Vehicle 1 made for $75.00'

Verify our order transaction is captured locally

nats stream info STATIONTX-LOCAL --context station1

And mirrored to transaction history in NGS

nats stream info STATIONTX-HISTORY --context cloudadmin

And our work queue now has has the order transaction too. Ready to process!

nats consumer info STATIONTX-PROCESS PAYMENT-CONSUMER --context payworker

Let's process payment capture for our order transaction

nats consumer next STATIONTX-PROCESS PAYMENT-CONSUMER --ack --count 1 --context payworker

We captured order transactions at our edge reliably -- even in the event of an edge-cloud network connectivity outage -- by leveraging a JetStream local to our edge microservices.

Our transactions are automatically mirrored to a long-retention "history" stream in NGS and further mirrored to a stream configured as a payment processing work queue. We leveraged placement tags to place stream persistence in optimal geographic regions for our solution.

Scenario Cleanup

Remove NGS JetStreams created for the scenario

nats stream rm ALL-EVENTS --force --context cloudadmin
nats stream rm STATIONTX-PROCESS --force --context cloudadmin
nats stream rm STATIONTX-HISTORY --force --context cloudadmin
nats stream rm STATION-EVENTS --force --context cloudadmin
nats stream rm VEHICLE-EVENTS --force --context cloudadmin
nats stream rm STATIONTX-LOCAL --force --context edgeadmin
nats kv del VEHICLE-LOG --force --context cloudadmin
nats object del STATION-ADS --force --context cloudadmin

Remove NATS contexts on your workstation created for the scenario

nats context rm vehicle1 --force; nats context rm vehicle2 --force
nats context rm vehicle3 --force; nats context rm cloudadmin --force
nats context rm datascience --force; nats context rm ngsleafnode --force
nats context rm payauthorizer --force; nats context rm payworker --force
nats context rm edgeadmin --force; nats context rm station1 --force
nats context rm nats_development --force
nats context rm nats_development_service --force
nats context rm nats_development_system --force

Terminate the NATS CLI embedded NATS server

Previous
Day 0