Skip to content

Conversation

@HakobP-Solicy
Copy link
Contributor

@HakobP-Solicy HakobP-Solicy commented Feb 10, 2026

Summary by CodeRabbit

  • New Features
    • Added TLSNotary identity verification support for Web2-like identities on GitHub, Discord, and Telegram.
    • Enabled users to add and remove TLSNotary-verified identities with cryptographic verification.
    • Extended identity handling to support new context types including nomis and tlsn for identity verification workflows.

@qodo-code-review
Copy link
Contributor

Review Summary by Qodo

Add TLSNotary identity verification and management

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add TLSNotary identity verification for GitHub, Discord, Telegram
• Implement identity add/remove operations via TLSN proof verification
• Create server-side proof structure validation module with WASM support
• Integrate TLSN identity handling into GCR identity routines and request handlers
Diagram
flowchart LR
  A["TLSNotary Proof"] -->|verifyTLSNotaryPresentation| B["Proof Validation"]
  B -->|parseHttpResponse| C["Extract User Data"]
  C -->|extractUser| D["Verified Identity"]
  D -->|applyTLSNIdentityAdd| E["GCR Storage"]
  E -->|IncentiveManager| F["Award Incentives"]
  G["Remove Request"] -->|applyTLSNIdentityRemove| H["GCR Update"]
  H -->|IncentiveManager| I["Rollback Incentives"]
Loading

Grey Divider

File Changes

1. src/libs/tlsnotary/verifier.ts ✨ Enhancement +336/-0

TLSNotary proof verification and parsing module

• New TLSNotary proof verification module with structure validation
• Implements proof parsing, HTTP response extraction, and user data extraction
• Supports GitHub, Discord, and Telegram identity contexts
• Provides verifyTLSNotaryPresentation, parseHttpResponse, extractUser, and verifyTLSNProof
 functions
• Includes type definitions for TLSNotary presentations and verification results

src/libs/tlsnotary/verifier.ts


2. src/libs/tlsnotary/index.ts ✨ Enhancement +20/-0

TLSNotary module public API exports

• New barrel export file for TLSNotary verification module
• Exports all public functions and types from verifier module
• Provides clean API surface for identity routines and handlers

src/libs/tlsnotary/index.ts


3. src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts ✨ Enhancement +354/-5

TLSN identity add/remove operations and verification

• Import TLSNotary verification functions and types
• Add applyTLSNIdentityAdd method for adding TLSN-verified identities with proof validation
• Add applyTLSNIdentityRemove method for removing TLSN identities
• Define TLSN_EXPECTED_ENDPOINTS mapping for GitHub, Discord, Telegram servers
• Integrate TLSN operations into identity operation dispatcher with tlsnadd and tlsnremove cases
• Implement security checks comparing claimed vs extracted identity values
• Award/rollback incentives for GitHub, Discord, Telegram connections

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts


View more (2)
4. src/libs/network/routines/transactions/handleIdentityRequest.ts ✨ Enhancement +8/-3

TLSN identity request handling integration

• Add tlsn_identity_assign case to identity request handler
• Call verifyTLSNProof for TLSN identity verification
• Add tlsn_identity_remove case to removal handler
• Remove unused imports (log, ensureGCRForUser)
• Import verifyTLSNProof and TLSNIdentityPayload from tlsnotary module

src/libs/network/routines/transactions/handleIdentityRequest.ts


5. src/libs/omniprotocol/protocol/handlers/gcr.ts ✨ Enhancement +6/-4

GCR handler context validation for TLSN support

• Update IdentityAssignRequest interface to include nomis and tlsn contexts
• Update context validation to accept nomis and tlsn in addition to existing contexts
• Add type assertion for editOperation to handle new TLSN context
• Update error messages to reflect new supported contexts

src/libs/omniprotocol/protocol/handlers/gcr.ts


Grey Divider

Qodo Logo

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 10, 2026

Walkthrough

Introduces TLSNotary integration for Web2 identity verification. Adds a new TLSNotary verification module with proof validation, HTTP response parsing, and user extraction. Integrates TLSNotary identity add/remove operations into GCR identity routines, extends identity request handlers to support TLSNotary flows, and expands supported identity contexts.

Changes

Cohort / File(s) Summary
TLSNotary Verification Module
src/libs/tlsnotary/index.ts, src/libs/tlsnotary/verifier.ts
New TLSNotary verification module providing structure-only proof validation (cryptographic verification delegated to frontend), HTTP response parsing, user extraction from platform APIs (GitHub, Discord, Telegram), and context-specific verification logic. Includes type definitions for presentations, verification results, and identity payloads. Verifier performs data format validation, response parsing with error handling, and contextual user credential extraction.
GCR Identity Routines
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts
Added two new static methods: applyTLSNIdentityAdd for adding TLSNotary-verified identities (includes validation, WASM crypto verification, server-name checks, response extraction, cross-field matching, persistence, and incentive triggering) and applyTLSNIdentityRemove for identity removal with optional incentive rollback. Introduced TLSN_EXPECTED_ENDPOINTS configuration for context-specific server and path expectations. Integrated tlsnadd and tlsnremove switch cases into the central apply method.
Identity Request Handler
src/libs/network/routines/transactions/handleIdentityRequest.ts
Added support for new TLSNotary identity methods: tlsn_identity_assign (invokes verifyTLSNProof with payload verification) and tlsn_identity_remove (returns success for removal). Replaced imports from logging/repository utils with TLSNotary verification imports.
OmniProtocol GCR Handler
src/libs/omniprotocol/protocol/handlers/gcr.ts
Expanded IdentityAssignRequest context type to include "nomis" and "tlsn" alongside existing contexts. Added type assertion when delegating to gcrIdentityRoutines.apply with explanatory inline comments.

Sequence Diagram

sequenceDiagram
    actor Client
    participant Handler as Identity Request<br/>Handler
    participant Verifier as TLSNotary<br/>Verifier
    participant GCRRoutines as GCR Identity<br/>Routines
    participant Parser as HTTP Response<br/>Parser
    participant DB as GCR<br/>Database
    participant Incentive as Incentive<br/>Manager

    Client->>Handler: POST tlsn_identity_assign
    Handler->>Verifier: verifyTLSNProof(payload)
    Verifier->>Verifier: validate context<br/>(github/discord/telegram)
    Verifier->>Verifier: structure validation<br/>of proof
    Verifier->>Parser: parseHttpResponse()
    Parser->>Parser: extract statusLine,<br/>headers, body
    Verifier->>Verifier: extractUser(context,<br/>responseBody)
    Verifier-->>Handler: {success, extractedUsername}
    Handler->>GCRRoutines: applyTLSNIdentityAdd(editOp)
    GCRRoutines->>GCRRoutines: structural/semantic<br/>validation
    GCRRoutines->>GCRRoutines: WASM crypto<br/>verification
    GCRRoutines->>GCRRoutines: server-name &<br/>cross-field matching
    GCRRoutines->>DB: persist identity
    GCRRoutines->>Incentive: trigger context<br/>incentives
    Incentive-->>GCRRoutines: success
    GCRRoutines-->>Handler: GCRResult
    Handler-->>Client: success response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Add TLSNotary Support #554: Implements the same TLSNotary feature set across all related modules—verifier, index exports, GCR routines, and identity handling.
  • Ud identities #481: Demonstrates the same architectural pattern for adding identity types—new manager module, GCR routines methods, incentive integration.
  • Integrated Nomis Identity #552: Modifies the same GCRIdentityRoutines class to add another identity type's static methods and apply-switch branches.

Suggested labels

Possible security concern, Review effort 5/5

Suggested reviewers

  • cwilvx

Poem

🐰 A rabbit hops through TLSNotary gates,
Verifying Web2 proofs, ne'er too late,
From GitHub, Discord, Telegram's hall,
Identity blooms for one and for all! 🔐✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: adding TLSNotary identity assignment and removal logic across multiple files and modules.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-tlsn-identity-assign-and-remove-logics

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.

@sonarqubecloud
Copy link

@qodo-code-review
Copy link
Contributor

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (4) 📎 Requirement gaps (0)

Grey Divider


Action required

1. PII in TLSN logs 📘 Rule violation ⛨ Security
Description
The new TLSN identity flow logs username and userId, which can be considered personal user
information and must not appear in application logs. These logs are also plain interpolated strings,
making them harder to audit/parse consistently.
Code

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[R1231-1233]

+        log.info(
+            `[TLSN Identity] Verifying proof for ${context} identity: ${username}`,
+        )
Evidence
The Secure Logging Practices rule forbids sensitive/personal user info in logs and requires logs to
be audit-friendly. The added log statements include username/userId directly in log messages.

Rule 5: Generic: Secure Logging Practices
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1231-1233]
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1328-1330]
src/libs/tlsnotary/verifier.ts[326-328]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
TLSN identity-related log messages include personal user information (`username`, `userId`) and are emitted as unstructured interpolated strings.

## Issue Context
Compliance requires logs to avoid sensitive/personal user data and be useful for auditing. Identity operations are security-sensitive, so logging must be carefully redacted and ideally structured.

## Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1231-1331]
- src/libs/tlsnotary/verifier.ts[326-328]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Internal errors returned to clients 📘 Rule violation ⛨ Security
Description
Several TLSN verification failures return verified.error and other internal details (e.g.,
expected/actual server names) in the message, which can leak implementation details to end-users.
Detailed failure context should be kept in internal logs, while client-facing messages remain
generic.
Code

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[R1238-1245]

+        if (!verified.success) {
+            log.warn(
+                `[TLSN Identity] Proof verification failed: ${verified.error}`,
+            )
+            return {
+                success: false,
+                message: `Proof verification failed: ${verified.error}`,
+            }
Evidence
The Secure Error Handling rule requires user-facing errors to avoid exposing internal details;
however, the new code returns raw verification errors to the caller. The same pattern exists in the
TLSN verifier API used by request handling.

Rule 4: Generic: Secure Error Handling
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1238-1245]
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1256-1259]
src/libs/tlsnotary/verifier.ts[319-323]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
TLSN-related APIs return internal verification details (`verified.error`, server mismatch specifics) in response messages, risking information leakage.

## Issue Context
Client-facing errors should be generic per secure error-handling requirements; detailed context should be logged internally for debugging.

## Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1238-1260]
- src/libs/tlsnotary/verifier.ts[317-324]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. TLSN add trusts client claims 📘 Rule violation ⛨ Security
Description
When operating in structure-validation-only mode (or when verified.recv is absent), the code
proceeds by trusting client-provided username/userId rather than deriving them from a verified
proof. This is insufficient validation for an externally supplied identity claim and can enable
identity spoofing.
Code

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[R1326-1332]

+            // WASM verification disabled - trust claimed data with warning
+            // NOTE: This is less secure but allows operation until WASM works in Node.js
+            log.warn(
+                `[TLSN Identity] WASM disabled - trusting claimed data for ${context}: ${username} (${userId})`,
+            )
+            extractedUser = { username, userId: String(userId) }
+        }
Evidence
The Security-First Input Validation rule requires validating external inputs; however, the verifier
explicitly only checks structure and the identity add path explicitly "trusts claimed data" in that
mode, meaning claims are not cryptographically validated server-side.

Rule 6: Generic: Security-First Input Validation and Data Handling
src/libs/tlsnotary/verifier.ts[6-9]
src/libs/tlsnotary/verifier.ts[156-160]
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1326-1332]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
TLSN identity assignment can succeed without server-side cryptographic verification by trusting client claims when running in structure-only verification mode.

## Issue Context
Identity binding is security-sensitive. Validating only proof structure is insufficient to authenticate the claimed `username`/`userId`.

## Fix Focus Areas
- src/libs/tlsnotary/verifier.ts[1-167]
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1230-1332]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. TLSN remove lacks audit log 📘 Rule violation ✧ Quality
Description
The TLSN identity removal operation performs a write (gcrMainRepository.save) without emitting an
audit log capturing who performed the action, what changed, and the outcome. This makes it harder to
reconstruct identity changes during incident response or compliance reviews.
Code

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[R1456-1472]

+        if (!simulate) {
+            await gcrMainRepository.save(accountGCR)
+
+            // Trigger incentive rollback if applicable
+            if (context === "github" && identity.userId) {
+                await IncentiveManager.githubUnlinked(
+                    editOperation.account,
+                    identity.userId,
+                )
+            } else if (context === "discord") {
+                await IncentiveManager.discordUnlinked(editOperation.account)
+            } else if (context === "telegram") {
+                await IncentiveManager.telegramUnlinked(editOperation.account)
+            }
+        }
+
+        return { success: true, message: "TLSN identity removed successfully" }
Evidence
The audit trail rule requires logging critical actions (writes/permission-like changes) with enough
context to reconstruct events. The new TLSN removal path saves changes and returns success but
contains no corresponding audit logging.

Rule 1: Generic: Comprehensive Audit Trails
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1456-1472]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
TLSN identity removal writes state without an accompanying audit log entry describing the action and outcome.

## Issue Context
Identity add/remove operations are critical security events and should be traceable without exposing sensitive user data.

## Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1422-1473]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. TLSN remove deletes non-TLSN 🐞 Bug ✓ Correctness
Description
applyTLSNIdentityRemove deletes identities purely by context+username and does not check that the
record was added via TLSN (proofType). This allows a tlsn_remove operation to remove legacy web2
identities and trigger incentive rollback unexpectedly.
Code

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[R1427-1455]

+        const { context, username } = editOperation.data
+
+        if (!context || !username) {
+            return {
+                success: false,
+                message: "Invalid payload: missing context or username",
+            }
+        }
+
+        const accountGCR = await ensureGCRForUser(editOperation.account)
+
+        accountGCR.identities.web2 = accountGCR.identities.web2 || {}
+        accountGCR.identities.web2[context] =
+            accountGCR.identities.web2[context] || []
+
+        // Find the identity to remove
+        const identity = accountGCR.identities.web2[context].find(
+            (id: Web2GCRData["data"]) => id.username === username,
+        )
+
+        if (!identity) {
+            return { success: false, message: "Identity not found" }
+        }
+
+        // Filter out the identity
+        accountGCR.identities.web2[context] = accountGCR.identities.web2[
+            context
+        ].filter((id: Web2GCRData["data"]) => id.username !== username)
+
Evidence
Web2 identities are stored in identities.web2[context] without a proofType field, and removal
for TLSN searches and filters only by username. Because TLSN identities are stored in the same
bucket (identities.web2[context]) and TLSN remove doesn’t validate proof provenance, it can delete
any entry that matches the username, not specifically TLSN-added records.

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[197-216]
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[277-281]
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1427-1455]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`applyTLSNIdentityRemove` removes any web2 identity matching `username` without checking `proofType === &quot;tlsn&quot;`, and without validating `context` is one of the TLSN-supported platforms. This can delete non-TLSN identities.

### Issue Context
TLSN identities are stored in the same `accountGCR.identities.web2[context]` arrays used for regular web2 identities.

### Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1422-1470]
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[197-415]

### Suggested changes
1. Validate `context` in remove (e.g., only `github|discord|telegram`).
2. When finding/filtering, require the record to be TLSN-added, e.g. `(id as any).proofType === &quot;tlsn&quot;`.
3. Prefer removing by `userId` (or both `userId` and `username`) to avoid removing the wrong record when usernames collide or change.
4. Only trigger TLSN incentive rollback when the removed record is confirmed TLSN provenance.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. TLSN add lacks field checks 🐞 Bug ⛯ Reliability
Description
applyTLSNIdentityAdd does not validate that username/userId/proof are present and well-typed before
use, allowing records with userId="undefined" or empty usernames to be stored (especially in the
WASM-disabled fallback). This can break deduplication and lead to corrupted identity data.
Code

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[R1180-1187]

+        // Extract context from editOperation.data (top level)
+        const { context } = editOperation.data
+        // Extract nested data fields (proof, username, userId are inside data.data)
+        const {
+            proof: proofString,
+            username,
+            userId,
+        } = editOperation.data.data || {}
Evidence
The TLSN add routine destructures username/userId from editOperation.data.data || {} without
validating required fields. In the fallback path it uses String(userId) even if userId is
undefined, and deduplication compares against String(userId), enabling storage of invalid
identities and inconsistent behavior.

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1180-1187]
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1325-1332]
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1341-1344]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
TLSN identity add accepts missing/invalid `username`, `userId`, and `proof` fields because it destructures from `editOperation.data.data || {}` and later stringifies `userId` without validation.

### Issue Context
This becomes especially problematic in the current fallback path (no `verified.recv`) where claimed values are trusted.

### Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1180-1203]
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1325-1348]

### Suggested changes
1. Early-validate: `context` is supported, `username` is a non-empty string, `userId` is a non-empty string/number, and `proofString` is present.
2. Reject on `userId == null` (avoid `String(undefined)`), and consider normalizing to a canonical string.
3. Consider limiting proof size (to avoid large JSON parse / DB bloat) and rejecting non-object proofs after parse.
4. Use the validated/extracted values (once real verification exists) for storage rather than the raw claimed ones.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +1231 to +1233
log.info(
`[TLSN Identity] Verifying proof for ${context} identity: ${username}`,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

1. Pii in tlsn logs 📘 Rule violation ⛨ Security

The new TLSN identity flow logs username and userId, which can be considered personal user
information and must not appear in application logs. These logs are also plain interpolated strings,
making them harder to audit/parse consistently.
Agent Prompt
## Issue description
TLSN identity-related log messages include personal user information (`username`, `userId`) and are emitted as unstructured interpolated strings.

## Issue Context
Compliance requires logs to avoid sensitive/personal user data and be useful for auditing. Identity operations are security-sensitive, so logging must be carefully redacted and ideally structured.

## Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1231-1331]
- src/libs/tlsnotary/verifier.ts[326-328]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1238 to +1245
if (!verified.success) {
log.warn(
`[TLSN Identity] Proof verification failed: ${verified.error}`,
)
return {
success: false,
message: `Proof verification failed: ${verified.error}`,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

2. Internal errors returned to clients 📘 Rule violation ⛨ Security

Several TLSN verification failures return verified.error and other internal details (e.g.,
expected/actual server names) in the message, which can leak implementation details to end-users.
Detailed failure context should be kept in internal logs, while client-facing messages remain
generic.
Agent Prompt
## Issue description
TLSN-related APIs return internal verification details (`verified.error`, server mismatch specifics) in response messages, risking information leakage.

## Issue Context
Client-facing errors should be generic per secure error-handling requirements; detailed context should be logged internally for debugging.

## Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1238-1260]
- src/libs/tlsnotary/verifier.ts[317-324]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1326 to +1332
// WASM verification disabled - trust claimed data with warning
// NOTE: This is less secure but allows operation until WASM works in Node.js
log.warn(
`[TLSN Identity] WASM disabled - trusting claimed data for ${context}: ${username} (${userId})`,
)
extractedUser = { username, userId: String(userId) }
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

3. Tlsn add trusts client claims 📘 Rule violation ⛨ Security

When operating in structure-validation-only mode (or when verified.recv is absent), the code
proceeds by trusting client-provided username/userId rather than deriving them from a verified
proof. This is insufficient validation for an externally supplied identity claim and can enable
identity spoofing.
Agent Prompt
## Issue description
TLSN identity assignment can succeed without server-side cryptographic verification by trusting client claims when running in structure-only verification mode.

## Issue Context
Identity binding is security-sensitive. Validating only proof structure is insufficient to authenticate the claimed `username`/`userId`.

## Fix Focus Areas
- src/libs/tlsnotary/verifier.ts[1-167]
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1230-1332]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1456 to +1472
if (!simulate) {
await gcrMainRepository.save(accountGCR)

// Trigger incentive rollback if applicable
if (context === "github" && identity.userId) {
await IncentiveManager.githubUnlinked(
editOperation.account,
identity.userId,
)
} else if (context === "discord") {
await IncentiveManager.discordUnlinked(editOperation.account)
} else if (context === "telegram") {
await IncentiveManager.telegramUnlinked(editOperation.account)
}
}

return { success: true, message: "TLSN identity removed successfully" }
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

4. Tlsn remove lacks audit log 📘 Rule violation ✧ Quality

The TLSN identity removal operation performs a write (gcrMainRepository.save) without emitting an
audit log capturing who performed the action, what changed, and the outcome. This makes it harder to
reconstruct identity changes during incident response or compliance reviews.
Agent Prompt
## Issue description
TLSN identity removal writes state without an accompanying audit log entry describing the action and outcome.

## Issue Context
Identity add/remove operations are critical security events and should be traceable without exposing sensitive user data.

## Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1422-1473]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1427 to +1455
const { context, username } = editOperation.data

if (!context || !username) {
return {
success: false,
message: "Invalid payload: missing context or username",
}
}

const accountGCR = await ensureGCRForUser(editOperation.account)

accountGCR.identities.web2 = accountGCR.identities.web2 || {}
accountGCR.identities.web2[context] =
accountGCR.identities.web2[context] || []

// Find the identity to remove
const identity = accountGCR.identities.web2[context].find(
(id: Web2GCRData["data"]) => id.username === username,
)

if (!identity) {
return { success: false, message: "Identity not found" }
}

// Filter out the identity
accountGCR.identities.web2[context] = accountGCR.identities.web2[
context
].filter((id: Web2GCRData["data"]) => id.username !== username)

Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

5. Tlsn remove deletes non-tlsn 🐞 Bug ✓ Correctness

applyTLSNIdentityRemove deletes identities purely by context+username and does not check that the
record was added via TLSN (proofType). This allows a tlsn_remove operation to remove legacy web2
identities and trigger incentive rollback unexpectedly.
Agent Prompt
### Issue description
`applyTLSNIdentityRemove` removes any web2 identity matching `username` without checking `proofType === "tlsn"`, and without validating `context` is one of the TLSN-supported platforms. This can delete non-TLSN identities.

### Issue Context
TLSN identities are stored in the same `accountGCR.identities.web2[context]` arrays used for regular web2 identities.

### Fix Focus Areas
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[1422-1470]
- src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts[197-415]

### Suggested changes
1. Validate `context` in remove (e.g., only `github|discord|telegram`).
2. When finding/filtering, require the record to be TLSN-added, e.g. `(id as any).proofType === "tlsn"`.
3. Prefer removing by `userId` (or both `userId` and `username`) to avoid removing the wrong record when usernames collide or change.
4. Only trigger TLSN incentive rollback when the removed record is confirmed TLSN provenance.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Contributor

@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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/libs/omniprotocol/protocol/handlers/gcr.ts (1)

46-49: ⚠️ Potential issue | 🟡 Minor

Stale doc comment — doesn't mention nomis or tlsn contexts.

Line 49 says "apply identity changes (xm, web2, pqc, ud)" but should now include nomis and tlsn.

- * Uses GCRIdentityRoutines to apply identity changes (xm, web2, pqc, ud).
+ * Uses GCRIdentityRoutines to apply identity changes (xm, web2, pqc, ud, nomis, tlsn).
🤖 Fix all issues with AI agents
In `@src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts`:
- Around line 1165-1173: The JSDoc for the "Add an identity via TLSNotary proof
verification" routine is inaccurate: the code path currently falls back to
trusting client-provided claims because verifyTLSNotaryPresentation returns no
recv data, so cryptographic verification is not performed. Update the comment on
the method in GCRIdentityRoutines.ts to state the current behavior: that
verifyTLSNotaryPresentation does not yield cryptographically-extracted recv data
and the implementation therefore accepts client-supplied claims in the fallback
branch (where the code uses the client-provided values), and remove or soften
any absolute statements like "never trusting client-provided claims" and
"performs cryptographic verification."
- Around line 1267-1332: The code block that depends on verified.recv is dead
because verifyTLSNotaryPresentation never returns recv; update the TLSN
verification flow in GCRIdentityRoutines.verify... to avoid implying proof
verification: add a top-of-function TODO comment stating "identity claims are
currently unverified — WASM recv path disabled" and either remove/disable the
entire recv-based branch (the if (verified.recv) block that calls
parseHttpResponse, extractUser, and performs username/userId checks) or gate it
explicitly behind a clearly named feature flag (e.g., ENABLE_WASM_RECV) so it
cannot run accidentally; also update the log messages (the else branch and any
success logs) to make it explicit they are trusting client claims, and keep
references to verifyTLSNotaryPresentation, verified.recv, parseHttpResponse,
extractUser, and TLSNIdentityContext to locate the code.
- Around line 1422-1449: The applyTLSNIdentityRemove function currently calls
ensureGCRForUser which can create a new GCR record; change it to load the record
with gcrMainRepository.findOneBy({ account: editOperation.account }) (same
pattern as applyXmIdentityRemove / applyNomisIdentityRemove), and if no record
is found return { success: false, message: "Account not found" } instead of
proceeding; keep the rest of the logic that accesses accountGCR.identities.web2
the same so you only avoid creating empty GCRs when removing an identity.
- Around line 1180-1212: The destructured fields proofString, username, and
userId (from editOperation.data.data) are not validated and can be undefined;
add explicit checks in the GCRIdentityRoutines method after parsing proof
(and/or immediately after destructuring) to ensure username and userId are
present and non-empty (and that proof was successfully parsed), returning a
failure object with a clear message (e.g., "Missing required field: username" or
"Missing required field: userId") if they are missing; keep existing context
validation (this.TLSN_EXPECTED_ENDPOINTS[context]) but perform these new
validations before any use of username/userId (e.g., before logging or storing
at later lines) to prevent downstream errors.
- Around line 1396-1410: The TLSN branch calling IncentiveManager.telegramLinked
is missing the attestation argument; update the call in GCRIdentityRoutines
(where isFirstConnection is used) to pass data.proof as the fourth parameter so
the call becomes IncentiveManager.telegramLinked(editOperation.account,
String(userId), referralCode, data.proof), matching the Web2 path and allowing
awardTelegramPoints to inspect attestation.payload.group_membership.

In `@src/libs/omniprotocol/protocol/handlers/gcr.ts`:
- Around line 100-107: Update the stale doc comment that enumerates allowed
context types to include "nomis" and "tlsn" and add a short TODO noting the
temporary nature of the `as any` cast; specifically, update the comment near the
use of gcrIdentityRoutines.apply to list "xm, web2, pqc, ud, nomis, tlsn" and
add a note that the editOperation is currently cast to any due to a missing
"tlsn" union member in the SDK's GCREdit type; keep the `as any` for now but add
a TODO referencing `@kynesyslabs/demosdk` and instruct to remove the cast and
restore proper typing of editOperation to GCREdit once the SDK is updated.

In `@src/libs/tlsnotary/index.ts`:
- Around line 1-6: Update the module doc comment to remove the incorrect "using
WASM" claim and clearly state that this TLSNotary Verification Module performs
server-side, structure-only verification without WASM support; reference the
verifier implementation (verifier.ts) and its structure-only verification mode
so readers know where the non-WASM logic lives and that WASM is intentionally
unsupported.

In `@src/libs/tlsnotary/verifier.ts`:
- Around line 264-277: The extractUser logic in extractUser (case "telegram")
currently returns an empty string when both user.username and user.first_name
are missing, which can silently match in applyTLSNIdentityAdd's username
comparison; update extractUser to treat a missing username as an extraction
failure by checking that either user.username or user.first_name exists before
returning, and if neither exists log a clear warning/error (include context like
"Telegram response missing username/first_name") and return null instead of
username:""; ensure callers like applyTLSNIdentityAdd rely on null to indicate
extraction failure.
- Around line 301-335: The function verifyTLSNProof currently returns
caller-supplied username/userId as extractedUsername/extractedUserId which is
misleading; update verifyTLSNProof to either (A) rename the return fields to
claimedUsername and claimedUserId and adjust callers (e.g.,
handleIdentityRequest.ts handling of tlsn_identity_assign) to reflect they are
unverified claims, or (B) implement proof-derived extraction by parsing the
verified proof inside verifyTLSNProof (similar to applyTLSNIdentityAdd) and
compare those extracted values with the supplied username/userId, returning the
extracted values only when they match; choose one approach and update all uses
of extractedUsername/extractedUserId accordingly to avoid implying proof-derived
trust.
🧹 Nitpick comments (4)
src/libs/tlsnotary/verifier.ts (2)

138-152: Consider adding an upper bound on data length before running the regex test.

/^[0-9a-fA-F]+$/.test(presentationJSON.data) will scan the entire string. If data is extremely large (megabytes of hex), this could be expensive. A length ceiling before the regex would be a simple safeguard.

+        // Upper bound check to prevent processing excessively large payloads
+        if (presentationJSON.data.length > 10_000_000) {
+            return {
+                success: false,
+                error: "Invalid presentation: 'data' field exceeds maximum allowed size",
+            }
+        }
+
         // Validate data is hex-encoded (basic check)
         if (!/^[0-9a-fA-F]+$/.test(presentationJSON.data)) {

82-95: initTLSNotaryVerifier and isVerifierInitialized are no-ops — document that clearly or remove the initialization ceremony.

These functions exist "for API compatibility" but serve no purpose. Any caller checking isVerifierInitialized() will always get true, which masks the fact that no real verification infrastructure is running. If these are meant to be a future extension point, the doc comment on isVerifierInitialized should warn that it does not indicate cryptographic readiness.

src/libs/network/routines/transactions/handleIdentityRequest.ts (1)

102-104: Verify the shape of payload.payload matches TLSNIdentityPayload at runtime.

The cast payload.payload as TLSNIdentityPayload assumes the payload contains context, proof, username, and userId fields. If a malformed request omits any of these, verifyTLSNProof will proceed with undefined values — the context validation on line 310 of verifier.ts would catch an invalid context, but undefined proof would still pass the typeof proof !== "object" check (since typeof undefined === "undefined"), leading to a structure validation failure. This works incidentally but is fragile.

Consider adding explicit field presence checks before the cast, consistent with how other cases validate their payloads.

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts (1)

1350-1359: Storing the full TLSNotary proof object in the JSONB column could significantly bloat storage.

TLSNotary proofs can contain large hex-encoded data fields. Storing the entire proof per identity in the web2 JSONB column could degrade query performance over time. Consider storing only the proofHash and persisting the full proof in a separate table or blob store if re-verification is needed.

Comment on lines +1165 to +1173
/**
* Add an identity via TLSNotary proof verification.
*
* This method performs cryptographic verification of the TLSNotary proof,
* extracts the proven data, and compares it with the claimed values.
* Only stores the identity if the proof is valid and claims match.
*
* Security: Data is extracted directly from the cryptographic proof,
* never trusting client-provided claims without verification.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Doc comment claims cryptographic verification and "never trusting client-provided claims" — both are currently false.

The JSDoc states the method "performs cryptographic verification" and "Data is extracted directly from the cryptographic proof, never trusting client-provided claims without verification." In reality, since verifyTLSNotaryPresentation returns no recv data, the code always falls to the else branch (line 1325) which explicitly trusts client claims. This doc will mislead any reviewer or auditor.

Update the doc to accurately reflect the current trust model.

🤖 Prompt for AI Agents
In `@src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts` around lines
1165 - 1173, The JSDoc for the "Add an identity via TLSNotary proof
verification" routine is inaccurate: the code path currently falls back to
trusting client-provided claims because verifyTLSNotaryPresentation returns no
recv data, so cryptographic verification is not performed. Update the comment on
the method in GCRIdentityRoutines.ts to state the current behavior: that
verifyTLSNotaryPresentation does not yield cryptographically-extracted recv data
and the implementation therefore accepts client-supplied claims in the fallback
branch (where the code uses the client-provided values), and remove or soften
any absolute statements like "never trusting client-provided claims" and
"performs cryptographic verification."

Comment on lines +1180 to +1212
// Extract context from editOperation.data (top level)
const { context } = editOperation.data
// Extract nested data fields (proof, username, userId are inside data.data)
const {
proof: proofString,
username,
userId,
} = editOperation.data.data || {}
// referralCode is at the editOperation level
const referralCode = editOperation.referralCode

// Parse the proof JSON string back to object
let proof: any
try {
proof =
typeof proofString === "string"
? JSON.parse(proofString)
: proofString
} catch (e) {
return {
success: false,
message: "Invalid proof: failed to parse proof JSON string",
}
}

// 1. Validate context is supported
const expected = this.TLSN_EXPECTED_ENDPOINTS[context]
if (!expected) {
return {
success: false,
message: `Unsupported TLSN context: ${context}`,
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing validation on destructured fields from editOperation.data.data.

Line 1187 uses editOperation.data.data || {}, so if data.data is undefined, all destructured fields (proofString, username, userId) become undefined. The method continues to line 1206 where context (from editOperation.data) is checked, but username and userId are never validated before being used (e.g., logged at line 1232, stored at line 1353).

Add early validation for required fields:

Proposed fix
         const {
             proof: proofString,
             username,
             userId,
         } = editOperation.data.data || {}
         // referralCode is at the editOperation level
         const referralCode = editOperation.referralCode
 
+        if (!username || userId === undefined || userId === null) {
+            return {
+                success: false,
+                message: "Invalid payload: missing username or userId",
+            }
+        }
+
         // Parse the proof JSON string back to object
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Extract context from editOperation.data (top level)
const { context } = editOperation.data
// Extract nested data fields (proof, username, userId are inside data.data)
const {
proof: proofString,
username,
userId,
} = editOperation.data.data || {}
// referralCode is at the editOperation level
const referralCode = editOperation.referralCode
// Parse the proof JSON string back to object
let proof: any
try {
proof =
typeof proofString === "string"
? JSON.parse(proofString)
: proofString
} catch (e) {
return {
success: false,
message: "Invalid proof: failed to parse proof JSON string",
}
}
// 1. Validate context is supported
const expected = this.TLSN_EXPECTED_ENDPOINTS[context]
if (!expected) {
return {
success: false,
message: `Unsupported TLSN context: ${context}`,
}
}
// Extract context from editOperation.data (top level)
const { context } = editOperation.data
// Extract nested data fields (proof, username, userId are inside data.data)
const {
proof: proofString,
username,
userId,
} = editOperation.data.data || {}
// referralCode is at the editOperation level
const referralCode = editOperation.referralCode
if (!username || userId === undefined || userId === null) {
return {
success: false,
message: "Invalid payload: missing username or userId",
}
}
// Parse the proof JSON string back to object
let proof: any
try {
proof =
typeof proofString === "string"
? JSON.parse(proofString)
: proofString
} catch (e) {
return {
success: false,
message: "Invalid proof: failed to parse proof JSON string",
}
}
// 1. Validate context is supported
const expected = this.TLSN_EXPECTED_ENDPOINTS[context]
if (!expected) {
return {
success: false,
message: `Unsupported TLSN context: ${context}`,
}
}
🤖 Prompt for AI Agents
In `@src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts` around lines
1180 - 1212, The destructured fields proofString, username, and userId (from
editOperation.data.data) are not validated and can be undefined; add explicit
checks in the GCRIdentityRoutines method after parsing proof (and/or immediately
after destructuring) to ensure username and userId are present and non-empty
(and that proof was successfully parsed), returning a failure object with a
clear message (e.g., "Missing required field: username" or "Missing required
field: userId") if they are missing; keep existing context validation
(this.TLSN_EXPECTED_ENDPOINTS[context]) but perform these new validations before
any use of username/userId (e.g., before logging or storing at later lines) to
prevent downstream errors.

Comment on lines +1267 to +1332
// 5. Parse HTTP response and extract user data (if WASM provided recv data)
let extractedUser: { username: string; userId: string } | null = null

// 5. Parse HTTP response and extract user data
// if (!verified.recv) {
// return {
// success: false,
// message: "No response data in proof",
// }
// }

if (verified.recv) {
const httpResponse = parseHttpResponse(verified.recv)
if (!httpResponse) {
return {
success: false,
message: "Failed to parse HTTP response from proof",
}
}

// 6. Extract user data based on context
extractedUser = extractUser(
context as TLSNIdentityContext,
httpResponse.body,
)

if (!extractedUser) {
return {
success: false,
message: `Failed to extract user data from ${context} response`,
}
}

// 7. CRITICAL SECURITY CHECK: Compare claimed vs extracted values
if (extractedUser.username !== username) {
log.warn(
`[TLSN Identity] Username mismatch: claimed "${username}", proof contains "${extractedUser.username}"`,
)
return {
success: false,
message: `Username mismatch: claimed "${username}", proof contains "${extractedUser.username}"`,
}
}

if (extractedUser.userId !== String(userId)) {
log.warn(
`[TLSN Identity] UserId mismatch: claimed "${userId}", proof contains "${extractedUser.userId}"`,
)
return {
success: false,
message: `UserId mismatch: claimed "${userId}", proof contains "${extractedUser.userId}"`,
}
}

log.info(
// `[TLSN Identity] Proof verified successfully for ${context}: ${username} (${userId})`,
`[TLSN Identity] Proof verified with WASM for ${context}: ${username} (${userId})`,
)
} else {
// WASM verification disabled - trust claimed data with warning
// NOTE: This is less secure but allows operation until WASM works in Node.js
log.warn(
`[TLSN Identity] WASM disabled - trusting claimed data for ${context}: ${username} (${userId})`,
)
extractedUser = { username, userId: String(userId) }
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

The recv-based extraction path is dead code; the else branch always executes.

verifyTLSNotaryPresentation never returns recv in its result (see verifier.ts line 156-160 — only success, time, verifyingKey are returned). So the if (verified.recv) block (lines 1278-1324) — including the critical security checks — never executes. The else branch (lines 1325-1332) always runs, unconditionally trusting client claims.

The commented-out block (lines 1271-1276) reinforces that this was recognized but deferred. At minimum, add a clear // TODO at the top of the method noting that all identity claims are currently unverified, and remove or gate the dead code to avoid giving a false impression of security.

🤖 Prompt for AI Agents
In `@src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts` around lines
1267 - 1332, The code block that depends on verified.recv is dead because
verifyTLSNotaryPresentation never returns recv; update the TLSN verification
flow in GCRIdentityRoutines.verify... to avoid implying proof verification: add
a top-of-function TODO comment stating "identity claims are currently unverified
— WASM recv path disabled" and either remove/disable the entire recv-based
branch (the if (verified.recv) block that calls parseHttpResponse, extractUser,
and performs username/userId checks) or gate it explicitly behind a clearly
named feature flag (e.g., ENABLE_WASM_RECV) so it cannot run accidentally; also
update the log messages (the else branch and any success logs) to make it
explicit they are trusting client claims, and keep references to
verifyTLSNotaryPresentation, verified.recv, parseHttpResponse, extractUser, and
TLSNIdentityContext to locate the code.

Comment on lines +1396 to +1410
} else if (context === "telegram") {
const isFirst = await this.isFirstConnection(
"telegram",
{ userId: String(userId) },
gcrMainRepository,
editOperation.account,
)

if (isFirst) {
await IncentiveManager.telegramLinked(
editOperation.account,
String(userId),
referralCode,
)
}
Copy link
Contributor

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, find the telegramLinked function definition to understand its signature
rg -n 'telegramLinked\s*\(' --type=ts -A5 | head -100

Repository: kynesyslabs/node

Length of output: 1948


🏁 Script executed:

# Look at the specific lines mentioned in the review
sed -n '322,327p' src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts
sed -n '1396,1410p' src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts

Repository: kynesyslabs/node

Length of output: 916


🏁 Script executed:

# Find the file containing IncentiveManager to locate telegramLinked
fd -t f -e ts -e js | xargs rg -l 'telegramLinked' | grep -i incentive

Repository: kynesyslabs/node

Length of output: 116


🏁 Script executed:

# Get the full implementation of telegramLinked method
sed -n '93,150p' src/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts

Repository: kynesyslabs/node

Length of output: 1699


🏁 Script executed:

# Also search for references to attestation or group_membership in IncentiveManager
rg -n 'attestation|group_membership' src/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts -A2

Repository: kynesyslabs/node

Length of output: 278


🏁 Script executed:

# Find awardTelegramPoints method and check if it uses attestation
rg -n 'awardTelegramPoints' --type=ts -A20

Repository: kynesyslabs/node

Length of output: 3610


🏁 Script executed:

# Also check if group_membership is referenced anywhere
rg -n 'group_membership' --type=ts -B2 -A2

Repository: kynesyslabs/node

Length of output: 1463


🏁 Script executed:

# Check what happens when isGroupMember is false
sed -n '880,920p' src/features/incentive/PointSystem.ts

Repository: kynesyslabs/node

Length of output: 1604


🏁 Script executed:

# Check what variables are available in the TLSN path context around line 1396-1410
sed -n '1370,1420p' src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts

Repository: kynesyslabs/node

Length of output: 1755


🏁 Script executed:

# Also check if there's a proof/attestation variable available in the TLSN context
sed -n '1360,1430p' src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts | head -100

Repository: kynesyslabs/node

Length of output: 2361


Pass data.proof (TelegramSignedAttestation) as the fourth argument to IncentiveManager.telegramLinked.

The TLSN path omits the attestation parameter that the Web2 path (line 326) passes as data.proof. The awardTelegramPoints method checks attestation?.payload?.group_membership === true to determine eligibility for incentive points. Without it, the check evaluates to false and users receive 0 points with the message "you must join the required group to earn points"—even though points should be awarded on successful linking for group members. Pass data.proof as the fourth argument to match the Web2 path behavior.

🤖 Prompt for AI Agents
In `@src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts` around lines
1396 - 1410, The TLSN branch calling IncentiveManager.telegramLinked is missing
the attestation argument; update the call in GCRIdentityRoutines (where
isFirstConnection is used) to pass data.proof as the fourth parameter so the
call becomes IncentiveManager.telegramLinked(editOperation.account,
String(userId), referralCode, data.proof), matching the Web2 path and allowing
awardTelegramPoints to inspect attestation.payload.group_membership.

Comment on lines +1422 to +1449
static async applyTLSNIdentityRemove(
editOperation: any,
gcrMainRepository: Repository<GCRMain>,
simulate: boolean,
): Promise<GCRResult> {
const { context, username } = editOperation.data

if (!context || !username) {
return {
success: false,
message: "Invalid payload: missing context or username",
}
}

const accountGCR = await ensureGCRForUser(editOperation.account)

accountGCR.identities.web2 = accountGCR.identities.web2 || {}
accountGCR.identities.web2[context] =
accountGCR.identities.web2[context] || []

// Find the identity to remove
const identity = accountGCR.identities.web2[context].find(
(id: Web2GCRData["data"]) => id.username === username,
)

if (!identity) {
return { success: false, message: "Identity not found" }
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

applyTLSNIdentityRemove uses ensureGCRForUser instead of findOneBy — may create empty GCR records on remove.

Other remove methods (e.g., applyXmIdentityRemove at line 142, applyNomisIdentityRemove at line 1100) use gcrMainRepository.findOneBy(...) and return "Account not found" when the account doesn't exist. This method uses ensureGCRForUser, which creates a new GCR record if one doesn't exist, then proceeds to find no matching identity. This is wasteful and inconsistent.

Proposed fix
     static async applyTLSNIdentityRemove(
         editOperation: any,
         gcrMainRepository: Repository<GCRMain>,
         simulate: boolean,
     ): Promise<GCRResult> {
         const { context, username } = editOperation.data
 
         if (!context || !username) {
             return {
                 success: false,
                 message: "Invalid payload: missing context or username",
             }
         }
 
-        const accountGCR = await ensureGCRForUser(editOperation.account)
+        const accountGCR = await gcrMainRepository.findOneBy({
+            pubkey: editOperation.account,
+        })
+
+        if (!accountGCR) {
+            return { success: false, message: "Account not found" }
+        }
 
         accountGCR.identities.web2 = accountGCR.identities.web2 || {}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static async applyTLSNIdentityRemove(
editOperation: any,
gcrMainRepository: Repository<GCRMain>,
simulate: boolean,
): Promise<GCRResult> {
const { context, username } = editOperation.data
if (!context || !username) {
return {
success: false,
message: "Invalid payload: missing context or username",
}
}
const accountGCR = await ensureGCRForUser(editOperation.account)
accountGCR.identities.web2 = accountGCR.identities.web2 || {}
accountGCR.identities.web2[context] =
accountGCR.identities.web2[context] || []
// Find the identity to remove
const identity = accountGCR.identities.web2[context].find(
(id: Web2GCRData["data"]) => id.username === username,
)
if (!identity) {
return { success: false, message: "Identity not found" }
}
static async applyTLSNIdentityRemove(
editOperation: any,
gcrMainRepository: Repository<GCRMain>,
simulate: boolean,
): Promise<GCRResult> {
const { context, username } = editOperation.data
if (!context || !username) {
return {
success: false,
message: "Invalid payload: missing context or username",
}
}
const accountGCR = await gcrMainRepository.findOneBy({
pubkey: editOperation.account,
})
if (!accountGCR) {
return { success: false, message: "Account not found" }
}
accountGCR.identities.web2 = accountGCR.identities.web2 || {}
accountGCR.identities.web2[context] =
accountGCR.identities.web2[context] || []
// Find the identity to remove
const identity = accountGCR.identities.web2[context].find(
(id: Web2GCRData["data"]) => id.username === username,
)
if (!identity) {
return { success: false, message: "Identity not found" }
}
🤖 Prompt for AI Agents
In `@src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts` around lines
1422 - 1449, The applyTLSNIdentityRemove function currently calls
ensureGCRForUser which can create a new GCR record; change it to load the record
with gcrMainRepository.findOneBy({ account: editOperation.account }) (same
pattern as applyXmIdentityRemove / applyNomisIdentityRemove), and if no record
is found return { success: false, message: "Account not found" } instead of
proceeding; keep the rest of the logic that accesses accountGCR.identities.web2
the same so you only avoid creating empty GCRs when removing an identity.

Comment on lines 100 to 107
// Apply the identity operation (simulate = false for actual execution)
// Type assertion needed: local IdentityAssignRequest includes "tlsn" context
// but the GCREdit type from SDK package may not have it yet
const result = await gcrIdentityRoutines.apply(
editOperation,
editOperation as any,
gcrMainRepository,
false, // simulate = false (actually apply changes)
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate the file and check its size
wc -l src/libs/omniprotocol/protocol/handlers/gcr.ts

Repository: kynesyslabs/node

Length of output: 110


🏁 Script executed:

# Read the relevant sections of the file
sed -n '40,60p' src/libs/omniprotocol/protocol/handlers/gcr.ts

Repository: kynesyslabs/node

Length of output: 834


🏁 Script executed:

# Read the section with the type assertion
sed -n '95,115p' src/libs/omniprotocol/protocol/handlers/gcr.ts

Repository: kynesyslabs/node

Length of output: 993


🏁 Script executed:

# Check package.json for current SDK version
cat package.json | grep -A 2 -B 2 "demosdk"

Repository: kynesyslabs/node

Length of output: 656


🌐 Web query:

@kynesyslabs/demosdk npm package latest version GCREdit type

💡 Result:

  • Latest npm version of @kynesyslabs/demosdk: 2.3.22 (shown as the current version on npm). [1]
  • GCREdit type: I could not find any published API-reference entry or docs page mentioning a GCREdit type for @kynesyslabs/demosdk. [2]

To confirm whether GCREdit exists in the installed typings (and whether it’s exported), search your local install:

npm i `@kynesyslabs/demosdk`@latest
grep -R "GCREdit" node_modules/@kynesyslabs/demosdk -n

Sources: [1] [2]


🏁 Script executed:

# Search for GCREdit type in node_modules
find node_modules/@kynesyslabs/demosdk -name "*.d.ts" 2>/dev/null | head -20

Repository: kynesyslabs/node

Length of output: 42


🏁 Script executed:

# Search for GCREdit in the SDK
grep -r "GCREdit" node_modules/@kynesyslabs/demosdk 2>/dev/null | head -10

Repository: kynesyslabs/node

Length of output: 42


🏁 Script executed:

# Also check if we can find the actual IdentityAssignRequest type in the codebase
rg "IdentityAssignRequest" src/ -A 5 -B 2

Repository: kynesyslabs/node

Length of output: 2041


🏁 Script executed:

# Check what contexts are validated in the code (around line 74-75 mentioned in scratchpad)
sed -n '70,80p' src/libs/omniprotocol/protocol/handlers/gcr.ts

Repository: kynesyslabs/node

Length of output: 635


Update stale doc comment to include new context types, and track as any cast as tech debt.

The doc comment on line 49 lists only "xm, web2, pqc, ud" but should include the newly added "nomis" and "tlsn" contexts. The as any cast on line 104 is a pragmatic workaround for SDK type misalignment—once @kynesyslabs/demosdk is updated to include "tlsn" in GCREdit, remove this assertion and update the comment accordingly.

🤖 Prompt for AI Agents
In `@src/libs/omniprotocol/protocol/handlers/gcr.ts` around lines 100 - 107,
Update the stale doc comment that enumerates allowed context types to include
"nomis" and "tlsn" and add a short TODO noting the temporary nature of the `as
any` cast; specifically, update the comment near the use of
gcrIdentityRoutines.apply to list "xm, web2, pqc, ud, nomis, tlsn" and add a
note that the editOperation is currently cast to any due to a missing "tlsn"
union member in the SDK's GCREdit type; keep the `as any` for now but add a TODO
referencing `@kynesyslabs/demosdk` and instruct to remove the cast and restore
proper typing of editOperation to GCREdit once the SDK is updated.

Comment on lines +1 to +6
/**
* TLSNotary Verification Module
*
* Provides server-side verification of TLSNotary proofs using WASM.
* Used by GCR identity routines to verify TLSN-based identity claims.
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Module doc comment is misleading — says "using WASM" but this module explicitly does not use WASM.

Line 4 states "Provides server-side verification of TLSNotary proofs using WASM" but the underlying verifier.ts operates in "structure-only verification mode" with WASM explicitly noted as unsupported. This will confuse future readers.

 /**
  * TLSNotary Verification Module
  *
- * Provides server-side verification of TLSNotary proofs using WASM.
+ * Provides server-side structural validation of TLSNotary proofs.
+ * NOTE: Full cryptographic verification via WASM is not yet supported in Node.js.
  * Used by GCR identity routines to verify TLSN-based identity claims.
  */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* TLSNotary Verification Module
*
* Provides server-side verification of TLSNotary proofs using WASM.
* Used by GCR identity routines to verify TLSN-based identity claims.
*/
/**
* TLSNotary Verification Module
*
* Provides server-side structural validation of TLSNotary proofs.
* NOTE: Full cryptographic verification via WASM is not yet supported in Node.js.
* Used by GCR identity routines to verify TLSN-based identity claims.
*/
🤖 Prompt for AI Agents
In `@src/libs/tlsnotary/index.ts` around lines 1 - 6, Update the module doc
comment to remove the incorrect "using WASM" claim and clearly state that this
TLSNotary Verification Module performs server-side, structure-only verification
without WASM support; reference the verifier implementation (verifier.ts) and
its structure-only verification mode so readers know where the non-WASM logic
lives and that WASM is intentionally unsupported.

Comment on lines +264 to +277
case "telegram": {
// Handle response format: { user: { id, username, first_name, ... } }
const user = json.user || json
if (user.id !== undefined) {
return {
username: user.username || user.first_name || "",
userId: String(user.id),
}
}
log.warn(
"[TLSNotary Verifier] Telegram response missing 'id' field",
)
return null
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Telegram extractUser can return an empty username, which may cause downstream issues.

When neither user.username nor user.first_name is present, the returned username is "". This could silently pass validation in applyTLSNIdentityAdd (line 1301 compares extractedUser.username !== username) and cause identity lookups by username to match unrelated empty-username entries.

Consider treating a missing username as an extraction failure, or at minimum logging this edge case more prominently.

🤖 Prompt for AI Agents
In `@src/libs/tlsnotary/verifier.ts` around lines 264 - 277, The extractUser logic
in extractUser (case "telegram") currently returns an empty string when both
user.username and user.first_name are missing, which can silently match in
applyTLSNIdentityAdd's username comparison; update extractUser to treat a
missing username as an extraction failure by checking that either user.username
or user.first_name exists before returning, and if neither exists log a clear
warning/error (include context like "Telegram response missing
username/first_name") and return null instead of username:""; ensure callers
like applyTLSNIdentityAdd rely on null to indicate extraction failure.

Comment on lines +301 to +335
export async function verifyTLSNProof(payload: TLSNIdentityPayload): Promise<{
success: boolean
message: string
extractedUsername?: string
extractedUserId?: string
}> {
const { context, proof, username, userId } = payload

// Validate context
if (!["github", "discord", "telegram"].includes(context)) {
return {
success: false,
message: `Unsupported TLSN context: ${context}`,
}
}

// Verify the proof structure
const verified = await verifyTLSNotaryPresentation(proof)
if (!verified.success) {
return {
success: false,
message: `Proof verification failed: ${verified.error}`,
}
}

log.info(
`[TLSNotary Verifier] ${context} proof structure validated for: username=${username}, userId=${userId}`,
)

return {
success: true,
message: "Proof structure verified",
extractedUsername: username,
extractedUserId: userId,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Security: verifyTLSNProof echoes back client-supplied claims as "extracted" values — misleading API contract.

The function names return fields extractedUsername and extractedUserId, implying they were derived from the proof. In reality, the function only validates proof structure and returns the caller-supplied username/userId verbatim. Any consumer of this API may incorrectly assume the values are proof-derived, creating a false sense of security.

This function is called in handleIdentityRequest.ts for tlsn_identity_assign, meaning the network-layer verification effectively rubber-stamps client claims.

Consider either:

  1. Renaming the fields to claimedUsername/claimedUserId to make the trust model explicit, or
  2. Actually extracting data from the proof (when possible) and comparing it with claims here, similar to what applyTLSNIdentityAdd attempts.
Suggested field rename to clarify trust model
 export async function verifyTLSNProof(payload: TLSNIdentityPayload): Promise<{
     success: boolean
     message: string
-    extractedUsername?: string
-    extractedUserId?: string
+    claimedUsername?: string
+    claimedUserId?: string
 }> {
     ...
     return {
         success: true,
         message: "Proof structure verified",
-        extractedUsername: username,
-        extractedUserId: userId,
+        claimedUsername: username,
+        claimedUserId: userId,
     }
 }
🤖 Prompt for AI Agents
In `@src/libs/tlsnotary/verifier.ts` around lines 301 - 335, The function
verifyTLSNProof currently returns caller-supplied username/userId as
extractedUsername/extractedUserId which is misleading; update verifyTLSNProof to
either (A) rename the return fields to claimedUsername and claimedUserId and
adjust callers (e.g., handleIdentityRequest.ts handling of tlsn_identity_assign)
to reflect they are unverified claims, or (B) implement proof-derived extraction
by parsing the verified proof inside verifyTLSNProof (similar to
applyTLSNIdentityAdd) and compare those extracted values with the supplied
username/userId, returning the extracted values only when they match; choose one
approach and update all uses of extractedUsername/extractedUserId accordingly to
avoid implying proof-derived trust.

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.

1 participant