Skip to content

Repository files navigation

Gitscoop

Gitscoop

Know any codebase before you touch it. Waitlist now open.

OverviewArchitectureRunning LocallyUtility ScriptsTech Stack

Overview

Gitscoop is currently in waitlist mode. Emails are collected through a robust, multi-layered pipeline. Before an entry is registered, it passes through Vercel BotID, strict rate limiting via Upstash Redis, and real-time email validation via ZeroBounce.

Rather than relying on basic webhooks, durable Upstash Workflows are utilized to orchestrate asynchronous confirmation and invitation emails. Authentication bypasses the default Clerk Account Portal completely in favor of custom Clerk Elements, handling sign-in, sign-up, password resets, and invite acceptance natively. Beyond that, all transactional emails are intercepted from Clerk and routed through tailored Resend templates.

Architecture

The diagram below illustrates the high-level architecture of the waitlist and authentication pipelines.

%%{
  init: {
    "themeCSS": ".edgeLabel .labelBkg { background: transparent !important; } .edgeLabel span:not(:empty) { padding: 5px 12px; border-radius: 6px; display: inline-block; }",
    "flowchart": {
      "padding": 20,
      "rankSpacing": 60,
      "nodeSpacing": 60,
      "subGraphTitleMargin": {
        "top": 12,
        "bottom": 16
      }
    }
  }
}%%
flowchart TD
    %% Gitscoop · Next.js 16 on Vercel
    User(["User"])
    WL["Waitlist Page"]
    Auth["Sign In / Sign Up\nClerk Elements"]
    Inv["/invitation/accept"]
    SSO["SSO Callback"]
    Mid{"Clerk Middleware"}
    App["App"]
    ClerkSvc["Clerk\nAuth · Waitlist · Invitation"]
    ResendSvc["Resend\nEmail · Audience"]

    Blocked(["Blocked"])
    Limited(["Rate Limited"])
    Rejected(["Rejected"])

    %% Vertical restraint for terminations
    Blocked ~~~ Limited
    Limited ~~~ Rejected

    subgraph Action["Server Action — joinWaitlist"]
        BotID["BotID (Vercel)\nBot Protection"]
        RL["Upstash Rate Limit\n5/hr waitlist\n10/hr global"]
        ResendCheck{"Existing\ncontact?"}
        Cache[("Upstash Redis\nEmail Cache")]
        ZB["ZeroBounce\nEmail Validation"]
        SyncResend["Resend Client\nManage Contact"]
    end

    %% Invisible link to pull Async subgraph down to make space for the webhook event label
    ClerkSvc ~~~~~ WHook

    subgraph Async["Async — Webhooks & Workflows"]
        WHook["/api/webhooks/clerk\nSvix Verified"]
        QStashSvc["Upstash Workflow\nQStash"]
        WFlow["/api/workflows/waitlist"]
        AFlow["/api/workflows/auth"]
    end

    %% Waitlist submission
    User --> WL --> BotID
    BotID -- bot --> Blocked
    BotID -- human --> RL
    RL -- exceeded --> Limited
    RL -- allowed --> ResendCheck
    ResendCheck -- yes --> ClerkSvc
    ResendCheck -- no --> Cache
    Cache -- cached invalid --> Rejected
    Cache -- cache miss --> ZB
    ZB -- invalid --> Rejected
    Cache -- cached valid --> ClerkSvc
    ZB -- valid --> ClerkSvc
    ClerkSvc --> SyncResend
    SyncResend -- "sync audience" --> ResendSvc

    %% Async webhook → workflow → email
    ClerkSvc -. "webhook events" .-> WHook
    WHook -. "forward" .-> QStashSvc
    QStashSvc -. "waitlist events" .-> WFlow
    QStashSvc -. "auth events" .-> AFlow
    WFlow -. "confirmation\ninvitation" .-> ResendSvc
    AFlow -. "OTP\npassword reset\npassword changed" .-> ResendSvc

    %% Auth entry & Middleware
    User --> Auth
    User --> SSO
    ClerkSvc -. "invitation link" .-> Inv
    Inv --> Auth
    Auth --> Mid
    SSO --> Mid
    Mid -- "signed in" --> App
    Mid -- "unauthenticated" --> WL
Loading

Running Locally

Follow these steps to set up and run Gitscoop arrival on your local machine.

Prerequisites

This application requires Node.js 24.11.1 and pnpm 11.2.2, both pinned via .nvmrc and devEngines in package.json. With pnpm 11+ already installed, pnpm install auto-enforces the exact versions for both.

Bootstrap (Node 22+ and pnpm 11+ required for the first install):

  • nvm: nvm install && nvm use, then npm install -g pnpm@11
  • Other: install Node 24.11.1, then npm install -g pnpm@11

Verify with node -vv24.11.1 and pnpm -v11.2.2 after running pnpm install.

Accounts on Clerk, Resend, Upstash, ZeroBounce, and ngrok for exposing local callback endpoints.

1. Cloning the Repository

git clone https://github.com/gitscoop/arrival.git
cd arrival

2. Installing Dependencies

pnpm install

3. Environment Variables

Create a local environment file by copying the example:

cp .env.example .env

Important

Open the newly created .env file and populate it with your specific service values. The file groups variables by service for convenience. Never commit your .env file.

4. Local URL and Ngrok Setup

Portless serves the local app at https://gitscoop.localhost. Ngrok provides the external routing needed for Clerk webhooks, Upstash workflows, and email redirects.

  1. Install ngrok and sign in.

  2. Grab your free dev domain from the ngrok dashboard (e.g. your-subdomain.ngrok-free.app).

  3. Set NEXT_PUBLIC_APP_URL in .env to that full origin, including https://.

  4. Portless runs the Next.js dev server on port 3000 so ngrok can target it reliably.

  5. Start the stable public tunnel in a separate terminal:

    ngrok http 3000 --url https://your-subdomain.ngrok-free.app

5. Services Configuration

Clerk

  • Waitlist Mode: In the dashboard, navigate to Waitlist → toggle on Enable waitlist.
  • Webhooks: Go to WebhooksAdd Endpoint. URL: https://your-subdomain.ngrok-free.app/api/webhooks/clerk. Subscribe to email.created, waitlistEntry.created, waitlistEntry.updated. Copy the Signing Secret to CLERK_WEBHOOK_SECRET in .env.
  • Disable Default Delivery: Under CustomizationEmails, toggle off Delivered by Clerk for verification_code, reset_password_code, and password_changed, because Gitscoop intercepts them.
  • Account Portal: Navigate to Account Portal and click Disable Account Portal to ensure all auth routes through custom Clerk Elements.
  • Keys: Set NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY in .env.

Resend

  • Add a sending domain (e.g., ops.yourdomain.com) and verify your DNS records.
  • Create a specific segment/audience for waitlist users. Set RESEND_SEGMENT_ID in .env.
  • Create your templates: Waitlist confirmation, Waitlist invitation, Verification code, Password reset code, and Password changed. Set their respective RESEND_*_TEMPLATE_IDs in .env.
  • Copy your API key to RESEND_API_KEY.

Upstash

  • Redis: Create a Redis database. Add UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN.
  • QStash: Navigate to the QStash tab. Copy QSTASH_TOKEN, QSTASH_CURRENT_SIGNING_KEY, and QSTASH_NEXT_SIGNING_KEY into your .env.

ZeroBounce

  • Grab your API key from the ZeroBounce dashboard and set ZEROBOUNCE_API_KEY.

6. Running the Application

Start the Next.js dev server via Portless once your ngrok tunnel is active in a separate terminal:

pnpm dev:portless

Note

Open https://gitscoop.localhost in your browser to access the local app. Running pnpm dev:portless automatically cleans up stale Portless processes. Use pnpm dev:prune to do a full cleanup manually.

Utility Scripts

Manage cached data and rate limits directly from your CLI:

Command Description
pnpm redis Interactive Redis CLI terminal
pnpm redis:clear:ratelimit Clear all active rate limit keys
pnpm redis:clear:emails Clear cached email validation results
pnpm redis:flush Flush the entire Redis database

Tech Stack

Primary tools and services powering the application:

Category Technology
Framework Next.js 16
Language TypeScript
Auth Clerk
Email Resend
Orchestration Upstash Workflow
Caching & RL Upstash Redis
Validation ZeroBounce
Analytics Vercel Analytics
Security Vercel BotID + FingerprintJS
Styling Tailwind CSS v4 + shadcn/ui
Local Dev URL Portless
Public Tunnel Ngrok