Skip to content

Add anonymous mode for unauthenticated requests - #15

Merged
jstjoe merged 7 commits into
mainfrom
jstjoe/anon-mode
Feb 4, 2026
Merged

jstjoe merged 7 commits into
mainfrom
jstjoe/anon-mode

Conversation

@jstjoe

@jstjoe jstjoe commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Introduce an anonymous mode that allows limited functionality without authentication. This mode enables the use of the 'dehydrate' tool and includes rate limiting for requests. Documentation updates provide guidance on using anonymous mode and its limitations.

@vercel

vercel Bot commented Feb 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sky-mcp-streamable Ready Ready Preview, Comment Feb 4, 2026 9:03pm

Request Review

@jstjoe
jstjoe requested a review from Copilot February 3, 2026 20:55
@github-actions

github-actions Bot commented Feb 3, 2026

Copy link
Copy Markdown

🔐 Gitleaks Findings: 8 issue(s) detected

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: jwt
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: stripe-access-token
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: generic-api-key
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces an anonymous mode that enables users to try the dehydrate functionality without authentication. When anonymous mode environment variables are configured and no credentials are provided, the server allows limited access with rate limiting.

Changes:

  • Added anonymous mode support with environment configuration for unauthenticated requests
  • Implemented IP-based rate limiting for anonymous requests using in-memory storage
  • Modified token generation to use entity counters in anonymous mode instead of persistent vault tokens

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/lib/middleware/authenticateBearer.ts Adds anonymous mode detection when credentials are absent
src/lib/middleware/rateLimiter.ts Implements rate limiting middleware for anonymous requests
src/server.ts Integrates anonymous mode throughout request handling and tool execution
tests/unit/middleware/authenticateBearer.test.ts Adds comprehensive tests for anonymous mode detection and credential handling
tests/unit/middleware/rateLimiter.test.ts Adds comprehensive tests for rate limiter functionality
README.md Documents anonymous mode usage, limitations, and configuration
CLAUDE.md Documents anonymous mode behavior and token format differences
.env.sample Adds anonymous mode environment variable templates

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/middleware/rateLimiter.ts Outdated
}

// Set rate limit headers
const remaining = Math.max(0, config.maxRequests - entry.count);

Copilot AI Feb 3, 2026

Copy link

Choose a reason for hiding this comment

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

The remaining count calculation is incorrect when the request has already exceeded the limit. For requests beyond maxRequests, this will show remaining as 0, but the count has already been incremented in line 82. This means the count is incremented even when blocking the request, which could lead to incorrect rate limit tracking. Consider checking if the limit is exceeded before incrementing the count.

Copilot uses AI. Check for mistakes.
Comment thread src/server.ts Outdated
{
skyflow: skyflowInstance,
vaultId: validatedVaultId,
isAnonymousMode: req.isAnonymousMode ?? false,

Copilot AI Feb 3, 2026

Copy link

Choose a reason for hiding this comment

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

Using nullish coalescing (??) is inconsistent with the earlier code at line 620 which checks 'req.isAnonymousMode && req.anonVaultConfig'. Since isAnonymousMode is set explicitly to true or false in the authenticateBearer middleware, the nullish coalescing operator is unnecessary. Consider using 'req.isAnonymousMode || false' or just 'req.isAnonymousMode' with appropriate type handling.

Suggested change
isAnonymousMode: req.isAnonymousMode ?? false,
isAnonymousMode: req.isAnonymousMode,

Copilot uses AI. Check for mistakes.
@claude

claude Bot commented Feb 3, 2026

Copy link
Copy Markdown

Pull Request Review: Add Anonymous Mode for Unauthenticated Requests

Summary

This PR introduces an anonymous mode that allows users to try the dehydrate tool without configuring Skyflow credentials. The implementation includes rate limiting and clear documentation about limitations. Overall, this is a well-designed feature with excellent test coverage.

✅ Strengths

1. Architecture & Design

  • Clean separation of concerns: Anonymous mode detection is handled in the authentication middleware, keeping the logic centralized
  • Per-request pattern maintained: The PR correctly follows the existing per-request Skyflow instance pattern
  • AsyncLocalStorage integration: The isAnonymousMode context is properly integrated into the request context (src/server.ts:71)
  • Graceful degradation: Tools provide helpful error messages when features aren't available in anonymous mode

2. Security & Rate Limiting

  • IP-based rate limiting: Proper implementation with X-Forwarded-For header support for proxied deployments (src/lib/middleware/rateLimiter.ts:21-33)
  • Separate tracking: Anonymous requests are tracked separately from authenticated requests (src/lib/middleware/rateLimiter.ts:64-66)
  • Memory cleanup: Automatic cleanup of expired entries with unref() to prevent blocking process exit (src/lib/middleware/rateLimiter.ts:48-51)
  • No credential exposure: Anonymous mode uses server-side credentials that aren't exposed to clients

3. Test Coverage

  • Comprehensive unit tests: 320 lines of tests for rate limiter, 180+ lines for authentication middleware
  • Edge case coverage: Tests handle scenarios like multiple IPs, expired entries, different client tracking
  • Test helpers: Clean mock factories for Express req/res objects

4. Documentation

  • Excellent documentation updates: CLAUDE.md and README.md clearly explain anonymous mode
  • Usage examples: Includes curl examples and Claude Desktop configuration
  • Clear limitations: Explicitly documents what works and what doesn't in anonymous mode

🔍 Potential Issues & Suggestions

1. Rate Limiting - Off-by-One Error (Minor Bug)

Location: src/lib/middleware/rateLimiter.ts:93

The rate limit check uses > instead of >=:

if (entry.count > config.maxRequests) {

Issue: If maxRequests is 10, users can actually make 11 requests before being blocked:

  • Requests 1-10: count becomes 1-10, passes check (10 > 10 is false)
  • Request 11: count becomes 11, blocked (11 > 10 is true)

Fix: Change to >=:

if (entry.count >= config.maxRequests) {

This would properly enforce the limit at exactly 10 requests.

Test verification: The test at line 167-170 expects requests "up to limit" to succeed, but with the current implementation, it allows one extra request. The test comment says "Should allow requests up to limit" but actually allows maxRequests + 1.

2. Rate Limiting - X-RateLimit-Remaining Header Issue

Location: src/lib/middleware/rateLimiter.ts:86, 93

The X-RateLimit-Remaining header is set before checking if the limit is exceeded:

// Set rate limit headers
const remaining = Math.max(0, config.maxRequests - entry.count);
res.setHeader("X-RateLimit-Remaining", remaining);

if (entry.count > config.maxRequests) {
  // return 429
}

Issue: On the rate-limited response (429), the Remaining header will show -1 (clamped to 0), which is correct. However, due to the off-by-one issue above, the client won't see a 0 remaining count before being blocked.

3. Anonymous Mode Detection - Partial Configuration

Location: src/lib/middleware/authenticateBearer.ts:200-211

The anonymous mode check uses a simple AND condition:

if (anonApiKey && anonVaultId && anonVaultUrl) {
  // Enable anonymous mode
}

Issue: If only 1 or 2 of the 3 env vars are set, the server silently falls back to returning 401, which could be confusing during configuration.

Suggestion: Add a warning log when partial configuration is detected:

const anonApiKey = process.env.ANON_MODE_API_KEY;
const anonVaultId = process.env.ANON_MODE_VAULT_ID;
const anonVaultUrl = process.env.ANON_MODE_VAULT_URL;

const anonVarsSet = [anonApiKey, anonVaultId, anonVaultUrl].filter(Boolean).length;
if (anonVarsSet > 0 && anonVarsSet < 3) {
  console.warn(`Partial anonymous mode configuration detected (${anonVarsSet}/3 vars set). All three ANON_MODE_* env vars are required.`);
}

if (anonApiKey && anonVaultId && anonVaultUrl) {
  console.log("No credentials provided, entering anonymous mode");
  // ...
}

4. Rate Limiter Store - Memory Leak Risk

Location: src/lib/middleware/rateLimiter.ts:15, 38-45

Current implementation: In-memory Map with periodic cleanup every 60 seconds

Risk: In a high-traffic scenario with many unique IPs, the Map could grow large between cleanup cycles. With 60-second windows and 60-second cleanup, entries could persist for up to 120 seconds.

Suggestion: Consider one of these improvements:

  1. Cleanup on access: Remove expired entry when accessed (lazy cleanup)
  2. Shorter cleanup interval: Run cleanup every 30 seconds instead of 60
  3. Max size limit: Add a maximum store size with LRU eviction

Example lazy cleanup:

let entry = rateLimitStore.get(key);

if (entry && now > entry.resetTime) {
  // Expired, remove it
  rateLimitStore.delete(key);
  entry = undefined; // Treat as new
}

if (!entry) {
  // Create new entry
  entry = { count: 1, resetTime: now + config.windowMs };
  rateLimitStore.set(key, entry);
} else {
  entry.count++;
}

5. Type Safety - Missing Type Declarations

Location: src/lib/middleware/authenticateBearer.ts:449-453, src/server.ts:595-597

The Express Request interface is extended in global namespace, but the properties are marked optional:

interface Request {
  skyflowCredentials?: { token: string } | { apiKey: string };
  isAnonymousMode?: boolean;
  anonVaultConfig?: { vaultId: string; vaultUrl: string };
}

Issue: The ?? operator is used in server.ts:687 (req.isAnonymousMode ?? false), which is correct for handling undefined. However, in the middleware, these are always set (either to a value or explicitly to false).

Suggestion: Consider making isAnonymousMode required (always set by middleware):

interface Request {
  skyflowCredentials?: { token: string } | { apiKey: string };
  isAnonymousMode: boolean; // Always set by authenticateBearer middleware
  anonVaultConfig?: { vaultId: string; vaultUrl: string };
}

This would require setting isAnonymousMode = false at the start of the middleware (which is already done at line 218).

6. Documentation - Deployment Considerations

Location: CLAUDE.md:140-167, src/lib/middleware/rateLimiter.ts:13-14

Missing info: The documentation doesn't mention that the current rate limiter is single-instance only.

Suggestion: Add a note in README.md:

### Anonymous Mode Limitations

...existing limitations...

**Note for Production Deployments**: The current rate limiter uses in-memory storage and is designed for single-instance deployments. For multi-instance deployments (load-balanced or serverless), consider implementing Redis-based rate limiting to share state across instances.

7. Minor - Environment Variable Parsing

Location: src/lib/middleware/rateLimiter.ts:113-116

Using parseInt with default fallback:

maxRequests: parseInt(process.env.ANON_MODE_RATE_LIMIT_REQUESTS || "10", 10),
windowMs: parseInt(process.env.ANON_MODE_RATE_LIMIT_WINDOW_MS || "60000", 10),

Edge case: If someone sets the env var to a non-numeric string, parseInt returns NaN, which could break rate limiting.

Suggestion: Add validation:

export function getAnonymousRateLimitConfig(): RateLimiterConfig {
  const maxRequests = parseInt(process.env.ANON_MODE_RATE_LIMIT_REQUESTS || "10", 10);
  const windowMs = parseInt(process.env.ANON_MODE_RATE_LIMIT_WINDOW_MS || "60000", 10);
  
  if (isNaN(maxRequests) || maxRequests < 1) {
    console.warn(`Invalid ANON_MODE_RATE_LIMIT_REQUESTS, using default: 10`);
    return { maxRequests: 10, windowMs: isNaN(windowMs) ? 60000 : windowMs };
  }
  
  if (isNaN(windowMs) || windowMs < 1000) {
    console.warn(`Invalid ANON_MODE_RATE_LIMIT_WINDOW_MS, using default: 60000`);
    return { maxRequests, windowMs: 60000 };
  }
  
  return { maxRequests, windowMs };
}

🎯 Performance Considerations

  1. Rate limiter performance: Map lookups are O(1), so performance is good
  2. Memory usage: Reasonable for single-instance deployments
  3. No additional latency: Anonymous mode check happens in existing middleware chain

🔒 Security Assessment

  1. ✅ No credential exposure to clients
  2. ✅ Proper rate limiting prevents abuse
  3. ✅ Token format difference prevents confusion (ENTITY_UNIQUE_COUNTER vs VAULT_TOKEN)
  4. ✅ Clear messaging about data not being persisted
  5. ✅ Separate tracking for anonymous vs authenticated requests

📊 Test Coverage Assessment

  • ✅ Unit tests for rate limiter (320 lines)
  • ✅ Unit tests for authentication middleware (180 lines added)
  • ✅ Edge cases covered (multiple IPs, expired entries, partial config)
  • ⚠️ Missing integration tests for the full anonymous mode flow
  • ⚠️ Test at line 167-170 might not catch the off-by-one bug

Suggestion: Add an integration test that verifies the exact limit:

it("should block exactly at maxRequests + 1", () => {
  const config = { maxRequests: 3, windowMs: 60000 };
  const rateLimiter = createAnonymousRateLimiter(config);
  
  const results: number[] = [];
  for (let i = 0; i < 5; i++) {
    const req = createMockRequest({ isAnonymousMode: true, ip: "1.2.3.4" });
    const mockRes = createMockResponse();
    rateLimiter(req, mockRes.res as Response, vi.fn());
    results.push(mockRes.statusCode || 200);
  }
  
  expect(results).toEqual([200, 200, 200, 429, 429]);
  // Should block at request 4 (index 3), not request 5
});

✅ Recommendation

APPROVE with minor fixes

This is a well-designed and thoroughly tested feature. The anonymous mode implementation follows the existing architecture patterns and includes proper security considerations. The issues identified are minor and can be addressed in follow-up commits or a subsequent PR:

Must fix before merge:

  1. Fix off-by-one error in rate limiter (line 93)

Should fix before merge:
2. Add partial configuration warning in authenticateBearer

Nice to have (can defer):
3. Add lazy cleanup to rate limiter
4. Add environment variable validation
5. Update documentation about single-instance deployment
6. Add integration test for exact rate limit boundary

The test coverage is excellent, and the feature provides clear value to users who want to try the service before configuring credentials.


Files reviewed:

  • ✅ src/lib/middleware/rateLimiter.ts
  • ✅ src/lib/middleware/authenticateBearer.ts
  • ✅ src/server.ts
  • ✅ tests/unit/middleware/rateLimiter.test.ts
  • ✅ tests/unit/middleware/authenticateBearer.test.ts
  • ✅ CLAUDE.md
  • ✅ README.md
  • ✅ .env.sample

@github-actions

github-actions Bot commented Feb 4, 2026

Copy link
Copy Markdown

🔐 Gitleaks Findings: 8 issue(s) detected

🔸 Rule: jwt
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: generic-api-key
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: stripe-access-token
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

@github-actions

github-actions Bot commented Feb 4, 2026

Copy link
Copy Markdown

🔐 Gitleaks Findings: 8 issue(s) detected

🔸 Rule: jwt
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: stripe-access-token
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

@jstjoe

jstjoe commented Feb 4, 2026

Copy link
Copy Markdown
Contributor Author

Hey @claude can you do an updated review? I think we fixed the issues but please check for me and let me know if we should make any more changes before merging.

@claude

claude Bot commented Feb 4, 2026 •

Copy link
Copy Markdown

Claude finished @jstjoe's task in 1m 21s —— View job


Pull Request Review - Updated Analysis

Great work addressing the previous feedback! I've reviewed the updated code and here's what I found:

Todo List

  • Read rate limiter implementation to check off-by-one fix
  • Read authentication middleware for configuration warnings
  • Read server.ts for any updates
  • Check test files for coverage improvements
  • Provide comprehensive review feedback

✅ Issues Fixed

1. Off-by-One Error - FIXED ✓

Location: src/lib/middleware/rateLimiter.ts:87

The rate limiter now correctly uses >= instead of >:

if (entry.count >= config.maxRequests) {

This properly enforces the limit. With maxRequests=10, the first 10 requests are allowed, and the 11th is blocked. The logic is:

  • Requests start with count=0
  • Check happens BEFORE incrementing
  • Request is allowed if count < maxRequests
  • After allowing, increment to track usage

Perfect! The comment on line 76 clarifies the intent: "count represents 'requests made so far' (before this one)"

2. Rate Limit Headers - FIXED ✓

Location: src/lib/middleware/rateLimiter.ts:102-109

The count increment now happens AFTER the check, which means:

  • The remaining count is calculated correctly
  • On the blocking request, the remaining is set to 0 (line 90)
  • The header logic is clean and correct

3. Partial Configuration Warning - FIXED ✓

Location: src/lib/middleware/authenticateBearer.ts:206-214

Excellent addition! The middleware now warns when only some anonymous mode env vars are set:

const anonVarsSet = [anonApiKey, anonVaultId, anonVaultUrl].filter(Boolean).length;
if (anonVarsSet > 0 && anonVarsSet < 3) {
  console.warn(
    `Partial anonymous mode configuration detected (${anonVarsSet}/3 vars set). ` +
    "All three ANON_MODE_* env vars are required: ANON_MODE_API_KEY, ANON_MODE_VAULT_ID, ANON_MODE_VAULT_URL"
  );
}

This will help with debugging configuration issues.

4. Test Coverage - IMPROVED ✓

Location: tests/unit/middleware/rateLimiter.test.ts:160-185

The test at lines 160-185 now has excellent documentation showing the expected behavior:

// With maxRequests=3:
// - Request 1: allowed, remaining=2
// - Request 2: allowed, remaining=1
// - Request 3: allowed, remaining=0
// - Request 4: blocked (429), remaining=0

This matches the fixed implementation perfectly!

5. Type Safety - IMPROVED ✓

Location: src/server.ts:609

The isAnonymousMode property is now correctly typed as non-optional:

interface Request {
  skyflowCredentials?: { token: string } | { apiKey: string };
  isAnonymousMode: boolean; // Always set by authenticateBearer middleware
  anonVaultConfig?: { vaultId: string; vaultUrl: string };
}

The usage at line 689 is now clean: isAnonymousMode: req.isAnonymousMode (no ?? needed).


🔍 Remaining Minor Items

1. Environment Variable Validation (Low Priority)

Location: src/lib/middleware/rateLimiter.ts:118-122

The rate limit config parsing doesn't validate for NaN:

return {
  maxRequests: parseInt(process.env.ANON_MODE_RATE_LIMIT_REQUESTS || "10", 10),
  windowMs: parseInt(process.env.ANON_MODE_RATE_LIMIT_WINDOW_MS || "60000", 10),
};

Impact: If someone sets ANON_MODE_RATE_LIMIT_REQUESTS=abc, it would become NaN and break rate limiting.

Severity: Low - this is an environment configuration error that would be caught quickly in testing.

Optional Fix: Add validation:

export function getAnonymousRateLimitConfig(): RateLimiterConfig {
  const maxRequests = parseInt(process.env.ANON_MODE_RATE_LIMIT_REQUESTS || "10", 10);
  const windowMs = parseInt(process.env.ANON_MODE_RATE_LIMIT_WINDOW_MS || "60000", 10);
  
  if (isNaN(maxRequests) || maxRequests < 1) {
    console.warn(`Invalid ANON_MODE_RATE_LIMIT_REQUESTS, using default: 10`);
    return { maxRequests: 10, windowMs: isNaN(windowMs) ? 60000 : windowMs };
  }
  
  if (isNaN(windowMs) || windowMs < 1000) {
    console.warn(`Invalid ANON_MODE_RATE_LIMIT_WINDOW_MS, using default: 60000`);
    return { maxRequests, windowMs: 60000 };
  }
  
  return { maxRequests, windowMs };
}

2. Rate Limiter Memory Management (Enhancement)

Location: src/lib/middleware/rateLimiter.ts:72-82

The current implementation creates or resets entries every 60 seconds, which is fine. However, there's a small optimization opportunity: expired entries aren't removed immediately when accessed.

Current behavior: Entries are cleaned up every 60 seconds (line 48)
Enhancement: Could add lazy cleanup on access

Impact: Minimal - the current approach is perfectly acceptable for production use.

Example enhancement (optional):

let entry = rateLimitStore.get(key);

// Lazy cleanup: remove expired entry when accessed
if (entry && now > entry.resetTime) {
  rateLimitStore.delete(key);
  entry = undefined; // Treat as new
}

if (!entry) {
  // Create new entry
  entry = {
    count: 0,
    resetTime: now + config.windowMs,
  };
  rateLimitStore.set(key, entry);
}

📊 Code Quality Assessment

Architecture

  • ✅ Clean separation of concerns
  • ✅ Per-request pattern maintained
  • ✅ AsyncLocalStorage properly used
  • ✅ Middleware chain is well-structured

Security

  • ✅ No credential exposure
  • ✅ Rate limiting prevents abuse
  • ✅ Token format differences are clear
  • ✅ Anonymous mode restrictions are well-documented

Testing

  • ✅ Comprehensive unit tests (354 lines for rate limiter)
  • ✅ Edge cases covered
  • ✅ Clear test documentation
  • ✅ Off-by-one bug would be caught by tests

Documentation

  • ✅ CLAUDE.md updated with anonymous mode details
  • ✅ README.md includes usage examples
  • ✅ Code comments are clear and helpful
  • ✅ Environment variables documented in .env.sample

✅ Final Recommendation

APPROVE - Ready to Merge

All critical issues from the previous review have been addressed:

  1. ✅ Off-by-one error fixed
  2. ✅ Rate limit headers corrected
  3. ✅ Partial configuration warning added
  4. ✅ Test coverage improved
  5. ✅ Type safety enhanced

The two remaining items I mentioned are optional enhancements, not blockers:

  • Environment variable validation (Low priority - would be caught in testing)
  • Lazy cleanup optimization (Enhancement - current approach is production-ready)

The PR is in excellent shape and ready for production. The anonymous mode feature is well-designed, thoroughly tested, and properly documented. Great job addressing all the feedback! 🎉


@claude

claude Bot commented Feb 4, 2026

Copy link
Copy Markdown

Pull Request Review: Anonymous Mode Implementation

This PR introduces anonymous mode functionality to allow limited access without authentication. Overall, the implementation is solid and well-tested, but there are several areas that need attention before merging.


🟢 Strengths

Excellent Test Coverage

  • 534 new test lines added across authenticateBearer and rateLimiter
  • Tests cover edge cases including boundary conditions, client isolation, and header extraction
  • Good use of vitest mocking patterns

Well-Designed Architecture

  • Clean separation of concerns with dedicated middleware
  • Proper use of AsyncLocalStorage for request context
  • Clear fallback behavior when anonymous mode is configured vs not configured

Security-Conscious Implementation

  • Rate limiting properly scoped to anonymous mode only
  • Clear distinction between authenticated and anonymous token types
  • Helpful error messages guide users toward authenticated setup

Documentation

  • Comprehensive CLAUDE.md and README.md updates
  • Clear explanation of anonymous mode limitations
  • Good table comparing tool behavior across modes

🔴 Critical Issues

1. Security: IP Spoofing Vulnerability (src/lib/middleware/rateLimiter.ts:22-28)

const forwarded = req.headers["x-forwarded-for"];
if (forwarded) {
  const ips = Array.isArray(forwarded)
    ? forwarded[0]
    : forwarded.split(",")[0];
  return ips.trim();
}

Issue: The code trusts the X-Forwarded-For header without validation. Attackers can bypass rate limiting by sending arbitrary values in this header.

Recommendation:

  • Either validate that the server is behind a trusted proxy, or
  • Use the rightmost IP from X-Forwarded-For (the last proxy's IP), or
  • Only trust X-Forwarded-For when behind specific known proxies (check X-Forwarded-Host or similar)
  • Consider using Express's trust proxy setting and relying on req.ip which handles this correctly

Example fix:

// Option 1: Configure express trust proxy and use req.ip consistently
app.set('trust proxy', true); // in server.ts

// Then in getClientId:
export function getClientId(req: Request): string {
  // req.ip is already properly extracted when trust proxy is configured
  return req.ip || req.socket.remoteAddress || "unknown";
}

2. Race Condition in Cleanup (src/lib/middleware/rateLimiter.ts:38-44)

function cleanupExpiredEntries(): void {
  const now = Date.now();
  for (const [key, entry] of rateLimitStore.entries()) {
    if (now > entry.resetTime) {
      rateLimitStore.delete(key);
    }
  }
}

Issue: While JavaScript is single-threaded, the cleanup can delete entries while they're being read/modified in concurrent requests (async operations). This could cause inconsistent state.

Recommendation: This is generally fine for Node.js (single event loop), but document this limitation. For multi-instance deployments, the comment already mentions Redis, which is good.


🟡 Important Issues

3. Missing Input Validation (src/lib/middleware/rateLimiter.ts:685-687)

return {
  maxRequests: parseInt(process.env.ANON_MODE_RATE_LIMIT_REQUESTS || "10", 10),
  windowMs: parseInt(process.env.ANON_MODE_RATE_LIMIT_WINDOW_MS || "60000", 10),
};

Issue: No validation that parsed integers are positive numbers. Invalid env vars could result in NaN or negative values.

Recommendation:

const maxRequests = parseInt(process.env.ANON_MODE_RATE_LIMIT_REQUESTS || "10", 10);
const windowMs = parseInt(process.env.ANON_MODE_RATE_LIMIT_WINDOW_MS || "60000", 10);

if (isNaN(maxRequests) || maxRequests <= 0) {
  throw new Error(`Invalid ANON_MODE_RATE_LIMIT_REQUESTS: must be positive integer`);
}
if (isNaN(windowMs) || windowMs <= 0) {
  throw new Error(`Invalid ANON_MODE_RATE_LIMIT_WINDOW_MS: must be positive integer`);
}

return { maxRequests, windowMs };

4. Inconsistent Error Response Shape (src/server.ts:781-795, 806-822)

The error responses for rehydrate and dehydrate_file in anonymous mode include isError: true, but this property is not defined in the tool output schema or documented.

Recommendation: Either document this property in the response schema or handle errors through a consistent error mechanism.

5. Potential Memory Leak (src/lib/middleware/rateLimiter.ts:15)

The in-memory Map will grow unbounded if cleanup doesn't run properly or if there are many unique IPs.

Recommendation:

  • Add a max size limit with LRU eviction
  • Or add a warning in production about memory usage
  • Document monitoring recommendations

6. Console.log in Production (src/lib/middleware/authenticateBearer.ts:545, src/server.ts:861, src/lib/middleware/rateLimiter.ts:654)

Multiple console.log statements for runtime behavior.

Recommendation: Use a proper logger (winston, pino, etc.) with log levels, or at minimum wrap in environment checks.


📝 Code Quality Suggestions

7. Type Safety Enhancement

Consider adding explicit types for the extended Express Request:

declare module 'express-serve-static-core' {
  interface Request {
    skyflowCredentials?: { token: string } | { apiKey: string };
    isAnonymousMode: boolean;
    anonVaultConfig?: { vaultId: string; vaultUrl: string };
  }
}

This is more conventional than global namespace declaration.

8. Magic Numbers (src/lib/middleware/rateLimiter.ts:48)

const cleanupInterval = setInterval(cleanupExpiredEntries, 60000);

Extract to named constant: const CLEANUP_INTERVAL_MS = 60_000;

9. DRY Violation

The error messages for rehydrate (src/server.ts:783-788) and dehydrate_file (src/server.ts:810-817) contain similar "configure your Skyflow credentials" text. Extract to a shared constant or function.

10. Test Coverage Gaps

While test coverage is good, consider adding:

  • Tests for expired entry cleanup behavior
  • Tests for invalid environment variables in getAnonymousRateLimitConfig
  • Integration tests that verify the full anonymous mode flow end-to-end

⚠️ Security Considerations

  1. Rate Limit Bypass: Fix the IP spoofing issue (see Jstjoe/2025 10 09 #1) before deploying
  2. DoS via Store Exhaustion: Consider max entries in rateLimitStore (see readme updats: title, try it out section #5)
  3. Credential Exposure: Verify that ANON_MODE_API_KEY in logs doesn't leak sensitive data (currently it doesn't, but worth documenting)

🎯 Performance Considerations

  1. Cleanup Interval: Every 60 seconds is reasonable, but document that entries may live up to windowMs + 60000ms
  2. Map Iteration: cleanupExpiredEntries iterates entire Map. For large deployments, consider batch limits or sampling
  3. Single-Instance Limitation: Documentation correctly notes this won't work across multiple instances without Redis

📋 Summary

Recommendation: Request changes before merging.

Must Fix Before Merge:

Should Fix Before Merge:

Nice to Have:


💡 Overall Assessment

This is a well-architected feature with excellent test coverage and clear documentation. The separation between authenticated and anonymous modes is clean, and the fallback behavior is intuitive. However, the IP spoofing vulnerability is a blocker that must be fixed before deployment to production.

Great work on the comprehensive tests and documentation! 🎉

@claude

claude Bot commented Feb 4, 2026

Copy link
Copy Markdown

Pull Request Review: Anonymous Mode Implementation

Summary

This PR introduces an anonymous mode feature that enables limited MCP server functionality without authentication. The implementation includes rate limiting, comprehensive tests, and excellent documentation updates.

Strengths

1. Code Quality

  • Clean separation of concerns: Rate limiting and authentication logic are properly isolated
  • Well-structured TypeScript types and interfaces
  • Pure functions that are testable (extractBearerToken, looksLikeJwt)
  • Consistent error handling with helpful user messages

2. Security

  • Rate limiting by IP prevents abuse of anonymous mode
  • Proper credential precedence (Authorization header over query params)
  • No credential leakage in logs (only indicates presence)
  • Graceful degradation (only dehydrate tool works in anonymous mode)

3. Test Coverage

  • Comprehensive unit tests: 534 lines across two test files
  • Edge case handling (boundary conditions)
  • Multiple scenarios covered (authentication states, IP extraction, rate limits)
  • Helper functions for testability

4. Documentation

  • Excellent CLAUDE.md updates with clear tables
  • User-friendly README quick start guide
  • Complete environment variable documentation

Observations & Recommendations

Rate Limiter (src/lib/middleware/rateLimiter.ts)

  • Good use of unref() to prevent blocking process exit
  • Consider adding stopCleanupInterval() export for test isolation
  • Correct off-by-one handling in rate limit logic

Anonymous Mode Detection (authenticateBearer.ts:200-222)

  • Good configuration validation
  • Consider moving partial config check to startup (avoid per-request overhead)

Test Coverage

  • Missing coverage for 401 when anonymous mode not configured
  • Tests note vitest env var stubbing limitations
  • Consider integration tests for this critical path

Security Analysis

  • Rate limiting bypass: No issues
  • Credential exposure: No issues
  • DoS vectors: Rate limiting mitigates abuse, consider max store size limit
  • IP spoofing: Uses X-Forwarded-For (standard), consider documenting limitation

Performance

  • Minimal per-request overhead (O(1) operations)
  • Memory usage: Low to moderate, cleanup runs every 60s
  • Concurrency: Safe (no race conditions)

Recommendations Priority

High

  1. Add integration test for 401 when env vars not configured
  2. Document IP spoofing limitation
  3. Consider max store size limit with LRU eviction

Medium

  1. Move partial config check to server startup
  2. Export cleanup interval control
  3. Add metrics/logging for rate limit events

Low

  1. Consider Retry-After header for 429 responses
  2. Document memory usage expectations

Overall Assessment

Verdict: LGTM with minor recommendations

This is a well-designed, thoroughly tested feature:

  • Clean architecture
  • Comprehensive test coverage (534 lines)
  • Excellent documentation
  • Security-conscious implementation
  • Type-safe TypeScript

Recommendations are mostly minor improvements. PR is ready to merge pending team requirements.

Great work on this feature!

@github-actions

github-actions Bot commented Feb 4, 2026

Copy link
Copy Markdown

🔐 Gitleaks Findings: 8 issue(s) detected

🔸 Rule: jwt
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: stripe-access-token
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

@claude

claude Bot commented Feb 4, 2026

Copy link
Copy Markdown

Pull Request Review: Add Anonymous Mode for Unauthenticated Requests

Summary

This PR introduces a well-designed anonymous mode feature that allows limited functionality without authentication. The implementation is thorough with excellent test coverage, clear documentation, and proper security considerations.

✅ Strengths

Code Quality

  • Excellent separation of concerns: New middleware (rateLimiter.ts) is cleanly isolated with exported test utilities
  • Comprehensive documentation: CLAUDE.md and README.md updates are clear and thorough, including tables and examples
  • Type safety: Proper TypeScript types with extended Express Request interface
  • AsyncLocalStorage integration: Anonymous mode flag is properly threaded through the request context (src/server.ts:68,784)
  • Consistent error messages: Anonymous mode restrictions return helpful error objects with setup instructions (src/server.ts:197-207,421-442)

Security

  • IP spoofing protection: Rate limiter correctly uses rightmost IP from X-Forwarded-For header (src/lib/middleware/rateLimiter.ts:27-34)
  • Proper credential precedence: User-provided credentials always override anonymous mode (src/lib/middleware/authenticateBearer.ts:207-219)
  • Clear token type differentiation: ENTITY_UNIQUE_COUNTER vs VAULT_TOKEN prevents confusion about data persistence

Test Coverage

  • Excellent coverage: 536 new test lines covering edge cases, boundary conditions, and security scenarios
  • Well-structured tests: Clear test organization with descriptive names and inline comments
  • Mock utilities: Reusable test helpers (createMockRequest, createMockResponse)
  • Store management tests: Verification of cleanup behavior and store size tracking

🔍 Areas for Improvement

1. Rate Limiter Memory Concerns (Medium Priority)

Location: src/lib/middleware/rateLimiter.ts:16-19

The in-memory Map can grow indefinitely with unique IPs in high-traffic scenarios. While the comment acknowledges this:

// WARNING: This Map can grow with unique client IPs. For high-traffic production
// deployments, consider using Redis to avoid memory issues and support multi-instance.
const rateLimitStore = new Map<string, RateLimitEntry>();

Suggestions:

  • Add a maximum store size limit (e.g., 10,000 entries) with LRU eviction
  • Consider implementing a simple LRU cache or using an existing library
  • Add metrics/logging when store size exceeds thresholds

2. Partial Environment Variable Configuration (Low Priority)

Location: src/lib/middleware/authenticateBearer.ts:196-205

The warning for partial anonymous mode configuration is helpful, but it only warns without taking action:

if (anonVarsSet > 0 && anonVarsSet < 3) {
  console.warn(
    `Partial anonymous mode configuration detected (${anonVarsSet}/3 vars set). ` +
      "All three ANON_MODE_* env vars are required: ANON_MODE_API_KEY, ANON_MODE_VAULT_ID, ANON_MODE_VAULT_URL"
  );
}

Suggestion: Consider making this a startup check that throws an error to prevent misconfiguration rather than a per-request warning.

3. Anonymous Mode Detection in Tests (Low Priority)

Location: tests/unit/middleware/authenticateBearer.test.ts:481-484

The test comments acknowledge testing limitations:

// Note: Tests for "ANON env vars NOT configured" and "only some ANON env vars configured"
// are difficult to test reliably due to vitest env var stubbing limitations.

Suggestion: While this is a known testing framework limitation, consider adding integration tests or manual test documentation to ensure these scenarios are verified before releases.

4. Rate Limit Store Cleanup Interval (Low Priority)

Location: src/lib/middleware/rateLimiter.ts:54,57

The cleanup interval is set to 1 minute and uses unref() to not block process exit:

const cleanupInterval = setInterval(cleanupExpiredEntries, CLEANUP_INTERVAL_MS);
cleanupInterval.unref();

Suggestion: Export a cleanup function that can be called explicitly during graceful shutdown to ensure all timers are properly cleared.

5. Console Logging Cleanup (Very Low Priority)

Location: src/server.ts:663

Changed from console.log to console.warn:

console.warn("Skyflow SDK initialization failed:", error instanceof Error ? error.message : "Unknown error");

Suggestion: Consider implementing structured logging (e.g., with pino or winston) for better production observability, especially since this is an MCP server that may be embedded in various contexts.

🎯 Performance Considerations

Positive:

  • Rate limiter uses O(1) Map lookups
  • Cleanup interval prevents unbounded memory growth
  • Anonymous mode check is a simple boolean flag
  • No additional network calls introduced

Potential Concern:

  • In-memory rate limiting doesn't scale horizontally (acknowledged in comments)
  • For multi-instance deployments, consider Redis or similar shared state

🧪 Test Coverage Analysis

Excellent coverage with 536 lines of new tests:

  • ✅ Rate limiting boundary conditions (rateLimiter.test.ts:155-187)
  • ✅ IP extraction and spoofing prevention (rateLimiter.test.ts:69-120)
  • ✅ Anonymous mode activation logic (authenticateBearer.test.ts:419-479)
  • ✅ Credential precedence (authenticateBearer.test.ts:486-548)
  • ✅ Store cleanup and management (rateLimiter.test.ts:318-354)

Missing coverage (acceptable for this PR):

  • Integration tests for end-to-end anonymous mode flow
  • Tests for concurrent rate limiting scenarios
  • Tests for actual Skyflow API behavior with ENTITY_UNIQUE_COUNTER tokens

📋 Checklist

  • ✅ Code follows repository patterns and conventions
  • ✅ Documentation is comprehensive and clear
  • ✅ Security concerns are properly addressed
  • ✅ Test coverage is excellent
  • ✅ Error messages are helpful and actionable
  • ✅ TypeScript types are properly defined
  • ⚠️ Consider LRU cache for rate limiter store (see suggestion Jstjoe/2025 10 09 #1)
  • ⚠️ Consider startup validation for partial env config (see suggestion Add Claude Code GitHub Workflow #2)

Recommendation

APPROVE - This is a high-quality PR that adds valuable functionality with proper safeguards. The suggestions above are minor improvements that can be addressed in follow-up PRs if desired. The implementation demonstrates:

  • Strong understanding of the existing architecture
  • Proper security considerations
  • Excellent test coverage
  • Clear documentation

The anonymous mode will provide a great "try before you buy" experience for new users while maintaining security boundaries for production use.


Reviewed Files: 10 files changed (+1077/-27)

  • ✅ src/lib/middleware/authenticateBearer.ts - Clean anonymous mode detection
  • ✅ src/lib/middleware/rateLimiter.ts - Well-structured rate limiter
  • ✅ src/server.ts - Proper integration into tool handlers
  • ✅ tests/unit/middleware/authenticateBearer.test.ts - Comprehensive tests
  • ✅ tests/unit/middleware/rateLimiter.test.ts - Excellent boundary testing
  • ✅ CLAUDE.md - Thorough documentation updates
  • ✅ README.md - Clear user-facing documentation
  • ✅ .env.sample - Proper configuration examples

🤖 Generated with Claude Code (Sonnet 4.5)

@github-actions

github-actions Bot commented Feb 4, 2026

Copy link
Copy Markdown

🔐 Gitleaks Findings: 8 issue(s) detected

🔸 Rule: jwt
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: stripe-access-token
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: generic-api-key
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

@claude

claude Bot commented Feb 4, 2026

Copy link
Copy Markdown

Pull Request Review: Anonymous Mode Implementation

Summary

This PR introduces anonymous mode functionality that allows limited unauthenticated access to the dehydrate tool with rate limiting. The implementation is well-thought-out and includes comprehensive test coverage. Overall, this is a solid feature addition with good security practices.

✅ Strengths

Code Quality

  • Excellent test coverage: Both rateLimiter.test.ts (356 lines) and authenticateBearer.test.ts (180 new lines) provide thorough testing
  • Clear separation of concerns: Rate limiting and authentication are properly isolated in middleware
  • Well-documented: Comments and documentation clearly explain anonymous mode behavior and limitations
  • Type safety: Proper TypeScript types throughout, including Express request augmentation

Security

  • IP spoofing prevention: Correctly uses rightmost IP from X-Forwarded-For to prevent bypass (line 27-34 in rateLimiter.ts)
  • Rate limiting: Implements per-IP rate limiting specifically for anonymous requests
  • Clear security boundaries: Only dehydrate tool works in anonymous mode; rehydrate and dehydrate_file return helpful errors

Design

  • Graceful degradation: Falls back to anonymous mode when credentials missing and env vars configured
  • Good UX: Helpful error messages guide users to configure credentials for full functionality
  • Per-request isolation: Maintains the existing per-request Skyflow instance pattern

🔍 Issues & Recommendations

1. Critical: .vercel/project.json should not be committed

Location: .vercel/project.json line 1

The change from one orgId to another suggests personal/organization-specific Vercel configuration was accidentally committed:

-{"projectId":"...","orgId":"team_EHvZvBIOBvSYYsZJZEQ2rcnO","projectName":"..."}
+{"projectId":"...","orgId":"team_w0Tg32Q0GT1KuUyTBaZpLg8G","projectName":"..."}

Recommendation:

  • Add .vercel/ to .gitignore
  • Revert this file change
  • This is Vercel-specific deployment configuration that shouldn't be in version control

2. Security: Rate Limit Store Memory Growth

Location: src/lib/middleware/rateLimiter.ts:16-19

The in-memory Map will grow indefinitely with unique IPs, despite the cleanup interval. Consider:

Current issue:

  • Cleanup only runs every 60 seconds
  • High-traffic sites could accumulate thousands of entries between cleanups
  • No upper bound on Map size

Recommendations:

  • Add a maximum store size check (e.g., 10,000 entries) and clear oldest entries when exceeded
  • Document this limitation more prominently for operators
  • Consider using an LRU cache library for automatic eviction
  • For production at scale, document Redis as the recommended solution

3. Configuration: No validation of ANON_MODE env vars at startup

Location: src/server.ts:588-590

The rate limiter config is parsed at startup, but anonymous mode env vars are only checked per-request. This could lead to runtime surprises.

Recommendation: Add startup validation:

// At server startup, validate anon mode config if any vars are set
const anonVarsPresent = [
  process.env.ANON_MODE_API_KEY,
  process.env.ANON_MODE_VAULT_ID,
  process.env.ANON_MODE_VAULT_URL
].filter(Boolean).length;

if (anonVarsPresent > 0 && anonVarsPresent < 3) {
  console.error("Partial anonymous mode configuration detected...");
  process.exit(1); // or throw
}

4. Edge Case: Race condition in rate limit check

Location: src/lib/middleware/rateLimiter.ts:92-108

While the implementation is correct for single-threaded Node.js, the comments could be clearer about concurrency behavior:

Current code (lines 92-108):

if (entry.count >= config.maxRequests) {
  return res.status(429).json({...});
}
entry.count++;

Note: This is actually safe in Node.js's single-threaded event loop, but:

  • Multiple requests from same IP could arrive during async operations
  • Document that this is not thread-safe (though Node.js doesn't need it to be)
  • For truly concurrent scenarios (workers, clusters), Redis would be needed

5. Code Quality: Inconsistent error response structure

Location: src/server.ts:196-210, 420-439

Anonymous mode error responses have slightly different structures:

  • rehydrate: includes anonymousModeRestricted: true
  • dehydrate_file: includes anonymousModeRestricted: true, alternativeTool: "dehydrate"

Recommendation: Standardize the error response schema for consistency. Consider:

interface AnonymousModeError {
  error: string;
  anonymousModeRestricted: true;
  message: string;
  helpUrl: string;
  alternativeTool?: string; // optional
}

6. Minor: Console logging removed

Location: src/lib/middleware/authenticateBearer.ts:550-559 (removed), src/server.ts:930

Debug logging was removed from authenticateBearer (lines 550-559 in diff) but a console.log was changed to console.warn in server.ts (line 930).

Recommendation:

  • Consider keeping debug logging behind an environment variable (e.g., DEBUG=true)
  • Consistent use of console.warn for warnings vs console.log for info

7. Documentation: Missing upgrade path guidance

Location: README.md:148

The README mentions "To unlock full functionality... configure your Skyflow credentials" but doesn't link to specific configuration instructions.

Recommendation: Add a direct link to the configuration section:

To unlock full functionality (rehydrate, file processing, persistent vault tokens), 
see [Configuration](#configuration) to set up your Skyflow credentials.

🧪 Test Coverage

Excellent

  • ✅ Rate limiter: Comprehensive tests for limits, resets, client isolation, headers
  • ✅ Authentication: Tests for anonymous mode detection, credential extraction
  • ✅ Edge cases: Empty IPs, partial config, multiple IPs in X-Forwarded-For

Missing (Nice-to-have)

  • Integration test: Full request flow through anonymous mode
  • E2E test: Actual Skyflow SDK behavior in anonymous mode with ENTITY_UNIQUE_COUNTER tokens
  • Load test: Memory behavior under high IP diversity

📊 Performance Considerations

  1. Rate limit cleanup interval: 60-second cleanup is reasonable but could be tuned based on traffic
  2. Map lookups: O(1) for get/set operations, efficient for this use case
  3. Memory overhead: Each entry is ~16 bytes + key string, minimal per client

🔒 Security Assessment

Strengths

  • ✅ Proper IP extraction from X-Forwarded-For (rightmost IP)
  • ✅ Rate limiting only on anonymous requests
  • ✅ Clear separation of anonymous vs authenticated data flows
  • ✅ No sensitive data logged

Concerns

  • ⚠️ No upper bound on rate limit store size (see Issue Add Claude Code GitHub Workflow #2)
  • ℹ️ In-memory store doesn't work across multiple instances (documented appropriately)

📝 Overall Assessment

Recommendation: Approve with minor changes

This is a well-implemented feature that follows security best practices and includes excellent test coverage. The issues identified are mostly minor improvements and documentation enhancements, with the exception of:

  1. Must fix before merge: Remove .vercel/project.json changes
  2. Should fix: Add rate limit store size cap to prevent memory issues
  3. Nice to have: Startup validation for anonymous mode env vars

The implementation demonstrates good understanding of Express middleware patterns, security considerations (IP spoofing prevention), and the MCP protocol. The anonymous mode provides a great "try before you buy" experience while maintaining security boundaries.

🎯 Action Items

  • Revert .vercel/project.json changes and add to .gitignore
  • Add maximum size check to rate limit store (prevent OOM)
  • Consider startup validation for partial anonymous mode config
  • Standardize error response structure across tools
  • Add direct link in README upgrade path section

Great work on this feature! The anonymous mode will make it much easier for users to try the service before committing to full setup. 🚀

@github-actions

github-actions Bot commented Feb 4, 2026

Copy link
Copy Markdown

🔐 Gitleaks Findings: 8 issue(s) detected

🔸 Rule: jwt
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: stripe-access-token
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

@claude

claude Bot commented Feb 4, 2026

Copy link
Copy Markdown

Pull Request Review: Anonymous Mode Implementation

Overview

This PR introduces a well-designed anonymous mode feature that allows unauthenticated users to try the dehydrate tool with rate limiting. The implementation is clean, follows existing patterns, and includes excellent test coverage.

✅ Strengths

1. Architecture & Design

  • Clean separation of concerns: Anonymous mode logic is properly isolated in middleware
  • Consistent with existing patterns: Follows the per-request credential approach
  • Graceful degradation: Tools that don't work in anonymous mode return helpful error messages instead of failing silently
  • Proper use of AsyncLocalStorage: isAnonymousMode correctly propagated through request context

2. Security

  • IP-based rate limiting: Correctly uses rightmost IP from X-Forwarded-For header (lines 27-34 in rateLimiter.ts) to prevent spoofing
  • Clear security comment: Explicitly documents why rightmost IP is used
  • No credential leakage: Removed debug logging of sensitive values from authenticateBearer.ts
  • Rate limit enforcement: Applied before request processing

3. Test Coverage

  • Comprehensive unit tests: 356 lines for rate limiter, 180 lines for auth middleware
  • Edge cases covered: IP extraction, rate limit boundaries, anonymous mode detection
  • Test utilities: Excellent mock factories for Request/Response objects
  • Boundary testing: Correctly tests maxRequests boundary (src/tests/unit/middleware/rateLimiter.test.ts:1322-1350)

4. Documentation

  • Excellent CLAUDE.md updates: Clear explanation of anonymous mode behavior
  • Helpful README section: "Try Before You Buy" section with examples
  • User-facing error messages: Clear instructions for upgrading to full functionality
  • Inline comments: Good explanations in rate limiter logic

5. User Experience

  • Helpful error responses: rehydrate and dehydrate_file explain why they're unavailable and how to enable them
  • Rate limit headers: Standard X-RateLimit-* headers for client feedback
  • Clear token format note: Response includes anonymousMode: true and explanatory note

🔍 Areas for Improvement

1. Memory Management (Minor)

Issue: In-memory rate limit store could grow unbounded in high-traffic scenarios

  • Location: src/lib/middleware/rateLimiter.ts:19
  • Current mitigation: Cleanup interval runs every 60 seconds
  • Potential issue: High traffic with many unique IPs between cleanup intervals
  • Suggestion: Consider adding a max store size check or TTL-based eviction
  • Documentation: Already includes clear warning comment

2. Configuration Validation (Minor)

Issue: Rate limiter config is created at server startup, but validation errors would crash the server

  • Location: src/server.ts:608-610
  • Current: getAnonymousRateLimitConfig() throws on invalid env vars
  • Suggestion: Consider validating env vars at startup with helpful error messages before creating Express app
  • Impact: Low - defaults are sensible and documented

3. Vercel Project Config (Low Priority)

Issue: .vercel/project.json shows orgId change

  • Location: .vercel/project.json:1
  • Note: This appears to be a local development artifact
  • Suggestion: Consider adding .vercel/ to .gitignore or documenting why it's committed

4. Tool Response Consistency (Minor)

Issue: dehydrate uses conditional spread for anonymous mode fields, while other tools use full objects

  • Location: src/server.ts:832-835
  • Current approach is fine: Conditional fields make sense for optional metadata
  • Observation: Both rehydrate and dehydrate_file always include full error objects, which is consistent

🎯 Code Quality

Type Safety: ✅ Excellent

  • Proper TypeScript interfaces for all new types
  • Global Express namespace extension for custom request properties
  • No any types used

Error Handling: ✅ Very Good

  • Anonymous mode detection with partial config warning (authenticateBearer.ts:200-205)
  • Clear error messages with actionable guidance
  • isError: true flag properly set for error responses

Testing: ✅ Excellent

  • Unit tests for all new middleware functions
  • Edge case coverage (IP extraction, rate limits, anonymous detection)
  • Test utilities for clean mocking
  • Note about vitest env var limitations acknowledged in comments

🔒 Security Assessment

Rate Limiting: ✅ Secure

  • Correct IP extraction (rightmost from X-Forwarded-For)
  • Per-IP tracking prevents abuse
  • Clear documentation of spoofing prevention

Anonymous Mode: ✅ Secure

  • Limited to single tool (dehydrate)
  • No vault persistence in anonymous mode
  • Credentials properly isolated per request
  • No credential fallthrough from authenticated to anonymous requests

Credentials: ✅ Secure

  • Removed verbose logging (was leaking presence indicators)
  • Credentials passed through without server-side validation (delegated to Skyflow API)
  • Anonymous mode uses separate vault configuration

📊 Performance Considerations

Good:

  • Rate limiter is O(1) lookup with Map
  • Cleanup interval uses unref() to prevent blocking shutdown
  • Per-request Skyflow instance pattern maintained

Watch:

  • In-memory store could become large under high traffic
  • Cleanup only runs every 60 seconds (acceptable for expected scale)

🎉 Summary

This is a high-quality PR that introduces a valuable feature with:

  • ✅ Clean architecture and code organization
  • ✅ Comprehensive test coverage (536 lines of new tests)
  • ✅ Excellent documentation
  • ✅ Secure implementation
  • ✅ Good user experience

The issues noted above are all minor and don't block merging. The code is production-ready as-is. Great work!

Recommendation

✅ Approve and merge - This PR is ready for production.

Optional follow-ups (can be done in separate PRs if needed):

  1. Add max size limit to rate limit store for extreme traffic scenarios
  2. Consider moving .vercel/ to .gitignore
  3. Add startup validation for rate limit config env vars

Review conducted with analysis of: implementation code, tests, documentation, security patterns, and adherence to project conventions in CLAUDE.md

@github-actions

github-actions Bot commented Feb 4, 2026

Copy link
Copy Markdown

🔐 Gitleaks Findings: 8 issue(s) detected

🔸 Rule: jwt
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: generic-api-key
📄 File: src/lib/middleware/authenticateBearer.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: src/lib/middleware/authenticateBearer.ts:null

🔸 Rule: stripe-access-token
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: jwt
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

🔸 Rule: generic-api-key
📄 File: tests/unit/middleware/authenticateBearer.test.ts:null
📝 Description: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
🔑 Secret: **********
🔗 Path: tests/unit/middleware/authenticateBearer.test.ts:null

@claude

claude Bot commented Feb 4, 2026

Copy link
Copy Markdown

PR Review: Add Anonymous Mode for Unauthenticated Requests

Thank you for this comprehensive implementation! This feature will significantly improve the developer experience.

✅ Strengths

Architecture & Design

  • Clean separation of concerns with middleware pattern
  • Excellent use of AsyncLocalStorage for per-request context
  • Graceful degradation with helpful error messages
  • Security-conscious rate limiting

Code Quality

  • Comprehensive test coverage (356 + 180 lines of tests)
  • Clear documentation in CLAUDE.md and README
  • Proper TypeScript types
  • Informative error handling

Implementation

  • Smart token format switching (ENTITY_UNIQUE_COUNTER vs VAULT_TOKEN)
  • Memory management with cleanup interval
  • Standard X-RateLimit-* headers
  • Proper env var validation

🔍 Critical Issues (Must Fix)

1. Vercel Project Config (.vercel/project.json)

Issue: orgId changed from team_EHvZvBIOBvSYYsZJZEQ2rcnO to team_w0Tg32Q0GT1KuUyTBaZpLg8G

This appears accidental and could cause deployment issues.

Fix: Revert this change

2. Incomplete outputSchema (src/server.ts:198)

Issue: rehydrate and dehydrate_file outputSchemas missing error fields returned in anonymous mode

Per CLAUDE.md: Keep schemas in sync with actual return values

Fix: Add optional error, anonymousModeRestricted, message, helpUrl fields to schemas

🔶 High Priority

3. Missing structuredContent (src/server.ts:584, 594)

Inconsistent error responses - anonymous mode includes structuredContent but Skyflow API errors dont

Fix: Add structuredContent to all error responses

4. Rate Limiter Init (src/server.ts:608)

Invalid env vars crash server at startup even if anonymous mode unused

Fix: Wrap in try-catch or lazy-initialize

5. Memory Growth (rateLimiter.ts:19)

In-memory Map unbounded growth risk with many IPs

Fix: Consider max size limit with LRU eviction

🟡 Security & Docs

6. IP Spoofing Docs (rateLimiter.ts:27)

Implementation correct but missing trusted proxy assumption documentation

Fix: Document deployment requirements

📋 Summary

Category Rating
Architecture ⭐⭐⭐⭐⭐
Security ⭐⭐⭐⭐
Testing ⭐⭐⭐⭐
Documentation ⭐⭐⭐⭐⭐
Code Quality ⭐⭐⭐⭐

🎯 Recommendation

Conditional approval - Excellent work!

Blocking issues:

  1. Revert .vercel/project.json
  2. Fix outputSchema for error cases

Once resolved, ready to merge. Great job on comprehensive tests and docs! 👏

@jstjoe
jstjoe merged commit 3c2e34e into main Feb 4, 2026
6 checks passed

This branch was successfully deployed

1 active deployment
Preview — 6f9c6c59 Deployed Feb 4, 2026 by vercel[bot]
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