My dent on the world wide web. A portfolio and blog, built as a pnpm + Turborepo monorepo combining a Next.js app, MDX compilation pipeline, visitor analytics, ad hoc tools, visitor analytics, and integrations for LLM agents and communication channels.
- Live site: www.claycurry.studio
- Repository: github.com/claycurry/claycurry.studio
- Main app: Next.js 16 + React 19 + TypeScript at
apps/www - Build system: Turborepo with pnpm workspaces
- Content: MDX blog posts loaded from
apps/www/blog - Data layer: Redis-backed counters (dev container provides Redis automatically)
Open the repo in VS Code with the Dev Containers extension or in GitHub Codespaces. The dev container automatically:
- Installs Node.js 22 and enables corepack/pnpm
- Starts a Redis server on port 6379
- Sets
KV_REST_API_REDIS_URL=redis://localhost:6379viacontainerEnv
Once the container is ready:
pnpm devLocal app URL: http://localhost:3000
- Node.js 20+ (CI uses Node 20)
- Corepack enabled (
corepack enable) - pnpm 9.15.0 (from
packageManagerin rootpackage.json) - Redis running locally on port 6379
corepack enable
pnpm installCreate apps/www/.env.local:
KV_REST_API_REDIS_URL="redis://localhost:6379"pnpm devLocal app URL: http://localhost:3000
| Purpose | Command |
|---|---|
| Start all dev tasks | pnpm dev |
| Start only website app | pnpm dev --filter www |
| Build all packages | pnpm build |
| Build website app only | pnpm build --filter www |
| Run all checks | pnpm check |
| Run website checks only | pnpm check --filter www |
| Run TypeScript checks | pnpm check-types |
| Run website type checks only | pnpm check-types --filter www |
| Run tests | pnpm test |
| Run link checks | pnpm check-links |
| Validate social metadata rules | pnpm --filter www check-social-metadata |
| Variable | Required locally | Purpose |
|---|---|---|
KV_REST_API_REDIS_URL |
Yes (auto-set by dev container) | Redis connection URL for click counts and page views. |
| Variable | Required for | Purpose |
|---|---|---|
RESEND_API_KEY |
Contact + feedback forms | Sends messages from /api/contact and /api/feedback. |
GITHUB_TOKEN |
Higher GitHub API limits in chat | Adds authenticated GitHub requests for portfolio chat context. |
AI_GATEWAY_API_KEY |
AI chat | Authenticates model requests through AI Gateway in /api/chat. |
ANTHROPIC_API_KEY |
AI model fallback | Optional direct provider fallback support. |
OPENAI_API_KEY |
AI model fallback | Optional direct provider fallback support. |
XAI_API_KEY |
AI model selection | Optional model/provider support. |
PERPLEXITY_API_KEY |
Web search tool in chat | Enables search tool usage when web search is turned on in chat. |
X_OWNER_USERNAME |
X bookmarks sync | Required. Canonical bookmark owner username. |
X_OWNER_USER_ID |
X bookmarks sync | Deprecated compatibility field. If set, it must resolve to the same account as X_OWNER_USERNAME. |
X_OAUTH2_CLIENT_ID |
X OAuth | Canonical OAuth client identifier for token exchange/refresh. |
X_OAUTH2_CLIENT_SECRET |
X OAuth | Canonical OAuth client secret for token exchange/refresh. |
X_OWNER_SECRET |
X OAuth bootstrap route | Shared secret gate for /api/x/auth. |
Deployment note:
VERCEL_ENVis provided by Vercel and used to namespace Redis keys (prod:,preview:,dev:).
- Endpoint:
apps/www/app/api/chat/route.ts - Streams responses via AI SDK + gateway model routing
- Supports optional web search tooling and blog-aware Q&A context
- Endpoints:
apps/www/app/api/clicks/route.ts,apps/www/app/api/views/route.ts - Uses Redis for persistent counters (requires
KV_REST_API_REDIS_URL) - Redis keys are prefixed by environment to avoid cross-environment collisions
- Endpoints:
apps/www/app/api/x/* - Supports OAuth token exchange, owner validation for the configured
X_OWNER_USERNAME, and bookmark retrieval - Returns stale snapshots instead of blank data when live sync fails and a prior snapshot exists
- Exposes a minimal operator status route at
/api/x/bookmarks/status?secret=... - Requires
X_OAUTH2_CLIENT_IDandX_OAUTH2_CLIENT_SECRET— shows a clear error when credentials are missing or legacy names are still in use
- Endpoints:
apps/www/app/api/contact/route.ts,apps/www/app/api/feedback/route.ts - Uses Resend API for contact and page feedback workflows
- Portfolio sections for About, Resume, Work, Writing, and Contact
- MDX blog with metadata-driven routing and dynamic post pages
- Ask-AI chat experience with source and reasoning streaming
- Click-count overlays and page view counters
- X bookmarks explorer with folder and search support
- Short-link redirects (e.g.
/x,/l,/g) for referral attribution
.
├─ apps/
│ └─ www/ # Next.js portfolio/blog app
├─ packages/
│ ├─ link-checker/ # Link validation utilities/scripts
│ └─ www/ # Legacy/experimental files (not a workspace package)
├─ scripts/
│ └─ setup-skills.sh # Postinstall skill bootstrap (skipped in CI/Vercel)
├─ turbo.json # Task graph and cache config
├─ pnpm-workspace.yaml # Workspace package globs
└─ package.json # Root scripts delegating to turbo
Note: packages/www currently has no package.json, so it is not an active pnpm workspace package.
- App routing uses Next.js App Router with route groups under
apps/www/app. - Blog content is sourced from
apps/www/blog/*.mdxand loaded throughapps/www/app/(portfolio)/blog/loader.ts. - MDX processing is configured in
apps/www/next.config.mjs(GFM, math, TOC, pretty-code). - Turbo tasks are defined in
turbo.json(dev,build,check,check-types,test,check-links). - Social metadata guardrails are enforced by
apps/www/scripts/check-social-metadata.mjs. - Redis helpers and key-prefix logic are centralized in
apps/www/lib/redis.ts.
- Production URL: https://www.claycurry.studio
- Deployed on Vercel
- Ensure required environment variables are set in Vercel Project Settings
- Keep Vercel system environment variables enabled so
VERCEL_ENVis available at runtime
- Create a branch and open a PR targeting
main. - Run checks before opening/updating the PR:
pnpm check --filter wwwpnpm check-types --filter wwwpnpm --filter www check-social-metadata
- Update
CHANGELOG.mdfor user-facing changes. - Follow conventional commit messages (validated in CI).
CI enforces merge criteria via .github/workflows/merge-criteria.yml, including changelog and commit checks.
# Created by Vercel CLI
AI_GATEWAY_API_KEY=
ANTHROPIC_API_KEY=
APPLE_DEV_TEAM_ID=
KV_REST_API_REDIS_URL=
RESEND_API_KEY=
VERCEL_OIDC_TOKEN=
# App-Only Authentication
#
# Bearer Token authenticates requests on behalf of your developer App.
X_BEARER_TOKEN=
# OAuth 1.0 Keys
#
# Consumer Key
# Think of these as the user name and password that represents your App when
# making API requests.
#
# While your Secret will remain permanently hidden, you can always view the
# last 6 characters of your Consumer Key.
# An Access Token and Secret are user-specific credentials used to authenticate
# OAuth 1.0 API requests. They specify the X account the request is made on
# behalf of.
X_OWNER_OAUTH1_ACCESS_TOKEN=
X_OWNER_OAUTH1_ACCESS_TOKEN_SECRET=
# OAuth 2.0 Keys
# Client ID
# Think of your Client ID as the user name that allows you to use OAuth 2.0 as
# an authentication method.
X_OAUTH2_CLIENT_ID=
# Client Secret
# Think of your Client Secret as the password that allows you to use OAuth 2.0
# as an authentication method.
X_OAUTH2_CLIENT_SECRET=
X_OWNER_USERNAME=your_x_username
X_OWNER_SECRET=
X_OWNER_USER_ID=
This project is licensed under the WTFPL. See LICENSE.md.
