Services

To share services that other accounts can reach via request reply, you have to Export a Service. Services are associated with the account performing the replies and are advertised in the exporting accounts’ JWT.

Adding a Public Service Export

To add a service to your account:

  1. > nsc add export --name help --subject help --service
  2. [ OK ] added public service export "help"

To review the service export:

  1. > nsc describe account
  2. ╭──────────────────────────────────────────────────────────────────────────────────────╮
  3. Account Details
  4. ├───────────────────────────┬──────────────────────────────────────────────────────────┤
  5. Name A
  6. Account ID ADETPT36WBIBUKM3IBCVM4A5YUSDXFEJPW4M6GGVBYCBW7RRNFTV5NGE
  7. Issuer ID OAFEEYZSYYVI4FXLRXJTMM32PQEI3RGOWZJT7Y3YFM4HB7ACPE4RTJPG
  8. Issued 2019-12-04 18:20:42 UTC
  9. Expires
  10. ├───────────────────────────┼──────────────────────────────────────────────────────────┤
  11. Max Connections Unlimited
  12. Max Leaf Node Connections Unlimited
  13. Max Data Unlimited
  14. Max Exports Unlimited
  15. Max Imports Unlimited
  16. Max Msg Payload Unlimited
  17. Max Subscriptions Unlimited
  18. Exports Allows Wildcards True
  19. ├───────────────────────────┼──────────────────────────────────────────────────────────┤
  20. Imports None
  21. ╰───────────────────────────┴──────────────────────────────────────────────────────────╯
  22. ╭────────────────────────────────────────────────────────────╮
  23. Exports
  24. ├──────┬─────────┬─────────┬────────┬─────────────┬──────────┤
  25. Name Type Subject Public Revocations Tracking
  26. ├──────┼─────────┼─────────┼────────┼─────────────┼──────────┤
  27. help Service help Yes 0 -
  28. ╰──────┴─────────┴─────────┴────────┴─────────────┴──────────╯

Importing a Service

Importing a service enables you to send requests to the remote Account. To import a Service, you have to create an Import. To create an import you need to know:

  • The exporting account’s public key
  • The subject the service is listening on
  • You can map the service’s subject to a different subject
  • Self-imports are not valid; you can only import services from other accounts.

To learn how to inspect a JWT from an account server, check this article.

First let’s create a second account to import the service into:

  1. > nsc add account B
  2. [ OK ] generated and stored account key "AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H"
  3. [ OK ] added account "B"
  4. > nsc add import --src-account ADETPT36WBIBUKM3IBCVM4A5YUSDXFEJPW4M6GGVBYCBW7RRNFTV5NGE --remote-subject help --service
  5. [ OK ] added service import "help"

Verifying our work:

  1. > nsc describe account
  2. ╭──────────────────────────────────────────────────────────────────────────────────────╮
  3. Account Details
  4. ├───────────────────────────┬──────────────────────────────────────────────────────────┤
  5. Name B
  6. Account ID AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H
  7. Issuer ID OAFEEYZSYYVI4FXLRXJTMM32PQEI3RGOWZJT7Y3YFM4HB7ACPE4RTJPG
  8. Issued 2019-12-04 20:12:42 UTC
  9. Expires
  10. ├───────────────────────────┼──────────────────────────────────────────────────────────┤
  11. Max Connections Unlimited
  12. Max Leaf Node Connections Unlimited
  13. Max Data Unlimited
  14. Max Exports Unlimited
  15. Max Imports Unlimited
  16. Max Msg Payload Unlimited
  17. Max Subscriptions Unlimited
  18. Exports Allows Wildcards True
  19. ├───────────────────────────┼──────────────────────────────────────────────────────────┤
  20. Exports None
  21. ╰───────────────────────────┴──────────────────────────────────────────────────────────╯
  22. ╭──────────────────────────────────────────────────────────────────────────╮
  23. Imports
  24. ├──────┬─────────┬────────┬──────────────┬─────────┬──────────────┬────────┤
  25. Name Type Remote Local/Prefix Expires From Account Public
  26. ├──────┼─────────┼────────┼──────────────┼─────────┼──────────────┼────────┤
  27. help Service help help A Yes
  28. ╰──────┴─────────┴────────┴──────────────┴─────────┴──────────────┴────────╯

Let’s also add a user to make requests from the service:

  1. > nsc add user b
  2. [ OK ] generated and stored user key "UDKNTNEL5YD66U2FZZ2B3WX2PLJFKEFHAPJ3NWJBFF44PT76Y2RAVFVE"
  3. [ OK ] generated user creds file "~/.nkeys/creds/O/B/b.creds"
  4. [ OK ] added user "b" to account "B"

Testing the Service

To test the service, we can install the nats-req and nats-rply tools:

Set up a process to handle the request. This process will run from account ‘A’ using user ‘U’:

  1. > go get github.com/nats-io/nats.go/examples/nats-rply
  2. > nats-rply -creds ~/.nkeys/creds/O/A/U.creds help "I will help"
  3. Listening on [help]
  4. # Remember you can also do:
  5. nsc reply --account A --user U help "I will help"

Send the request:

  1. > go get github.com/nats-io/nats.go/examples/nats-req
  2. > nats-req -creds ~/.nkeys/creds/O/B/b.creds help me
  3. Published [help] : 'me'

The service receives the request:

  1. Received on [help]: 'me'

And the response is received by the requestor:

  1. Received [_INBOX.v6KAX0v1bu87k49hbg3dgn.StIGJF0D] : 'I will help'

Or more simply:

  1. > nsc reply --account A --user U help "I will help"
  2. listening on [help]
  3. ...
  4. > nsc req --account B --user b help me
  5. published request: [help] : 'me'
  6. received reply: [_INBOX.GCJltVq1wRSb5FoJrJ6SE9.w8utbBXR] : 'I will help'

Securing Services

If you want to create a service that is only accessible to accounts you designate you can create a private service. The export will be visible in your account, but subscribing accounts will require an authorization token that must be created by you and generated specifically for the requesting account. The authorization token is simply a JWT signed by your account where you authorize the client account to import your service.

Creating a Private Service Export

  1. > nsc add export --subject "private.help.*" --private --service --account A
  2. [ OK ] added private service export "private.help.*"

As before, we declared an export, but this time we added the --private flag. The other thing to note is that the subject for the request has a wildcard. This enables the account to map specific subjects to specifically authorized accounts.

  1. > nsc describe account A
  2. ╭──────────────────────────────────────────────────────────────────────────────────────╮
  3. Account Details
  4. ├───────────────────────────┬──────────────────────────────────────────────────────────┤
  5. Name A
  6. Account ID ADETPT36WBIBUKM3IBCVM4A5YUSDXFEJPW4M6GGVBYCBW7RRNFTV5NGE
  7. Issuer ID OAFEEYZSYYVI4FXLRXJTMM32PQEI3RGOWZJT7Y3YFM4HB7ACPE4RTJPG
  8. Issued 2019-12-04 20:19:19 UTC
  9. Expires
  10. ├───────────────────────────┼──────────────────────────────────────────────────────────┤
  11. Max Connections Unlimited
  12. Max Leaf Node Connections Unlimited
  13. Max Data Unlimited
  14. Max Exports Unlimited
  15. Max Imports Unlimited
  16. Max Msg Payload Unlimited
  17. Max Subscriptions Unlimited
  18. Exports Allows Wildcards True
  19. ├───────────────────────────┼──────────────────────────────────────────────────────────┤
  20. Imports None
  21. ╰───────────────────────────┴──────────────────────────────────────────────────────────╯
  22. ╭─────────────────────────────────────────────────────────────────────────────╮
  23. Exports
  24. ├────────────────┬─────────┬────────────────┬────────┬─────────────┬──────────┤
  25. Name Type Subject Public Revocations Tracking
  26. ├────────────────┼─────────┼────────────────┼────────┼─────────────┼──────────┤
  27. help Service help Yes 0 -
  28. private.help.* Service private.help.* No 0 -
  29. ╰────────────────┴─────────┴────────────────┴────────┴─────────────┴──────────╯

Generating an Activation Token

For the foreign account to import a private service and be able to send requests, you have to generate an activation token. The activation token in addition to granting permission to the account allows you to subset the service’s subject:

To generate a token, you’ll need to know the public key of the account importing the service. We can easily find the public key for account B by running:

  1. > nsc list keys --account B
  2. ╭──────────────────────────────────────────────────────────────────────────────────────────╮
  3. Keys
  4. ├────────┬──────────────────────────────────────────────────────────┬─────────────┬────────┤
  5. Entity Key Signing Key Stored
  6. ├────────┼──────────────────────────────────────────────────────────┼─────────────┼────────┤
  7. O OAFEEYZSYYVI4FXLRXJTMM32PQEI3RGOWZJT7Y3YFM4HB7ACPE4RTJPG *
  8. B AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H *
  9. b UDKNTNEL5YD66U2FZZ2B3WX2PLJFKEFHAPJ3NWJBFF44PT76Y2RAVFVE *
  10. ╰────────┴──────────────────────────────────────────────────────────┴─────────────┴────────╯
  1. > nsc generate activation --account A --target-account AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H --subject private.help.AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H -o /tmp/activation.jwt
  2. [ OK ] generated "private.help.*" activation for account "AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H"
  3. [ OK ] wrote account description to "/tmp/activation.jwt"

The command took the account that has the export (‘A’), the public key of account B, the subject where requests from account B will be handled, and an output file where the token can be stored. The subject for the export allows the service to handle all requests coming in on private.help.*, but account B can only request from a specific subject.

For completeness, the contents of the JWT file looks like this:

  1. > cat /tmp/activation.jwt
  2. -----BEGIN NATS ACTIVATION JWT-----
  3. eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJUS01LNEFHT1pOVERDTERGUk9QTllNM0hHUVRDTEJTUktNQUxXWTVSUUhFVEVNNE1VTDdBIiwiaWF0IjoxNTc1NDkxNjEwLCJpc3MiOiJBREVUUFQzNldCSUJVS00zSUJDVk00QTVZVVNEWEZFSlBXNE02R0dWQllDQlc3UlJORlRWNU5HRSIsIm5hbWUiOiJwcml2YXRlLmhlbHAuQUFNNDZFM1lGNVdPWlNFNVdOWVdITjNZWUlTVlpPU0k2WEhURjJRNjRFQ1BYU0ZRWlJPSk1QMkgiLCJzdWIiOiJBQU00NkUzWUY1V09aU0U1V05ZV0hOM1lZSVNWWk9TSTZYSFRGMlE2NEVDUFhTRlFaUk9KTVAySCIsInR5cGUiOiJhY3RpdmF0aW9uIiwibmF0cyI6eyJzdWJqZWN0IjoicHJpdmF0ZS5oZWxwLkFBTTQ2RTNZRjVXT1pTRTVXTllXSE4zWVlJU1ZaT1NJNlhIVEYyUTY0RUNQWFNGUVpST0pNUDJIIiwidHlwZSI6InNlcnZpY2UifX0.4tFx_1UzPUwbV8wFNIJsQYu91K9hZaGRLE10nOphfHGetvMPv1384KC-1AiNdhApObSDFosdDcpjryD0QxaDCQ
  4. ------END NATS ACTIVATION JWT------

When decoded it looks like this:

  1. > nsc describe jwt -f /tmp/activation.jwt
  2. ╭─────────────────────────────────────────────────────────────────────────────────────────╮
  3. Activation
  4. ├─────────────────┬───────────────────────────────────────────────────────────────────────┤
  5. Name private.help.AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H
  6. Account ID AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H
  7. Issuer ID ADETPT36WBIBUKM3IBCVM4A5YUSDXFEJPW4M6GGVBYCBW7RRNFTV5NGE
  8. Issued 2019-12-04 20:33:30 UTC
  9. Expires
  10. ├─────────────────┼───────────────────────────────────────────────────────────────────────┤
  11. Hash ID DD6BZKI2LTQKAJYD5GTSI4OFUG72KD2BF74NFVLUNO47PR4OX64Q====
  12. ├─────────────────┼───────────────────────────────────────────────────────────────────────┤
  13. Import Type Service
  14. Import Subject private.help.AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H
  15. ├─────────────────┼───────────────────────────────────────────────────────────────────────┤
  16. Max Messages Unlimited
  17. Max Msg Payload Unlimited
  18. Network Src Any
  19. Time Any
  20. ╰─────────────────┴───────────────────────────────────────────────────────────────────────╯

The token can be shared directly with the client account.

If you manage many tokens for many accounts, you may want to host activation tokens on a web server and share the URL with the account. The benefit to the hosted approach is that any updates to the token would be available to the importing account whenever their account is updated, provided the URL you host them in is stable. When using a JWT account server, the tokens can be stored right on the server and shared by an URL that is printed when the token is generated.

Importing a Private Service

Importing a private service is more natural than a public one because the activation token stores all the necessary details. Again, the token can be an actual file path or a remote URL.

  1. > nsc add import --account B -u /tmp/activation.jwt --local-subject private.help --name private.help
  2. [ OK ] added service import "private.help.AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H"
  3. > nsc describe account B
  4. ╭──────────────────────────────────────────────────────────────────────────────────────╮
  5. Account Details
  6. ├───────────────────────────┬──────────────────────────────────────────────────────────┤
  7. Name B
  8. Account ID AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H
  9. Issuer ID OAFEEYZSYYVI4FXLRXJTMM32PQEI3RGOWZJT7Y3YFM4HB7ACPE4RTJPG
  10. Issued 2019-12-04 20:38:06 UTC
  11. Expires
  12. ├───────────────────────────┼──────────────────────────────────────────────────────────┤
  13. Max Connections Unlimited
  14. Max Leaf Node Connections Unlimited
  15. Max Data Unlimited
  16. Max Exports Unlimited
  17. Max Imports Unlimited
  18. Max Msg Payload Unlimited
  19. Max Subscriptions Unlimited
  20. Exports Allows Wildcards True
  21. ├───────────────────────────┼──────────────────────────────────────────────────────────┤
  22. Exports None
  23. ╰───────────────────────────┴──────────────────────────────────────────────────────────╯
  24. ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
  25. Imports
  26. ├──────────────┬─────────┬───────────────────────────────────────────────────────────────────────┬──────────────┬─────────┬──────────────┬────────┤
  27. Name Type Remote Local/Prefix Expires From Account Public
  28. ├──────────────┼─────────┼───────────────────────────────────────────────────────────────────────┼──────────────┼─────────┼──────────────┼────────┤
  29. help Service help help A Yes
  30. private.help Service private.help.AAM46E3YF5WOZSE5WNYWHN3YYISVZOSI6XHTF2Q64ECPXSFQZROJMP2H private.help A No
  31. ╰──────────────┴─────────┴───────────────────────────────────────────────────────────────────────┴──────────────┴─────────┴──────────────┴────────╯

When importing a service, you can specify the local subject you want to use to make requests. The local subject in this case is private.help. However when the request is forwarded by NATS, the request is sent on the remote subject.

Testing the Private Service

Testing a private service is no different than a public one:

  1. > nsc reply --account A --user U "private.help.*" "help is here"
  2. listening on [private.help.*]
  3. > nsc req --account B --user b private.help help_me
  4. published request: [private.help] : 'help_me'
  5. received reply: [_INBOX.3MhS0iCHfqO8wUl1x59bHB.jpE2jvEj] : 'help is here'