Skip to content

feat: retained member sessions - #6

Merged
ECWireless merged 3 commits into
mainfrom
feat/retained-member-sessions
Aug 2, 2026
Merged

feat: retained member sessions#6
ECWireless merged 3 commits into
mainfrom
feat/retained-member-sessions

Conversation

@ECWireless

@ECWireless ECWireless commented Aug 2, 2026

Copy link
Copy Markdown
Member

This pull request migrates the authentication and authorization system from a basic signature-based flow to a standards-based Sign-In with Ethereum (SIWE) flow, with secure session management and improved rate limiting. It introduces new API endpoints for SIWE message generation, verification, session restoration, and logout, and updates file/channel access endpoints to require a valid member session. The documentation and environment variable requirements are also updated to reflect these changes.

Authentication and Session Management Overhaul

  • Introduced new SIWE-based authentication endpoints: app/api/auth/message/route.ts (SIWE message generation), app/api/auth/verify/route.ts (SIWE verification and session establishment), app/api/auth/session/route.ts (session restoration), and app/api/auth/logout/route.ts (logout and session clearing). These endpoints manage secure, short-lived sessions using HttpOnly cookies and JWTs. [1] [2] [3] [4]
  • Added session helpers in app/api/shared/session.ts (referenced in docs) to handle challenge/session tokens and secure cookies, backed by JWT_SECRET.

API Route Updates

  • Refactored app/api/files/route.ts and app/api/channel/route.ts to require a valid member session instead of verifying signatures in the request body. Errors related to session/auth are now handled consistently. [1] [2] [3] [4] [5] [6]
  • Removed legacy signature validation and related request body types from app/api/shared/memberAuth.ts. [1] [2] [3]

Membership Verification and Subgraph Query

  • Updated the member subgraph query to require a minimum of 100 shares (shares_gte) for eligibility, and clarified error messages for ineligible wallets. [1] [2]

Security and Rate Limiting

  • Implemented rate limiting for authentication endpoints and RPC usage in app/api/shared/authRateLimit.ts, returning appropriate 429 responses with Retry-After headers.
  • Updated documentation to clarify that JWT_SECRET must be a cryptographically random value and described the new authentication/session system. [1] [2]

These changes enhance security, improve user experience with session persistence, and lay the groundwork for more scalable and standards-compliant authentication.

Summary by CodeRabbit

  • New Features

    • Added SIWE-based wallet authentication with session restoration and logout.
    • Added support for signing in with eligible Gnosis Chain members holding at least 100 shares.
    • Added authentication rate limiting and clearer error responses.
    • Protected archive and file access using authenticated sessions.
  • Bug Fixes

    • Improved handling of expired sessions, invalid networks, mismatched wallets, and failed requests.
  • Style

    • Refined archive and access-page layouts, including responsive behavior.

Copilot AI review requested due to automatic review settings August 2, 2026 05:16
@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
the-valhalla Ready Ready Preview Aug 2, 2026 5:49am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ECWireless, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aeabc467-33c2-486a-b6b6-1095030eb498

📥 Commits

Reviewing files that changed from the base of the PR and between 828e341 and 94c06dd.

📒 Files selected for processing (12)
  • AGENTS.md
  • app/api/auth/message/route.ts
  • app/api/auth/session/route.ts
  • app/api/auth/verify/route.ts
  • app/api/channel/route.ts
  • app/api/files/route.ts
  • app/api/shared/authRateLimit.ts
  • app/api/shared/memberAuth.ts
  • app/config.ts
  • app/page.tsx
  • app/utils/requests.ts
  • sample.env
📝 Walkthrough

Walkthrough

The PR replaces wallet-balance and signed-message authorization with Gnosis SIWE authentication, JWT-backed member sessions, session-protected archive APIs, client session state, rate limiting, and updated access-page layouts.

Changes

SIWE session authentication

Layer / File(s) Summary
Session and member-auth foundation
app/api/shared/session.ts, app/api/shared/authRateLimit.ts, app/api/shared/memberAuth.ts, package.json, sample.env, AGENTS.md
Adds JWT challenge and session helpers, authentication rate limits, 100-share eligibility checks, session errors, and jose configuration.
Challenge, verification, and session routes
app/api/auth/*
Adds same-origin SIWE challenge, verification, session restoration, and logout endpoints with cookie management and rate-limit responses.
Client session and request integration
app/utils/requests.ts, app/page.tsx
Adds session request helpers, normalized API errors, session restoration, wallet transition handling, authentication states, logout handling, and session-backed archive requests.
Session-protected archive APIs
app/api/channel/route.ts, app/api/files/route.ts
Replaces request signatures with member-session authorization while preserving S3 URL generation and paginated file listing.
Access layout and repository guidance
app/globals.css, docs/session-workflow.md
Updates access-page layout rules and documents branch naming requirements.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Wallet
  participant AuthMessage
  participant AuthVerify
  participant MemberAuth
  participant SessionCookie
  participant ValhallaPage
  participant ArchiveAPI
  Wallet->>AuthMessage: Request SIWE challenge
  AuthMessage->>SessionCookie: Store challenge cookie
  Wallet->>AuthVerify: Submit message and signature
  AuthVerify->>MemberAuth: Verify signature and eligibility
  AuthVerify->>SessionCookie: Store member session
  ValhallaPage->>ArchiveAPI: Request archive data
  ArchiveAPI-->>ValhallaPage: Return session-authorized data
Loading

Possibly related PRs

  • raid-guild/the-valhalla#3: Modifies the same authentication routes and member-auth utilities with signature-based authorization.

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding retained member sessions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/retained-member-sessions

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.

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 migrates Valhalla’s access gating from an ad-hoc “sign a message” flow to an EIP-4361 (SIWE) flow with retained, HttpOnly cookie sessions, and updates the client and protected API routes to rely on those sessions for file/channel access.

Changes:

  • Added SIWE message + verification endpoints and JWT-backed cookie helpers for challenge/session management.
  • Updated /api/files and /api/channel to require a valid member session instead of a request-body signature.
  • Refactored the client-side flow to restore sessions, run SIWE, and normalize API error handling (including 401/403 recovery/logout).

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
sample.env Updates JWT secret guidance to require a 32+ byte random value.
pnpm-lock.yaml Removes ethers and locks new jose dependency.
package.json Swaps ethers for jose to support JWT/session signing.
docs/session-workflow.md Adds branch naming guidance (conventional prefixes).
app/utils/requests.ts Introduces session/auth request helpers and an ApiRequestError with HTTP status.
app/page.tsx Reworks the UI flow to restore sessions, run SIWE, and handle session invalidation/logout.
app/globals.css Adjusts layout/styles for the new hero + gated access presentation.
app/api/shared/session.ts Adds JWT signing/verification helpers and secure cookie management for challenge/session tokens.
app/api/shared/memberAuth.ts Shifts membership enforcement to session-based authorization and adds a shares threshold filter in the member query.
app/api/shared/authRateLimit.ts Adds in-memory rate limiting utilities and a standard 429 response.
app/api/files/route.ts Protects file listing behind requireMemberSession() instead of signature verification.
app/api/channel/route.ts Protects signed URL issuance behind requireMemberSession() instead of signature verification.
app/api/auth/verify/route.ts Verifies SIWE message/signature and establishes a session cookie on success.
app/api/auth/session/route.ts Restores/refreshes existing sessions and clears invalid/ineligible sessions.
app/api/auth/message/route.ts Generates a SIWE message and stores a short-lived challenge cookie.
app/api/auth/logout/route.ts Clears auth cookies to end a retained session.
AGENTS.md Updates repo guidance to reflect SIWE + retained-session architecture and JWT_SECRET requirements.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (1)

app/api/files/route.ts:65

  • The response from this session-protected endpoint can include sensitive membership-gated data (the S3 object keys). Consider explicitly setting Cache-Control: no-store to prevent intermediary/proxy caching once auth is cookie-based.
    return NextResponse.json({ response: files });

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

Comment thread app/api/shared/memberAuth.ts
Comment thread app/api/channel/route.ts Outdated
Comment thread app/api/files/route.ts Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 7

🧹 Nitpick comments (9)
app/api/auth/verify/route.ts (1)

173-178: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse NOT_MEMBER_ERROR instead of repeating the string.

app/api/shared/memberAuth.ts line 5 exports NOT_MEMBER_ERROR with the same text. This file already imports from that module. Two copies will drift when the threshold changes.

♻️ Suggested change
 import {
   isEligibleMemberAddress,
   logServerError,
+  NOT_MEMBER_ERROR,
 } from "../../shared/memberAuth";
     if (!(await isEligibleMemberAddress(parsedMessage.address))) {
       return NextResponse.json(
-        { error: "This wallet does not hold at least 100 RaidGuild shares." },
+        { error: NOT_MEMBER_ERROR },
         { status: 403 },
       );
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/auth/verify/route.ts` around lines 173 - 178, Replace the duplicated
membership error string in the isEligibleMemberAddress rejection with the
imported NOT_MEMBER_ERROR constant from memberAuth.ts, preserving the existing
403 response behavior.
app/api/auth/message/route.ts (1)

63-76: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Construct the origin URL once.

new URL(origin) runs twice on lines 68 and 72.

♻️ Suggested change
   const nonce = generateSiweNonce();
   const now = new Date();
+  const originUrl = new URL(origin);
   const message = createSiweMessage({
     address,
     chainId: gnosis.id,
-    domain: new URL(origin).host,
+    domain: originUrl.host,
     expirationTime: new Date(now.getTime() + 5 * 60 * 1000),
     issuedAt: now,
     nonce,
-    scheme: new URL(origin).protocol.slice(0, -1),
+    scheme: originUrl.protocol.slice(0, -1),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/auth/message/route.ts` around lines 63 - 76, Construct the origin URL
once before createSiweMessage, store it in a local variable, and reuse that
variable for both the domain and scheme fields instead of calling new
URL(origin) twice.
app/api/files/route.ts (1)

67-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the shared MemberSessionError response mapping.

app/api/channel/route.ts lines 48-53 contain the identical block. A small helper in app/api/shared/memberAuth.ts keeps the status contract in one place as more protected routes appear.

♻️ Suggested helper
// app/api/shared/memberAuth.ts
export function memberSessionErrorResponse(error: unknown) {
  if (error instanceof MemberSessionError) {
    return NextResponse.json({ error: error.message }, { status: error.status });
  }
  return null;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/files/route.ts` around lines 67 - 73, Extract the duplicated
MemberSessionError handling from the route-level catch blocks into a shared
memberSessionErrorResponse helper in memberAuth.ts. Update both the files route
and channel route to call the helper and return its response when non-null,
preserving the existing error message and status contract.
app/api/channel/route.ts (1)

14-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Check the session before parsing the body.

requireMemberSession() runs on line 31, after the request body is read and validated. An unauthenticated caller currently receives 400 for a malformed body instead of 401. Moving the session check first gives consistent responses and avoids work for unauthenticated callers.

♻️ Suggested change
 export async function POST(req: Request) {
+  try {
+    await requireMemberSession();
+  } catch (error: unknown) {
+    if (error instanceof MemberSessionError) {
+      return NextResponse.json(
+        { error: error.message },
+        { status: error.status },
+      );
+    }
+    logServerError("Error fetching channel", error);
+    return NextResponse.json({ error: "Failed to fetch data" }, { status: 500 });
+  }
+
   let requestBody: ChannelRequestBody;

Keep the existing MemberSessionError branch for the S3 block, or extract a small helper to avoid repeating the mapping.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/channel/route.ts` around lines 14 - 31, Move the
requireMemberSession() call and its existing MemberSessionError handling to the
beginning of POST, before req.json() parsing and validation. Preserve the 401
response mapping and keep the current request-body validation and S3 error
handling behavior unchanged, reusing a helper only if needed to avoid
duplicating the session-error mapping.
app/api/shared/authRateLimit.ts (1)

11-13: 📐 Maintainability & Code Quality | 🔵 Trivial

In-memory limits apply per process only.

rateLimitEntries and rpcBudget live in process memory. On serverless or multi-instance deployments, each instance keeps its own counters, so the effective limit multiplies by the instance count. Counters also reset on cold start.

This is acceptable as a first defense layer. If the deployment scales horizontally, plan a shared store or an edge/WAF rate limit. Document the tradeoff before adding a new dependency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/shared/authRateLimit.ts` around lines 11 - 13, Document near
rateLimitEntries and rpcBudget that these in-memory counters are process-local,
reset on cold starts, and multiply effective limits across serverless or
multi-instance deployments. Explicitly note that this is an intentional first
defense layer and that horizontally scaled deployments should use a shared store
or edge/WAF rate limiting, without adding a dependency.
AGENTS.md (1)

38-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add the rate-limit module to the repository map.

The map lists app/api/shared/memberAuth.ts and app/api/shared/session.ts. This PR also adds app/api/shared/authRateLimit.ts, which controls authentication throttling and 429 responses. Document it so contributors do not bypass the limiter.

📝 Proposed documentation addition
 - `app/api/shared/session.ts`: signed challenge/session tokens and secure cookie
   helpers backed by `JWT_SECRET`.
+- `app/api/shared/authRateLimit.ts`: request throttling for authentication and
+  RPC routes, including `429` responses with `Retry-After`.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@AGENTS.md` around lines 38 - 43, Add app/api/shared/authRateLimit.ts to the
repository map alongside memberAuth.ts and session.ts, describing that it
provides authentication throttling and 429 responses.
app/page.tsx (2)

354-384: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Explain the queueMicrotask deferral.

The effect calls resetAuthentication and resetFileRequest, then defers setAuthPhase and setActionError into a microtask. The ordering requirement is not obvious. Without a comment, a later edit can inline the two calls and lose the error message that the resets clear.

Add a short comment that states why the state updates must run after the resets.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/page.tsx` around lines 354 - 384, Add a concise comment immediately
before the queueMicrotask call in the protected-access error effect, explaining
that resetAuthentication and resetFileRequest clear related state, so
setAuthPhase and setActionError must be deferred to preserve the error message
after those resets. Do not change the existing ordering or behavior.

304-352: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the nested ternary chains with a derived helper.

accessState uses a ten-level ternary chain. statusAnnouncement then repeats almost the same branch order. A future state change must be applied in both places, and the two chains can drift.

Extract the state resolution into a small function and map the announcement from a lookup keyed by accessState.

♻️ Sketch of the refactor
+const resolveAccessState = (input: {
+  chainId?: number;
+  hasAddress: boolean;
+  hasVerifiedAccess: boolean;
+  filesError: unknown;
+  isConnecting: boolean;
+  isEndingSession: boolean;
+  isFilesLoading: boolean;
+  isSessionLoading: boolean;
+  logoutError: string;
+  sessionError: unknown;
+}) => {
+  if (input.hasVerifiedAccess) {
+    if (input.isFilesLoading) return "archive-loading";
+    return input.filesError ? "archive-error" : "archive";
+  }
+  if (input.isEndingSession) return "signing-out";
+  if (input.logoutError) return "logout-error";
+  if (input.isSessionLoading || input.isConnecting) return "loading";
+  if (input.sessionError) return "session-error";
+  if (!input.hasAddress) return "idle";
+  return input.chainId !== gnosis.id ? "network-error" : "check-in";
+};

Then build statusAnnouncement from a Record<AccessState, string>, and keep only the check-in case dependent on authPhase.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/page.tsx` around lines 304 - 352, Replace the nested ternary chains
defining accessState and statusAnnouncement with a derived state-resolution
helper and a Record keyed by AccessState for announcements. Ensure the helper
preserves the existing branch precedence, and keep only the check-in
announcement dynamic by deriving it from authPhase while all other states use
lookup values.
app/utils/requests.ts (1)

83-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type the channel response.

axios.post has no generic here, so data.data.channel resolves to any. app/page.tsx declares useMutation<string, Error, string>, which relies on that untyped value. Add the response type so the boundary stays typed.

♻️ Proposed typing
 export const getValhallaFile = async (key: string) => {
   try {
-    const data = await axios.post("/api/channel", {
+    const data = await axios.post<{ channel: string }>("/api/channel", {
       key,
     });
     return data.data.channel;
As per coding guidelines: "Prefer typed request validation at API boundaries."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/utils/requests.ts` around lines 83 - 92, Update getValhallaFile to
provide an explicit response type to axios.post, defining the expected channel
payload as a string. Return the typed channel value so the useMutation<string,
Error, string> contract remains enforced without any.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/api/auth/message/route.ts`:
- Around line 57-61: Update the rate-limit flow in the message route to key the
primary limit on the client’s network identity rather than the
attacker-controlled address, while retaining the address-based limit as a
secondary check. Derive forwarded client identity only when the request comes
through a trusted proxy; otherwise use the direct connection identity, and apply
the existing authRateLimitResponse behavior for either limit.

In `@app/api/auth/session/route.ts`:
- Around line 43-49: Update the error response in the session route’s catch
block to include the Cache-Control: no-store header, matching the successful
response paths and preventing intermediaries from storing the authentication
error response.
- Around line 26-42: The session route must enforce per-address rate limiting
before calling isEligibleMemberAddress. Add the session-specific
checkAuthRateLimit invocation near the start of the handler, and return
authRateLimitResponse immediately when the address exceeds the limit, preserving
the existing eligibility and session-token flow for allowed requests.

In `@app/api/auth/verify/route.ts`:
- Around line 41-44: Update the createPublicClient configuration for
gnosisClient to pass the GNOSIS_RPC_URL environment variable to http() and
configure an explicit request timeout; preserve the existing Gnosis chain setup.
Add GNOSIS_RPC_URL to sample.env without committing an endpoint value.

In `@app/api/shared/authRateLimit.ts`:
- Around line 63-86: Update checkAuthRpcBudget to accept an identity parameter
and track independent counters and reset times per identity while preserving the
existing global cap. In the auth verify route, pass the challenge address as
identity when invoking checkAuthRpcBudget so one smart-account cannot exhaust
another caller’s budget.

In `@app/api/shared/memberAuth.ts`:
- Around line 24-25: Update MEMBERSHIP_MIN_SHARES to the intended whole-share
threshold used directly by the subgraph shares_gte filter, then align the
related membership error messages with that threshold so they report the actual
required share count.

In `@app/api/shared/session.ts`:
- Around line 167-189: Update getSameOrigin to derive the expected protocol from
x-forwarded-proto when the request is confirmed to come through a known trusted
proxy, while retaining requestUrl.protocol for direct or untrusted requests.
Compare the parsed Origin protocol against that trusted protocol and preserve
the existing same-host validation and null-on-invalid behavior.

---

Nitpick comments:
In `@AGENTS.md`:
- Around line 38-43: Add app/api/shared/authRateLimit.ts to the repository map
alongside memberAuth.ts and session.ts, describing that it provides
authentication throttling and 429 responses.

In `@app/api/auth/message/route.ts`:
- Around line 63-76: Construct the origin URL once before createSiweMessage,
store it in a local variable, and reuse that variable for both the domain and
scheme fields instead of calling new URL(origin) twice.

In `@app/api/auth/verify/route.ts`:
- Around line 173-178: Replace the duplicated membership error string in the
isEligibleMemberAddress rejection with the imported NOT_MEMBER_ERROR constant
from memberAuth.ts, preserving the existing 403 response behavior.

In `@app/api/channel/route.ts`:
- Around line 14-31: Move the requireMemberSession() call and its existing
MemberSessionError handling to the beginning of POST, before req.json() parsing
and validation. Preserve the 401 response mapping and keep the current
request-body validation and S3 error handling behavior unchanged, reusing a
helper only if needed to avoid duplicating the session-error mapping.

In `@app/api/files/route.ts`:
- Around line 67-73: Extract the duplicated MemberSessionError handling from the
route-level catch blocks into a shared memberSessionErrorResponse helper in
memberAuth.ts. Update both the files route and channel route to call the helper
and return its response when non-null, preserving the existing error message and
status contract.

In `@app/api/shared/authRateLimit.ts`:
- Around line 11-13: Document near rateLimitEntries and rpcBudget that these
in-memory counters are process-local, reset on cold starts, and multiply
effective limits across serverless or multi-instance deployments. Explicitly
note that this is an intentional first defense layer and that horizontally
scaled deployments should use a shared store or edge/WAF rate limiting, without
adding a dependency.

In `@app/page.tsx`:
- Around line 354-384: Add a concise comment immediately before the
queueMicrotask call in the protected-access error effect, explaining that
resetAuthentication and resetFileRequest clear related state, so setAuthPhase
and setActionError must be deferred to preserve the error message after those
resets. Do not change the existing ordering or behavior.
- Around line 304-352: Replace the nested ternary chains defining accessState
and statusAnnouncement with a derived state-resolution helper and a Record keyed
by AccessState for announcements. Ensure the helper preserves the existing
branch precedence, and keep only the check-in announcement dynamic by deriving
it from authPhase while all other states use lookup values.

In `@app/utils/requests.ts`:
- Around line 83-92: Update getValhallaFile to provide an explicit response type
to axios.post, defining the expected channel payload as a string. Return the
typed channel value so the useMutation<string, Error, string> contract remains
enforced without any.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b78aa8f-9834-4e23-9886-0a79ec864a1f

📥 Commits

Reviewing files that changed from the base of the PR and between 6ddb876 and 828e341.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • AGENTS.md
  • app/api/auth/logout/route.ts
  • app/api/auth/message/route.ts
  • app/api/auth/session/route.ts
  • app/api/auth/verify/route.ts
  • app/api/channel/route.ts
  • app/api/files/route.ts
  • app/api/shared/authRateLimit.ts
  • app/api/shared/memberAuth.ts
  • app/api/shared/session.ts
  • app/globals.css
  • app/page.tsx
  • app/utils/requests.ts
  • docs/session-workflow.md
  • package.json
  • sample.env

Comment thread app/api/auth/message/route.ts
Comment thread app/api/auth/session/route.ts
Comment thread app/api/auth/session/route.ts
Comment thread app/api/auth/verify/route.ts
Comment thread app/api/shared/authRateLimit.ts Outdated
Comment thread app/api/shared/memberAuth.ts
Comment thread app/api/shared/session.ts
@ECWireless
ECWireless merged commit 35c38ee into main Aug 2, 2026
3 checks passed
@ECWireless
ECWireless deleted the feat/retained-member-sessions branch August 2, 2026 05:51
@coderabbitai coderabbitai Bot mentioned this pull request Aug 2, 2026
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