Skip to content

Feat/nats credfile based auth#1591

Merged
RinkalBhojani merged 5 commits intomainfrom
feat/nats-credfile-based-auth
Mar 28, 2026
Merged

Feat/nats credfile based auth#1591
RinkalBhojani merged 5 commits intomainfrom
feat/nats-credfile-based-auth

Conversation

@RinkalBhojani
Copy link
Copy Markdown
Contributor

@RinkalBhojani RinkalBhojani commented Mar 28, 2026

Summary by CodeRabbit

  • New Features

    • Support for multiple NATS authentication methods (nkey, creds file, username/password, none)
    • Optional NATS-based notifications that can be enabled/disabled via config
    • Persistent message storage for notification ACKs to improve reliability
  • Documentation

    • Updated environment configuration templates with new NATS and notification settings

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 28, 2026

📝 Walkthrough

Walkthrough

Adds configurable NATS authentication (nkey, creds, usernamePassword, none), a creds-file option, and a toggle for NATS-based notifications. Migrates pending ACK storage from in-memory Map to JetStream KV, updates many getNatsOptions callsites to accept a creds file, and gates notification NATS wiring behind a feature flag.

Changes

Cohort / File(s) Summary
Environment Configuration
\.env\.demo, \.env\.sample
Added NATS_CREDS_FILE, NATS_AUTH_TYPE (default nkey), NOTIFICATION_NATS_AUTH_TYPE, and ENABLE_NATS_NOTIFICATION=false.
Common NATS Config & Utils
libs/common/src/nats.config.ts, libs/common/src/common.utils.ts
Extended getNatsOptions signature to accept creds and multiple auth modes; added NatsAuthType and shouldLoadNatsNotification() utility.
Notification: NATS Service
apps/notification/src/nats/nats.service.ts
Added multi-mode auth selection, creds-file handling, cached JS/KV support and getKV(bucketName) method.
Notification: Pending ACK Store
apps/notification/src/nats/pendingAckStore.ts
Replaced in-memory Map with JetStream KV persistence, changed key format, made `save
Notification: Module & Subscriber
apps/notification/src/notification.module.ts, apps/notification/src/nats/nats-subscriber.ts
Conditional NATS module registration via shouldLoadNatsNotification; subscriber now awaits async pendingAckStore operations.
Microservice & Gateway Modules
many apps/*/src/*module.ts (API gateway + services, ~40 files)
Updated all ClientsModule.register and module NATS client calls to pass process.env.NATS_CREDS_FILE as third argument to getNatsOptions; minor whitespace/formatting adjustments.
Microservice Entrypoints
many apps/*/src/main.ts (18+ files)
Updated getNatsOptions calls during microservice bootstrap to include process.env.NATS_CREDS_FILE; removed stray blank lines.
Small Logic Change
apps/oid4vc-verification/src/oid4vc-verification.service.ts
Debug log now includes ecosystemId in created verification log message.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Subscriber
  participant PendingAckStore
  participant NatsService
  participant JetStreamKV
  participant NATS

  Subscriber->>PendingAckStore: save(stream, consumer, msg)
  activate PendingAckStore
  PendingAckStore->>NatsService: ensure connected / getKV("PENDING_ACK")
  activate NatsService
  NatsService->>JetStreamKV: js.views.kv("PENDING_ACK")
  deactivate NatsService
  PendingAckStore->>JetStreamKV: put(key, JSON(metadata))
  JetStreamKV-->>PendingAckStore: ack
  deactivate PendingAckStore

  Subscriber->>NATS: publish ack/acknowledge (via JsMsg.ack)
  NATS-->>Subscriber: ack received

  Subscriber->>PendingAckStore: delete(key)
  PendingAckStore->>JetStreamKV: delete(key)
  JetStreamKV-->>PendingAckStore: ack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

feature

Suggested reviewers

  • GHkrishna
  • shitrerohit

Poem

🐰 I hopped through code with joyous feet,

NATS creds and KV make a tidy seat.
ACKs now live where streams can see,
Auth modes dance — nkey, creds, or user+pass, whee!
A little rabbit nods: "Deploy with glee!"

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat/nats credfile based auth' accurately describes the main change: adding NATS credentials file-based authentication support across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/nats-credfile-based-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…older

Signed-off-by: RinkalBhojani <rinkal.bhojani@ayanworks.com>
Signed-off-by: RinkalBhojani <rinkal.bhojani@ayanworks.com>
Signed-off-by: RinkalBhojani <rinkal.bhojani@ayanworks.com>
@RinkalBhojani RinkalBhojani force-pushed the feat/nats-credfile-based-auth branch from 60c5fc4 to c7e9d24 Compare March 28, 2026 10:44
Signed-off-by: RinkalBhojani <rinkal.bhojani@ayanworks.com>
@RinkalBhojani RinkalBhojani marked this pull request as ready for review March 28, 2026 14:35
@RinkalBhojani RinkalBhojani self-assigned this Mar 28, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (7)
libs/common/src/common.utils.ts (1)

153-156: Consider case-insensitive comparison for consistency.

The existing shouldLoadOidcModules uses .toLowerCase() for case-insensitive comparison, but shouldLoadNatsNotification uses strict equality. This inconsistency means ENABLE_NATS_NOTIFICATION=TRUE or True would return false.

♻️ Proposed fix for consistency
 export function shouldLoadNatsNotification(): boolean {
   const raw = process.env.ENABLE_NATS_NOTIFICATION;
-  return 'true' === raw;
+  return 'true' === raw?.toLowerCase();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/common/src/common.utils.ts` around lines 153 - 156, The
shouldLoadNatsNotification function currently compares
process.env.ENABLE_NATS_NOTIFICATION with 'true' using strict equality, which is
case-sensitive; update shouldLoadNatsNotification to perform a case-insensitive
check (e.g., normalize with .toLowerCase() and optional .trim()) and guard
against undefined/null before calling string methods so values like 'TRUE' or '
True ' return true, mirroring the behavior of shouldLoadOidcModules.
libs/common/src/nats.config.ts (1)

53-61: Consider adding a warning or error for missing nkeySeed in default case.

When NATS_AUTH_TYPE=nkey (or default) but nkeySeed is not provided, the function silently falls back to unauthenticated mode. While this might be intentional for backward compatibility, it could mask configuration issues.

At minimum, consider logging a warning when falling back to unauthenticated mode for the default nkey auth type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/common/src/nats.config.ts` around lines 53 - 61, The switch
default/'nkey' branch currently returns baseOptions silently when nkeySeed is
missing; update that branch to emit a warning (e.g., via the existing logger or
console.warn) when NATS auth type is 'nkey' (or default) but nkeySeed is falsy,
then continue to return baseOptions; reference the nkeySeed variable, the
nkeyAuthenticator usage, and baseOptions in the change so the warning clearly
states that nkey authentication was requested but seed is missing and the client
will connect unauthenticated.
.env.sample (1)

279-283: Fix formatting issues flagged by linter.

Static analysis detected several formatting problems:

  • Line 279: Trailing whitespace and unordered key (NATS_AUTH_TYPE should come before NATS_CREDS_FILE)
  • Line 281: Spaces around equal sign and trailing whitespace
  • Line 283: Missing blank line at end of file
🔧 Proposed fix
 PULL_CONSUMER=hub-pull-consumer

+# 'nkey' | 'creds' | 'usernamePassword' | 'none'
+NATS_AUTH_TYPE=nkey
 NATS_CREDS_FILE=/platform/app_user.creds
-# 'nkey' | 'creds' | 'usernamePassword' | 'none'
-NATS_AUTH_TYPE=nkey 
 # 'nkey' | 'creds' | 'usernamePassword' | 'none'
-NOTIFICATION_NATS_AUTH_TYPE= 
+NOTIFICATION_NATS_AUTH_TYPE=

 ENABLE_NATS_NOTIFICATION=false
+
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.sample around lines 279 - 283, Remove trailing whitespace and normalize
spacing around equals for the env keys shown: ensure lines use KEY=VALUE with no
extra spaces (fix NOTIFICATION_NATS_AUTH_TYPE and NATS_AUTH_TYPE). Reorder the
block so NATS_AUTH_TYPE appears before NATS_CREDS_FILE if that key exists
elsewhere (preserve logical ordering of NATS_* keys). Ensure there's a single
blank newline at end of file. Target the keys NATS_AUTH_TYPE, NATS_CREDS_FILE,
NOTIFICATION_NATS_AUTH_TYPE, and ENABLE_NATS_NOTIFICATION when making these
edits.
apps/notification/src/nats/nats.service.ts (1)

16-16: Consider importing NatsAuthType from the shared config.

This type is already defined in libs/common/src/nats.config.ts. Importing it would avoid duplication and ensure consistency if the allowed auth types change.

♻️ Import from shared config
-type NatsAuthType = 'nkey' | 'creds' | 'usernamePassword' | 'none';
+import { NatsAuthType } from '@credebl/common/nats.config';

Note: You may need to export the type from nats.config.ts if it isn't already exported.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/notification/src/nats/nats.service.ts` at line 16, Replace the locally
declared NatsAuthType in nats.service.ts with the shared definition by importing
NatsAuthType from the common nats config (e.g., from
libs/common/src/nats.config.ts); if NatsAuthType is not exported from that
module, export it there first, then update nats.service.ts to remove the
duplicate type and add the import so the service uses the single shared
NatsAuthType definition.
apps/organization/src/organization.module.ts (1)

57-58: Duplicate provider entry.

UserActivityRepository is listed twice in the providers array. While NestJS handles this gracefully (no runtime error), it's dead code that should be cleaned up.

♻️ Remove duplicate entry
     UserActivityRepository,
-    UserActivityRepository,
     UserOrgRolesRepository,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/organization/src/organization.module.ts` around lines 57 - 58, Remove
the duplicate provider entry for UserActivityRepository in the providers array
of the OrganizationModule (organization.module.ts); locate the providers list
where UserActivityRepository appears twice and delete the redundant occurrence
so only a single UserActivityRepository provider remains, and while there
confirm there are no other duplicate provider entries in the same array.
apps/notification/src/nats/pendingAckStore.ts (2)

75-75: Refactor nested template literal for readability.

SonarCloud flags the nested template literal. Extract the delay suffix to improve clarity.

♻️ Suggested refactor
         nak: async (delay?: number) => {
           const nc = await this.natsService.connect();
-          nc.publish(metadata.reply, Buffer.from(`-NAK${delay ? ` ${delay}` : ''}`));
+          const delaySuffix = delay ? ` ${delay}` : '';
+          nc.publish(metadata.reply, Buffer.from(`-NAK${delaySuffix}`));
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/notification/src/nats/pendingAckStore.ts` at line 75, Refactor the
nested template literal in the nc.publish call by extracting the delay suffix
into a separate variable (e.g., in pendingAckStore code compute a delaySuffix
from the delay value) and then use that variable in the Buffer.from call so the
publish becomes Buffer.from(`-NAK${delaySuffix}`); update the statement that
currently calls nc.publish(metadata.reply, Buffer.from(`-NAK${delay ? `
${delay}` : ''}`)) to use the new delaySuffix variable for clarity while keeping
the same semantics with nc.publish and metadata.reply.

85-88: Consider adding error handling for delete operation.

Unlike get(), the delete() method has no error handling. If the key doesn't exist or there's a KV connection issue, the error propagates to the caller. For consistency with get(), consider whether this should fail silently or log errors.

♻️ Optional: Add resilient error handling
   async delete(key: PendingKey): Promise<void> {
-    const kv = await this.getKV();
-    await kv.delete(key);
+    try {
+      const kv = await this.getKV();
+      await kv.delete(key);
+    } catch (error) {
+      console.error(`Failed to delete pending ACK for key ${key}:`, error);
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/notification/src/nats/pendingAckStore.ts` around lines 85 - 88, The
delete method in pendingAckStore (async delete(key: PendingKey)) lacks error
handling and will propagate KV errors; wrap the await kv.delete(key) call in a
try-catch, call the same logger used in get() (or the class logger) to log a
debug/warn message on failure and optionally swallow the error to match get()'s
resilient behavior, or rethrow if your design requires failing; ensure you
acquire the KV via getKV() as before and reference the delete method and getKV()
so the change is easy to locate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.demo:
- Around line 249-255: Fix formatting in the .env demo: remove trailing
whitespace after the value of NATS_AUTH_TYPE (currently "nkey "), remove the
extra space after the equals sign for NOTIFICATION_NATS_AUTH_TYPE so it reads
NOTIFICATION_NATS_AUTH_TYPE= (no spaces), and ensure the file ends with a
newline character so ENABLE_NATS_NOTIFICATION=false is followed by an EOF
newline.

In `@apps/notification/src/nats/nats-subscriber.ts`:
- Around line 68-70: The code calls msg.ack() without awaiting it, so
pendingAckStore.delete(data.ackKey) may run before the async ACK completes;
update the handler to await the ACK promise (await msg.ack()) and only then call
await this.pendingAckStore.delete(data.ackKey), handling errors from msg.ack()
(try/catch or propagated) so the pending key is removed only after successful
ACK; locate the logic around msg.ack() and this.pendingAckStore.delete in the
NATS subscriber handler to make this change.

In `@apps/notification/src/nats/nats.service.ts`:
- Around line 110-118: The getKV method currently caches a single KV in this.kv
and ignores the bucketName parameter; update it so caching is per-bucket by
replacing the single this.kv with a Map keyed by bucketName (e.g., this.kvMap)
and return or set entries via this.js.views.kv(bucketName), or if only one
bucket is intended, remove the bucketName parameter and hardcode the bucket
(eliminate this.kv ambiguity). Ensure you update references to getKV and any
initialization of this.kv to use the new Map (or remove usages if you hardcode)
and preserve the connected check and error behavior.

In `@apps/notification/src/nats/pendingAckStore.ts`:
- Around line 80-82: The catch block in pendingAckStore.ts currently swallows
all errors and returns undefined; update the catch to at minimum log the caught
error (using the project's logger if available, otherwise console.error) so
KV/network/data errors are visible, e.g., in the try/catch around the get/save
logic for the pending ACK store (the function containing this catch), and
preserve the existing return behavior only after logging the error.

In `@apps/webhook/src/main.ts`:
- Around line 15-19: Replace the incorrect usage of
process.env.ISSUANCE_NKEY_SEED with process.env.WEBHOOK_NKEY_SEED in the NATS
options calls: update the getNatsOptions invocation that supplies the NKEY seed
(the call using CommonConstants.WEBHOOK_SERVICE) and any similar call in the
Webhook module (the place creating NATS options in webhook.module.ts) so both
pass process.env.WEBHOOK_NKEY_SEED instead of process.env.ISSUANCE_NKEY_SEED.

In `@apps/webhook/src/webhook.module.ts`:
- Around line 23-27: The NATS options in the WebhookModule are using the
issuance NKEY seed (process.env.ISSUANCE_NKEY_SEED) which can authenticate this
service with the wrong key; update the call to getNatsOptions inside
WebhookModule (where CommonConstants.WEBHOOK_SERVICE is passed) to use a
webhook-specific environment variable (e.g. process.env.WEBHOOK_NKEY_SEED)
instead of ISSUANCE_NKEY_SEED, and ensure the new env var is documented/loaded
where other service envs are handled.

In `@libs/common/src/nats.config.ts`:
- Around line 39-49: The usernamePassword branch in the NATS config silently
falls back to unauthenticated mode when NATS_USER or NATS_PASSWORD are missing;
update the case 'usernamePassword' handling to validate process.env.NATS_USER
and process.env.NATS_PASSWORD and, if either is absent, throw a clear Error (or
log and throw) instead of returning baseOptions so the misconfiguration is
surfaced; ensure the error message names the missing env var(s) and keep using
usernamePasswordAuthenticator(user, pass) when both are present.
- Around line 29-37: The 'creds' branch in the switch silently falls back to
unauthenticated baseOptions when creds is missing or unreadable; modify the case
'creds' block in nats.config.ts (the code that currently calls readFileSync and
returns {...baseOptions, authenticator: credsAuthenticator(utf8)}) to fail fast:
if creds is falsy or the file cannot be read, throw a clear Error identifying
the missing/invalid creds (include the creds path and the selected auth type),
and only call credsAuthenticator(readFileSync(...)) and return the auth options
when the file exists and is successfully read; ensure the thrown message gives
enough context for debugging.

---

Nitpick comments:
In @.env.sample:
- Around line 279-283: Remove trailing whitespace and normalize spacing around
equals for the env keys shown: ensure lines use KEY=VALUE with no extra spaces
(fix NOTIFICATION_NATS_AUTH_TYPE and NATS_AUTH_TYPE). Reorder the block so
NATS_AUTH_TYPE appears before NATS_CREDS_FILE if that key exists elsewhere
(preserve logical ordering of NATS_* keys). Ensure there's a single blank
newline at end of file. Target the keys NATS_AUTH_TYPE, NATS_CREDS_FILE,
NOTIFICATION_NATS_AUTH_TYPE, and ENABLE_NATS_NOTIFICATION when making these
edits.

In `@apps/notification/src/nats/nats.service.ts`:
- Line 16: Replace the locally declared NatsAuthType in nats.service.ts with the
shared definition by importing NatsAuthType from the common nats config (e.g.,
from libs/common/src/nats.config.ts); if NatsAuthType is not exported from that
module, export it there first, then update nats.service.ts to remove the
duplicate type and add the import so the service uses the single shared
NatsAuthType definition.

In `@apps/notification/src/nats/pendingAckStore.ts`:
- Line 75: Refactor the nested template literal in the nc.publish call by
extracting the delay suffix into a separate variable (e.g., in pendingAckStore
code compute a delaySuffix from the delay value) and then use that variable in
the Buffer.from call so the publish becomes Buffer.from(`-NAK${delaySuffix}`);
update the statement that currently calls nc.publish(metadata.reply,
Buffer.from(`-NAK${delay ? ` ${delay}` : ''}`)) to use the new delaySuffix
variable for clarity while keeping the same semantics with nc.publish and
metadata.reply.
- Around line 85-88: The delete method in pendingAckStore (async delete(key:
PendingKey)) lacks error handling and will propagate KV errors; wrap the await
kv.delete(key) call in a try-catch, call the same logger used in get() (or the
class logger) to log a debug/warn message on failure and optionally swallow the
error to match get()'s resilient behavior, or rethrow if your design requires
failing; ensure you acquire the KV via getKV() as before and reference the
delete method and getKV() so the change is easy to locate.

In `@apps/organization/src/organization.module.ts`:
- Around line 57-58: Remove the duplicate provider entry for
UserActivityRepository in the providers array of the OrganizationModule
(organization.module.ts); locate the providers list where UserActivityRepository
appears twice and delete the redundant occurrence so only a single
UserActivityRepository provider remains, and while there confirm there are no
other duplicate provider entries in the same array.

In `@libs/common/src/common.utils.ts`:
- Around line 153-156: The shouldLoadNatsNotification function currently
compares process.env.ENABLE_NATS_NOTIFICATION with 'true' using strict equality,
which is case-sensitive; update shouldLoadNatsNotification to perform a
case-insensitive check (e.g., normalize with .toLowerCase() and optional
.trim()) and guard against undefined/null before calling string methods so
values like 'TRUE' or ' True ' return true, mirroring the behavior of
shouldLoadOidcModules.

In `@libs/common/src/nats.config.ts`:
- Around line 53-61: The switch default/'nkey' branch currently returns
baseOptions silently when nkeySeed is missing; update that branch to emit a
warning (e.g., via the existing logger or console.warn) when NATS auth type is
'nkey' (or default) but nkeySeed is falsy, then continue to return baseOptions;
reference the nkeySeed variable, the nkeyAuthenticator usage, and baseOptions in
the change so the warning clearly states that nkey authentication was requested
but seed is missing and the client will connect unauthenticated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd0f9bf3-1cad-48e4-acfa-ad7d4281b3bf

📥 Commits

Reviewing files that changed from the base of the PR and between ab28429 and c481f6f.

📒 Files selected for processing (65)
  • .env.demo
  • .env.sample
  • apps/agent-provisioning/src/agent-provisioning.module.ts
  • apps/agent-provisioning/src/main.ts
  • apps/agent-service/src/agent-service.module.ts
  • apps/agent-service/src/main.ts
  • apps/api-gateway/src/agent-service/agent-service.module.ts
  • apps/api-gateway/src/app.module.ts
  • apps/api-gateway/src/authz/authz.module.ts
  • apps/api-gateway/src/cloud-wallet/cloud-wallet.module.ts
  • apps/api-gateway/src/connection/connection.module.ts
  • apps/api-gateway/src/credential-definition/credential-definition.module.ts
  • apps/api-gateway/src/ecosystem/ecosystem.module.ts
  • apps/api-gateway/src/fido/fido.module.ts
  • apps/api-gateway/src/geo-location/geo-location.module.ts
  • apps/api-gateway/src/issuance/issuance.module.ts
  • apps/api-gateway/src/main.ts
  • apps/api-gateway/src/notification/notification.module.ts
  • apps/api-gateway/src/oid4vc-issuance/oid4vc-issuance.module.ts
  • apps/api-gateway/src/oid4vc-verification/oid4vc-verification.module.ts
  • apps/api-gateway/src/organization/organization.module.ts
  • apps/api-gateway/src/platform/platform.module.ts
  • apps/api-gateway/src/schema/schema.module.ts
  • apps/api-gateway/src/user/user.module.ts
  • apps/api-gateway/src/utilities/utilities.module.ts
  • apps/api-gateway/src/verification/verification.module.ts
  • apps/api-gateway/src/webhook/webhook.module.ts
  • apps/api-gateway/src/x509/x509.module.ts
  • apps/cloud-wallet/src/cloud-wallet.module.ts
  • apps/cloud-wallet/src/main.ts
  • apps/connection/src/connection.module.ts
  • apps/connection/src/main.ts
  • apps/ecosystem/src/ecosystem.module.ts
  • apps/ecosystem/src/main.ts
  • apps/geo-location/src/geo-location.module.ts
  • apps/geo-location/src/main.ts
  • apps/issuance/src/issuance.module.ts
  • apps/issuance/src/main.ts
  • apps/ledger/src/credential-definition/credential-definition.module.ts
  • apps/ledger/src/ledger.module.ts
  • apps/ledger/src/main.ts
  • apps/ledger/src/schema/schema.module.ts
  • apps/notification/src/main.ts
  • apps/notification/src/nats/nats-subscriber.ts
  • apps/notification/src/nats/nats.service.ts
  • apps/notification/src/nats/pendingAckStore.ts
  • apps/notification/src/notification.module.ts
  • apps/oid4vc-issuance/src/main.ts
  • apps/oid4vc-issuance/src/oid4vc-issuance.module.ts
  • apps/oid4vc-verification/src/main.ts
  • apps/oid4vc-verification/src/oid4vc-verification.module.ts
  • apps/organization/src/main.ts
  • apps/organization/src/organization.module.ts
  • apps/user/src/main.ts
  • apps/user/src/user.module.ts
  • apps/utility/src/main.ts
  • apps/utility/src/utilities.module.ts
  • apps/verification/src/main.ts
  • apps/verification/src/verification.module.ts
  • apps/webhook/src/main.ts
  • apps/webhook/src/webhook.module.ts
  • apps/x509/src/main.ts
  • apps/x509/src/x509.module.ts
  • libs/common/src/common.utils.ts
  • libs/common/src/nats.config.ts

Comment thread .env.demo
Comment thread apps/notification/src/nats/nats-subscriber.ts
Comment thread apps/notification/src/nats/nats.service.ts
Comment thread apps/notification/src/nats/pendingAckStore.ts Outdated
Comment thread apps/webhook/src/main.ts
Comment thread apps/webhook/src/webhook.module.ts
Comment thread libs/common/src/nats.config.ts Outdated
Comment thread libs/common/src/nats.config.ts
Signed-off-by: RinkalBhojani <rinkal.bhojani@ayanworks.com>
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
.env.demo (1)

249-255: ⚠️ Potential issue | 🟡 Minor

Resolve remaining dotenv-linter warnings (key order + EOF newline).

NATS_AUTH_TYPE should be placed before NATS_CREDS_FILE, and the file should end with a trailing newline to clear current lint warnings.

🔧 Proposed fix
-NATS_CREDS_FILE=
-# 'nkey' | 'creds' | 'usernamePassword' | 'none'
 NATS_AUTH_TYPE=nkey
 # 'nkey' | 'creds' | 'usernamePassword' | 'none'
+NATS_CREDS_FILE=
+# 'nkey' | 'creds' | 'usernamePassword' | 'none'
 NOTIFICATION_NATS_AUTH_TYPE=
 
 ENABLE_NATS_NOTIFICATION=false
+
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.demo around lines 249 - 255, The dotenv file has lint warnings due to
key order and missing EOF newline: move the NATS_AUTH_TYPE entry above
NATS_CREDS_FILE (so NATS_AUTH_TYPE appears before NATS_CREDS_FILE), ensure
NOTIFICATION_NATS_AUTH_TYPE remains in its intended position, and add a trailing
newline at the end of the file so the file ends with a single EOF newline to
satisfy dotenv-linter.
🧹 Nitpick comments (2)
.env.sample (1)

277-283: Address dotenv-linter warnings in the new NATS env block.

Please reorder NATS_AUTH_TYPE before NATS_CREDS_FILE and add a trailing blank line at EOF to keep .env.sample lint-clean.

Proposed diff
-PULL_CONSUMER=hub-pull-consumer
-
-NATS_CREDS_FILE=/platform/app_user.creds
-# 'nkey' | 'creds' | 'usernamePassword' | 'none'
-NATS_AUTH_TYPE=nkey
+PULL_CONSUMER=hub-pull-consumer
+
+# 'nkey' | 'creds' | 'usernamePassword' | 'none'
+NATS_AUTH_TYPE=nkey
+NATS_CREDS_FILE=/platform/app_user.creds
 # 'nkey' | 'creds' | 'usernamePassword' | 'none'
 NOTIFICATION_NATS_AUTH_TYPE=
 
 ENABLE_NATS_NOTIFICATION=false
+
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.sample around lines 277 - 283, Move the NATS env variables so
NATS_AUTH_TYPE appears before NATS_CREDS_FILE (i.e., reorder the block so
NATS_AUTH_TYPE, then NATS_CREDS_FILE) and ensure the file ends with a single
trailing newline; update the lines containing NATS_AUTH_TYPE, NATS_CREDS_FILE,
NOTIFICATION_NATS_AUTH_TYPE and ENABLE_NATS_NOTIFICATION accordingly to satisfy
dotenv-linter.
apps/notification/src/nats/pendingAckStore.ts (1)

34-37: Remove the impossible msg.info null path.

save() already accepts a JsMsg, so info is part of the contract here. Keeping this branch makes the real precondition (reply) harder to see. Based on learnings: In NATS.js (nats npm package), the JsMsg interface has an info property typed as DeliveryInfo (not optional), meaning msg.info is always present on JetStream messages and doesn't require null checking or non-null assertions. (nats-io.github.io)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/notification/src/nats/pendingAckStore.ts` around lines 34 - 37, Remove
the impossible null-check for msg.info in save(): since save() accepts a JsMsg
and JsMsg.info is non-optional, delete the `if (!info) { throw ... }` branch so
the real precondition (msg.reply) is obvious; ensure any downstream uses of
`info` use it directly (e.g., in save() and any callers) and add a brief comment
noting that JsMsg.info is always present per the nats JsMsg contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/notification/src/nats/pendingAckStore.ts`:
- Around line 25-27: getKV() assumes the KV bucket exists (KV_BUCKET) and calls
NatsService.getKV() which only binds to this.js.views.kv(bucketName); to fix,
add an explicit provisioning or startup validation: either create the KV bucket
during NATS bootstrap (mirror ensureStream() behavior) or add a startup check
that calls NatsService.createOrEnsureKV(KV_BUCKET) (or similar) and fail fast if
creation/binding fails; update pendingAckStore.getKV(), and ensure NatsService
exposes a method to create/ensure a KV bucket so save(), delete(), and get()
won't throw or silently fail.
- Around line 59-80: The get(key: PendingKey) function is incorrectly casting
mockMsg to JsMsg; define and export a new PendingAckHandle interface that
explicitly lists the properties you intend to provide (e.g., reply?: string,
ack(): Promise<void>, nak?(delay?: number): Promise<void>) and change get() to
return Promise<PendingAckHandle | undefined>; remove the JsMsg cast on mockMsg,
ensure mockMsg matches PendingAckHandle, and update the consumer in
nats-subscriber.ts to import/use PendingAckHandle instead of JsMsg so the
contract is explicit and safe.

---

Duplicate comments:
In @.env.demo:
- Around line 249-255: The dotenv file has lint warnings due to key order and
missing EOF newline: move the NATS_AUTH_TYPE entry above NATS_CREDS_FILE (so
NATS_AUTH_TYPE appears before NATS_CREDS_FILE), ensure
NOTIFICATION_NATS_AUTH_TYPE remains in its intended position, and add a trailing
newline at the end of the file so the file ends with a single EOF newline to
satisfy dotenv-linter.

---

Nitpick comments:
In @.env.sample:
- Around line 277-283: Move the NATS env variables so NATS_AUTH_TYPE appears
before NATS_CREDS_FILE (i.e., reorder the block so NATS_AUTH_TYPE, then
NATS_CREDS_FILE) and ensure the file ends with a single trailing newline; update
the lines containing NATS_AUTH_TYPE, NATS_CREDS_FILE,
NOTIFICATION_NATS_AUTH_TYPE and ENABLE_NATS_NOTIFICATION accordingly to satisfy
dotenv-linter.

In `@apps/notification/src/nats/pendingAckStore.ts`:
- Around line 34-37: Remove the impossible null-check for msg.info in save():
since save() accepts a JsMsg and JsMsg.info is non-optional, delete the `if
(!info) { throw ... }` branch so the real precondition (msg.reply) is obvious;
ensure any downstream uses of `info` use it directly (e.g., in save() and any
callers) and add a brief comment noting that JsMsg.info is always present per
the nats JsMsg contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d80624c0-e600-4e6a-835c-0ece7df3b209

📥 Commits

Reviewing files that changed from the base of the PR and between c481f6f and d06fd6f.

📒 Files selected for processing (6)
  • .env.demo
  • .env.sample
  • apps/notification/src/nats/nats-subscriber.ts
  • apps/notification/src/nats/pendingAckStore.ts
  • apps/oid4vc-verification/src/oid4vc-verification.service.ts
  • libs/common/src/nats.config.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/oid4vc-verification/src/oid4vc-verification.service.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/notification/src/nats/nats-subscriber.ts
  • libs/common/src/nats.config.ts

Comment on lines +25 to +27
private async getKV(): Promise<KV> {
return this.natsService.getKV(this.KV_BUCKET);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "References to the bucket name:"
rg -n -C2 '\bPENDING_ACK\b'

echo
echo "JetStream/KV initialization paths:"
rg -n -C3 'views\.kv\s*\(|getKV\s*\(|jetstreamManager\s*\('

Repository: credebl/platform

Length of output: 6265


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Finding ensureStream and related functions ==="
fd -t f '\.ts$' -x grep -l 'ensureStream\|ensureBucket\|ensureConsumer' {} \;

echo
echo "=== Examining ensureStream implementation ==="
rg -n 'export.*ensureStream|function ensureStream|const ensureStream' -A 15

echo
echo "=== Checking nats.module.ts around initialization ==="
rg -n 'ensureStream|ensureConsumer|ensureBucket' apps/notification/src/nats/nats.module.ts -B 3 -A 3

Repository: credebl/platform

Length of output: 253


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== jetstream.setup.ts full content ==="
cat -n apps/notification/src/nats/jetstream.setup.ts

echo
echo "=== Checking for bucket-related functions across NATS folder ==="
rg -n 'kv\(|bucket|PENDING_ACK|createBucket' apps/notification/src/nats/ --type ts

Repository: credebl/platform

Length of output: 5523


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Full nats.module.ts initialization ==="
cat -n apps/notification/src/nats/nats.module.ts | head -80

echo
echo "=== Check if there are any bucket-related initialization patterns elsewhere ==="
rg -n 'buckets\.|views\.kv|createBucket|bucket\.create' apps/notification/ --type ts

Repository: credebl/platform

Length of output: 2218


Verify PENDING_ACK is provisioned before first use.

getKV() currently assumes the bucket already exists. NatsService.getKV() only binds via this.js.views.kv(bucketName), which will fail if the bucket is missing. The codebase provisions JetStream streams via ensureStream() during startup, but there is no equivalent bucket provisioning. Missing the bucket causes save() and delete() to throw, and get() to return undefined after logging. Either create the bucket during NATS bootstrap or fail fast with an explicit startup check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/notification/src/nats/pendingAckStore.ts` around lines 25 - 27, getKV()
assumes the KV bucket exists (KV_BUCKET) and calls NatsService.getKV() which
only binds to this.js.views.kv(bucketName); to fix, add an explicit provisioning
or startup validation: either create the KV bucket during NATS bootstrap (mirror
ensureStream() behavior) or add a startup check that calls
NatsService.createOrEnsureKV(KV_BUCKET) (or similar) and fail fast if
creation/binding fails; update pendingAckStore.getKV(), and ensure NatsService
exposes a method to create/ensure a KV bucket so save(), delete(), and get()
won't throw or silently fail.

Comment on lines +59 to +80
async get(key: PendingKey): Promise<JsMsg | undefined> {
try {
const kv = await this.getKV();
const entry: KvEntry | null = await kv.get(key);

if (!entry) {
return undefined;
}

const metadata: PendingMessageMetadata = JSON.parse(new TextDecoder().decode(entry.value));

const mockMsg = {
reply: metadata.reply,
ack: async () => {
const nc = await this.natsService.connect();
nc.publish(metadata.reply);
},
nak: async (delay?: number) => {
const nc = await this.natsService.connect();
nc.publish(metadata.reply, Buffer.from(`-NAK${delay ? ` ${delay}` : ''}`));
}
} as unknown as JsMsg;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find the file and understand the full context
find . -type f -name "pendingAckStore.ts" | head -5

Repository: credebl/platform

Length of output: 107


🏁 Script executed:

# Look for call sites that use the result of pendingAckStore.get()
rg "\.get\(" --type typescript -A 5 | grep -A 5 "pendingAckStore"

Repository: credebl/platform

Length of output: 94


🏁 Script executed:

# Search for accesses to msg.subject, msg.info, msg.seq, msg.redelivered in the codebase
rg "msg\.(subject|info|seq|redelivered)" --type typescript -B 2 -A 2

Repository: credebl/platform

Length of output: 94


🏁 Script executed:

# Check the PendingMessageMetadata interface definition
rg "interface PendingMessageMetadata|type PendingMessageMetadata" --type typescript -A 10

Repository: credebl/platform

Length of output: 94


🏁 Script executed:

# View the complete pendingAckStore.ts file to understand all usages
wc -l apps/notification/src/nats/pendingAckStore.ts

Repository: credebl/platform

Length of output: 108


🏁 Script executed:

# Search for usages of pendingAckStore.get() without file type constraint
rg "pendingAckStore" -A 10 | grep -E "(get\(|\.get\(|pendingAckStore)"

Repository: credebl/platform

Length of output: 911


🏁 Script executed:

# Look for any pattern where get() return value is used
rg "\.get\(" -A 5 --glob="*.ts" | head -50

Repository: credebl/platform

Length of output: 2853


🏁 Script executed:

# View the complete pendingAckStore.ts file
cat -n apps/notification/src/nats/pendingAckStore.ts

Repository: credebl/platform

Length of output: 3203


🏁 Script executed:

# Search for accesses to msg properties in the entire codebase
rg "msg\.(subject|info|seq|redelivered|reply|ack|nak)" --glob="*.ts" -B 2 -A 2

Repository: credebl/platform

Length of output: 3933


🏁 Script executed:

# Look for where the returned msg from pendingAckStore.get() is actually used
rg "await this\.pendingAckStore\.get" -B 2 -A 10 --glob="*.ts"

Repository: credebl/platform

Length of output: 1070


🏁 Script executed:

# Check the nats-subscriber.ts file to see full context of msg usage
cat -n apps/notification/src/nats/nats-subscriber.ts | grep -A 30 "pendingAckStore.get"

Repository: credebl/platform

Length of output: 630


🏁 Script executed:

# Verify if there are any other accesses to msg properties after get() call
rg "pendingAckStore.get" -A 20 --glob="*.ts"

Repository: credebl/platform

Length of output: 1254


Remove the JsMsg cast and create a proper PendingAckHandle interface.

The mockMsg object is cast to JsMsg but lacks required properties: info (which is always present on actual JsMsg), seq, and redelivered. While the current call site in nats-subscriber.ts only uses msg.ack() (which is provided), the cast is fundamentally incorrect and masks a contract mismatch. If future code accesses msg.subject or msg.info, it will fail at runtime. Create a dedicated PendingAckHandle interface that honestly declares what this object provides, and update the return type accordingly.

Suggested fix
+interface PendingAckHandle {
+  reply: string;
+  subject: string;
+  streamSequence: number;
+  ack(): Promise<void>;
+  nak(delay?: number): Promise<void>;
+}
+
-  async get(key: PendingKey): Promise<JsMsg | undefined> {
+  async get(key: PendingKey): Promise<PendingAckHandle | undefined> {
     try {
       const kv = await this.getKV();
       const entry: KvEntry | null = await kv.get(key);

       if (!entry) {
         return undefined;
       }

       const metadata: PendingMessageMetadata = JSON.parse(new TextDecoder().decode(entry.value));

-      const mockMsg = {
+      return {
         reply: metadata.reply,
+        subject: metadata.subject,
+        streamSequence: metadata.streamSequence,
         ack: async () => {
           const nc = await this.natsService.connect();
           nc.publish(metadata.reply);
         },
         nak: async (delay?: number) => {
           const nc = await this.natsService.connect();
           nc.publish(metadata.reply, Buffer.from(`-NAK${delay ? ` ${delay}` : ''}`));
         }
-      } as unknown as JsMsg;
-
-      return mockMsg;
+      };
     } catch (error) {
       this.logger.warn(`Failed to retrieve pending ACK for key ${key}`, error);
       return undefined;
     }
   }

Export PendingAckHandle and update the call site in nats-subscriber.ts to depend on it explicitly.

🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 78-78: Refactor this code to not use nested template literals.

See more on https://sonarcloud.io/project/issues?id=credebl_platform&issues=AZ0z-FfKo5CI2C-YylC1&open=AZ0z-FfKo5CI2C-YylC1&pullRequest=1591

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/notification/src/nats/pendingAckStore.ts` around lines 59 - 80, The
get(key: PendingKey) function is incorrectly casting mockMsg to JsMsg; define
and export a new PendingAckHandle interface that explicitly lists the properties
you intend to provide (e.g., reply?: string, ack(): Promise<void>, nak?(delay?:
number): Promise<void>) and change get() to return Promise<PendingAckHandle |
undefined>; remove the JsMsg cast on mockMsg, ensure mockMsg matches
PendingAckHandle, and update the consumer in nats-subscriber.ts to import/use
PendingAckHandle instead of JsMsg so the contract is explicit and safe.

@RinkalBhojani RinkalBhojani merged commit 6f7da22 into main Mar 28, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants