{"id":18739271,"url":"https://github.com/1995parham/saf","last_synced_at":"2025-09-05T21:33:24.563Z","repository":{"id":48201056,"uuid":"389397345","full_name":"1995parham/saf","owner":"1995parham","description":"Using NATS Jetstream as queue manager to replace RabbitMQ, etc.","archived":false,"fork":false,"pushed_at":"2025-09-01T15:37:00.000Z","size":791,"stargazers_count":34,"open_issues_count":5,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-01T16:24:38.054Z","etag":null,"topics":["consumer","nats","nats-jetstream","producer","producer-consumer"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1995parham.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-07-25T16:53:18.000Z","updated_at":"2025-09-01T14:19:50.000Z","dependencies_parsed_at":"2023-01-29T04:31:20.249Z","dependency_job_id":"b57122ce-1c8e-4f66-afd8-a7d378b8bcff","html_url":"https://github.com/1995parham/saf","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/1995parham/saf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fsaf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fsaf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fsaf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fsaf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1995parham","download_url":"https://codeload.github.com/1995parham/saf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fsaf/sbom","scorecard":{"id":1871,"data":{"date":"2025-08-11","repo":{"name":"github.com/1995parham/saf","commit":"3ba02412f4408f25bfbc318a28a937e3906695f5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5.5,"checks":[{"name":"Maintained","score":10,"reason":"14 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/23 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:33","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:34","Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Warn: no topLevel permission defined: .github/workflows/codeql.yml:1","Warn: no topLevel permission defined: .github/workflows/helm.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:43: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:51: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:57: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:68: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/codeql.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/codeql.yml:69: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:74: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/helm.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/helm.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/helm.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/helm.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/helm.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/helm.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/helm.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/helm.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/helm.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/helm.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/helm.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/1995parham/saf/helm.yml/main?enable=pin","Warn: containerImage not pinned by hash: build/package/Dockerfile:3","Warn: containerImage not pinned by hash: build/package/Dockerfile:21: pin your Docker image by updating docker.io/library/alpine:latest to docker.io/library/alpine:latest@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1","Info:   0 out of  13 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of  11 third-party GitHubAction dependencies pinned","Info:   0 out of   2 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: SAST configuration detected: CodeQL","Info: all commits (7) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-14T12:44:45.022Z","repository_id":48201056,"created_at":"2025-08-14T12:44:45.022Z","updated_at":"2025-08-14T12:44:45.022Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273826140,"owners_count":25175232,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-05T02:00:09.113Z","response_time":402,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["consumer","nats","nats-jetstream","producer","producer-consumer"],"created_at":"2024-11-07T15:33:12.618Z","updated_at":"2025-09-05T21:33:24.529Z","avatar_url":"https://github.com/1995parham.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eSaf\u003c/h1\u003e\n\u003ch6 align=\"center\"\u003eSaf means Queue in Persian\u003c/h6\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./.github/assets/banner.jpg\" /\u003e\n  \u003cbr\u003e\n  \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/1995parham/saf/ci.yml?label=ci\u0026logo=github\u0026style=for-the-badge\u0026branch=main\" alt=\"GitHub Workflow Status\" /\u003e\n  \u003ca href=\"https://codecov.io/gh/1995parham/saf\"\u003e\n    \u003cimg src=\"https://img.shields.io/codecov/c/gh/1995parham/saf?logo=codecov\u0026style=for-the-badge\" alt=\"Codecov\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://1995parham-teaching.github.io/ie-lecture/lectures/nats101/#/1\"\u003e\n    \u003cimg alt=\"Static Badge\" src=\"https://img.shields.io/badge/Presentation-nats101-orange?style=for-the-badge\u0026logo=revealdotjs\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## Introduction\n\nOne of the problems, that we face on projects with queues is deploying [RabbitMQ](https://www.rabbitmq.com/) on the cloud which brings us many challenges for CPU load, split brain, etc.\nI want to see how [NATS](https://nats.io/) with [Jetstream](https://docs.nats.io/nats-concepts/jetstream) can work as a queue manager to replace RabbitMQ.\nSaf project here have been developed to show the Jetstream usage as a queue manager and also load test it.\n\nMeanwhile, we did some real-world tests and the results are not good, after the recent Erlang upgrades to make it cloud compatible,\nI think RabbitMQ will work better.\n\n### What is NATS?\n\nNATS messaging enables the exchange of data that is segmented into messages among computer applications and services.\nThese messages are addressed by subjects and do not depend on network location. This provides an abstraction layer\nbetween the application or service and the underlying physical network. Data is encoded and framed as a message and sent\nby a publisher. The message is received, decoded, and processed by one or more subscribers.\n\n## Description\n\nSaf gets events from its producer side and publish them into NATS.\nThe consumer side gets events from NATS and do the process which may takes time.\nThe producer side here is an HTTP server which validate the given event request, then\nafter embedding trace information, it publishes event into the Jetstream.\n[Stream](https://docs.nats.io/nats-concepts/jetstream/streams) created by the consumer side, and it is defined in `internal/cmq/cmq.go`.\n\nProduce uses a same channel for all subjects and marshals data into JSON.\n\n## Scenarios\n\nOn production, we need to handle two following scenarios when using a queue manager:\n\n1. We have event on the producer side, but there isn't any available server, so we need to send an error.\n2. There is no consumer available, so events must be available on NATS until it gets back.\n\nJetstream stores messages for 1 hour in memory. So you can shut down the consumer\nand send events happily and then after consumer starts again consumes these events.\n\nThe following description shows the stream that stores messages in memory:\n\n```bash\n# you can install natscli from https://github.com/nats-io/natscli\nnats stream ls\n```\n\n```text\n╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n│                                                     Streams                                                     │\n├────────┬────────────────────────────────────────────────┬─────────────────────┬──────────┬───────┬──────────────┤\n│ Name   │ Description                                    │ Created             │ Messages │ Size  │ Last Message │\n├────────┼────────────────────────────────────────────────┼─────────────────────┼──────────┼───────┼──────────────┤\n│ events │ Saf's event channel contains only events topic │ 2023-03-22 14:46:02 │ 1        │ 188 B │ 4m9s         │\n╰────────┴────────────────────────────────────────────────┴─────────────────────┴──────────┴───────┴──────────────╯\n```\n\nAlso, you can see the description of the consumer:\n\n```bash\nnats consumer info events saf\n```\n\n```text\nInformation for Consumer events \u003e saf created 2023-03-22T14:46:02+03:30\n\nConfiguration:\n\n                Name: saf\n    Delivery Subject: _INBOX.u4ykdwReyQtmYjiZpxEJNM\n      Filter Subject: events\n      Deliver Policy: All\n Deliver Queue Group: saf\n          Ack Policy: Explicit\n            Ack Wait: 30s\n       Replay Policy: Instant\n     Max Ack Pending: 1,000\n        Flow Control: false\n\nState:\n\n   Last Delivered Message: Consumer sequence: 1 Stream sequence: 1 Last delivery: 7m54s ago\n     Acknowledgment floor: Consumer sequence: 1 Stream sequence: 1 Last Ack: 7m54s ago\n         Outstanding Acks: 0 out of maximum 1,000\n     Redelivered Messages: 0\n     Unprocessed Messages: 0\n          Active Interest: Active using Queue Group saf\n```\n\nPlease note that we have the [Pull Consumer](https://natsbyexample.com/examples/jetstream/pull-consumer/go) using\nnew JetStream client API in code.\n\nIn case of having not important consumers, you can use NATS core consume type from your subject\n(in this case, you don't need any stream). These consumers lose messages in their downtime\n(please note that, NATS use TCP and you will not lose any messages in case of healthy producer and consumer)\nbut they have a little footprint on server.\n\n### Channels\n\nAfter consuming messages from NATS, Saf send events into Channels.\nChannels are homemade concept of the Saf project. They are actually an interface defined as:\n\n```go\ntype Channel interface {\n Init(*zap.Logger, trace.Tracer, interface{}, \u003c-chan model.ChanneledEvent)\n Run()\n Name() string\n}\n```\n\nEach channel has a name by which is referenced in configuration. They can accept arbitrary configuration (as an empty\ninterface) and they must validate it in their initiation process. Each channel has a way to receive events, and it will\nbe set by `SetChannel`. So we can describe a channel lifecycle as follows:\n\n```go\nInit() -\u003e Run()\n```\n\n## Up and Running\n\nEverything you need to test the project and gather some results are available\nin a single docker-compose:\n\n```bash\njust dev up\n```\n\n## on Kubernetes\n\nYou can also deploy NATS on Kubernetes cluster using [NATS helm chart](https://github.com/nats-io/k8s).\nValues for deploying two clusters are available in `./deployments/k8s/`.\nOfficial chart support NATS exporter by default, and it can set up a `ServiceMonitor` too.\n\nAlso, Saf itself has charts (for producer and consumer) in `./charts` that you\ncan use to deploy it.\n\nAfter deployment, you can easily use port-forwarding to send events and test your environment.\n\n## APIs\n\nThe following API call publishes an event to subject `hello`.\n\n```bash\ncurl -X POST -d '{ \"subject\": \"hello\" }' -H 'Content-Type: application/json' http://127.0.0.1:1378/api/event\n```\n\n## Clustering\n\nOne of the main issues about using Jetstream is how does its clustering work?\nThere are three RAFT groups exist in a single Jetstream cluster:\n\n1. Meta Group: all servers join the Meta Group and the Jetstream API is managed by this group.\n   A leader is elected and this owns the API and takes care of server placement.\n\n2. Stream Group: each Stream creates a RAFT group, this group synchronizes state and data between its members.\n   The elected leader handles ACKs and so forth, if there is no leader the stream will not accept messages.\n\n3. Consumer Group: each Consumer creates a RAFT group, this group synchronizes consumer state between its members.\n   The group will live on the machines where the Stream Group is and handle consumption ACKs etc. Each Consumer will have their own group.\n\n## Super-Cluster (with Jetstream)\n\nFirst create a simple cluster without any gateway configuration and then create the following stream:\n\n```bash\nnats stream new rides --subjects 'ride.accepted, ride.finish' --max-age '5m' --max-bytes '10m' --replicas 2 --storage memory --retention limits --discard old\n\nnats pub --count 10 ride.accepted 'ride {{ID}} started on {{Time}}'\n```\n\nNext upgrade its configuration to have gateway and also create a new cluster to form a super cluster and see how it works with Jetstream.\nThen you can see streams in both regions, but each stream has its leader in its cluster.\n\n```bash\nnats stream report\nnats server request gateways --user admin --password amdin | jq\n```\n\nLast step is to create a new stream in the new cluster to see it will be synced to the old cluster.\n\n```bash\nnats stream new murche --subjects 'ride.eta' --max-age '5m' --max-bytes '10m' --replicas 2 --storage memory --retention limits --discard old\n```\n\n## Enabling Authentication\n\nIn a scenario where the NATS cluster is already set up and users are actively producing and consuming from it,\nthe goal is to enable authentication without causing any downtime.\n\nTo enable authentication with zero downtime in a NATS cluster that currently lacks any form of authentication,\nyou can follow these steps:\n\n1. Define users and passwords: Begin by creating a list of users and their corresponding passwords.\n   Determine the access privileges and permissions for each user based on your requirements.\n   This step can be completed offline without affecting the existing cluster.\n2. Communicate new authentication requirements to clients: Once the user credentials are established,\n   inform all clients (producers and consumers) about the upcoming authentication process.\n   Request them to modify their configurations to include the username and password when connecting to the NATS cluster.\n   (They can send usernames and passwords into the cluster without having any issue before enabling the authentication)\n3. Enable authentication\n4. Monitor and troubleshoot\n\nBy following these steps, you can effectively enable authentication on the NATS cluster without causing any downtime\nfor the existing producers and consumers.\n\n### System Account\n\nBy enabling authentication, you can define an account (again, accounts are like tenants to NATS) to be a system account.\nUsing the system account you can read system events and APIs, so you can have access into the systematic commands of `natscli` too.\n\n## Remove duplicate messages\n\nClients may send a same request multiple times, Jetstream can remove duplicate message based on their ID.\nEach message has an ID header, and you can use your application logic to provide that ID, and ask Jetstream\nto remove those messages.\n\nConsider the following request:\n\n```bash\ncurl -X POST -d '{ \"subject\": \"hello\", \"data\": \"Hello World\", \"id\": \"1\" }' -H 'Content-Type: application/json' http://127.0.0.1:1378/api/event\n```\n\nIt sends a message with identification equals to 1, if your send another request you will not see that request on consumers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1995parham%2Fsaf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1995parham%2Fsaf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1995parham%2Fsaf/lists"}