Conversation
10-dimension review with adversarial verification: 62 findings, 36 confirmed. Records the executable prompt, per-finding evidence/verdicts, and the disjoint-ownership implementation plan (34 fixes across 11 tracks, 2 deferred). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Validate .well-known handles as hostnames and gate them through isAllowedPdsUrl before fetch (closes unauthenticated SSRF where an IP/host:port handle reached the server fetch). - Reject non-canonical IPv4 literals (decimal/hex/octal/short-form) via a glibc inet_aton-matching parser; add 0.0.0.0/8 and 100.64.0.0/10 (CGNAT) to the blocklist. - Set redirect:"error" on all upstream PDS/appview fetches (xrpc proxy get/list/getBlob, getCertsProfile, getRecordServer, DID-doc, createSession) so an allowed host cannot 30x the server onto an internal target. - Add an IP-scoped rate limiter to the unauthenticated xrpc GET proxy. - Cap the foreign getBlob stream by counting bytes, independent of the declared Content-Length. - Pure-JS IP validation (no node:net) since @/lib/atproto/did is imported by client modules. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… relay - Group-account unlock now consults a per-target (groupDid) limiter alongside the per-caller one and denies if either trips, so an attacker with many caller DIDs can no longer multiply password guesses against one victim account. Fail-closed behavior preserved. - Feedback route no longer emails attacker-controlled content to an attacker-supplied recipient; only the internal team notification remains, with the submitter address used solely as Reply-To. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…adata, tighten robots - Scope X-Frame-Options: DENY / CSP frame-ancestors 'none' to non-embed routes and allow framing on /embed/* so the shipped third-party board embed works again. - Convert root / to a Server Component that reads the session cookie and redirects (307) to /home or /welcome, removing the client-bundle load + spinner flash. - Wrap OG generateMetadata upstream fetches in unstable_cache (revalidate 300s) to cut repeated crawler/share-scraper TTFB. - Disallow /workspace, /endorsement-graph, /profile in robots. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…activity rows
- Wrap the TipTap-based LeafletEditor (~9.7 MB dep tree) in next/dynamic({ssr:false}) and route all 8 import sites through the wrapper, removing it from read-heavy first-load bundles (profile, activity-detail, project-detail); it loads only on entering edit mode.
- Lazy-load the d3-hierarchy contributor board (ActivityFancyBoard) so it loads only when the Contributors tab opens.
- Memoize weight-percent maps and contributor rows in activity-detail so read-only rows don't re-render on edit keystrokes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…arks; dedupe truncateDid - useMemo the explore sorted lists and wrap the result rows (cert/account/project/user/project-card/funding) in React.memo so unchanged rows bail on each search keystroke. - Memoize profile endorsement cards and drop the duplicate in-grid filter/sort. - Change the nested inner page-level <main> in explore (x2) and home to <div> (class-only CSS selectors, so the >=1300px baseline is unchanged) - fixes duplicate main landmarks (WCAG 1.3.1). - Replace the two triplicated local truncateDid copies in explore-page with the shared helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Split the monolithic NavbarContext into a memoized setters context and a values context, so setter-only consumers (usePageTitle callers across pages) stop re-rendering when navbar values change. Public hook API is unchanged. - Render the page-title chrome as an <h1> so primary pages have a programmatic heading (WCAG 1.3.1 / 2.4.6); mobile and desktop bars are mutually display:none so only one h1 is ever in the a11y tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…session on HTTP error - Home-feed loadMore now runs under an AbortController/generation guard so a filter toggle mid-pagination no longer appends stale, filter-violating events. - useUserIndexerActivities de-dupes by URI when merging pages, eliminating duplicate rows and duplicate React keys. - useSession no longer permanently caches a null handle/email on a non-OK getSession response; it resets so the next mount refetches. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…esize storm - useProjectItems resolves item URIs through the indexer's batch-by-URI query (chunks of 50), falling back to per-URI getRecord only for URIs the indexer reports missing - turning an N-request fan-out on project-detail into ~1. - useLayoutBreakpoints guards its setter with an equality check so unchanged breakpoints during a resize drag no longer re-render every consumer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t scrim tokens - Swap hardcoded rgba hover/tint/shadow literals for the existing theme-aware tokens (--overlay-weak/-medium, --color-success-bg/-error-bg/-error-border, --shadow-md, --navbar-bg) so hover feedback and status tints flip correctly in dark mode. - Add invariant --scrim-pill/-hover/-ghost tokens and reference them from the 8 duplicated image-overlay pill scrim sites. - Delete the hand-written leaflet-attribution dark override now that the token flips. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Delete the never-imported activity-contributor-board component and four unused exports (GuillocheMeshQuiet, fetchContributorInformation, scopeKey, clearTourCompleted), each verified to have zero references. - Replace the workspace-pane local truncateDid copy with the shared helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion, upload-blob limits Add regression tests for previously-untested security-adjacent routes: group password-reset cross-account verify (409 on identity mismatch), onboarding clone-blob host-allowlist + redirect:error SSRF guards, members anti-escalation (cannot add a member directly as owner), and upload-blob content-type allowlist + size cap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…indings, track plan 73 confirmed findings across 9 review dimensions plus an exhaustive triage of all 63 lint warnings; every finding adversarially verified (2 skeptics for high severity). Implementation plan: 11 parallel disjoint-ownership tracks + 2 sequential cross-cutting tracks, nothing deferred. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hase 0/1.5 Two plan reviewers (ownership/coupling, sequencing/risk). Accepted: app-dialog to CSS track, finding 9/15 dedup, ma-earth split across T7 + phase 1.5 with PDS fallback, phase-0 shared helpers, lint gate restated (<=2 warnings, 0 unused-disable), 5 new tests, T7/T9 commit splits, 7 file-list corrections. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
postIndexer: single rich-result wrapper ({ok,status,data,errors}) for the
hand-rolled POST /api/indexer blocks; never throws on HTTP errors so each
call site keeps its exact throw/fail-soft/warn semantics when migrated.
deriveIdentity: canonical displayName/handle/initials/href/avatar
derivation with record-level overrides, replacing ~20 drifting copies.
Phase 0 of docs/perf-quality-2026-07-12/plan.md; call-site migration
follows in later tracks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
HomeFeedRow/EndorsementGroupRow/HomeFeedBody memoized (element-wise group comparator per review); grouped-endorsement expansion windows 50 accounts per click instead of mounting up to ~1000; row/sentence/preview layer moved verbatim to home-feed-rows.tsx (CertPreview re-exported for tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…chunks Re-enable autoPauseRedraw with a repaintEpoch bumped on image preload and theme flips (paintNode/linkColor deps); force config no longer re-applies and reheats on container resize; NetworkActorsByDids chunks fetch via Promise.all; AllEndorsements loader switched to the cacheable indexer GET contract (op/badgeType/first/after). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, router staleTimes; metadata fixes Profile route loads SettingsPanel/ProfileEndorsements/ProfileFollowers/ ProfileLists via next/dynamic (OrgSettings likewise inside settings-panel); landing network stats server-fetched in the ISR render via network-counts-server.ts (fail-soft, 5s timeout) with the client hook as null-field fallback; experimental.staleTimes.dynamic=30; double-Certified title suffixes fixed (workspace/explore/endorsement-graph); dead /about sitemap entry removed; /apps converted to a server component with an SsoAppLink island; /home and /project/new gain titles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r bodies at boundaries coerceClaimActivityValue normalizes malformed PDS records before render in use-activity and use-project-items; sessionStorage active-org restore is shape-validated; /api/auth/login non-JSON error bodies no longer surface raw SyntaxError to the sign-in form. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… CGS resolution, collapse hyperboard waterfall createCachedDidResource factory (module cache + stale window + abort-safe fetch + refetch) adopted by use-followers/use-following; in-flight coalescers added to use-received-endorsements, use-endorsements, fetchDisplayProfile/fetchBoardForActivity; use-endorsement-lists moves from permanent force-refetch to a versioned cache bus; use-cgs-memberships resolves groups through the batched DID resolver; useHyperboard fetches displayProfiles per-identity instead of behind a global barrier; rewritten fetchers ride postIndexer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ing components; drop dead draft plumbing useContextUpdates gains a 30s shared cache + in-flight dedupe (invalidated from UpdateForm saves), halving duplicate fetches on activity/project detail; useRouteRkey derives during render instead of a post-mount effect (kills a guaranteed second render of the 2300-line component); trailing component cluster moved to cert-detail-parts.tsx; contributorKey/ contributionRoleText deduped into lib/atproto/contributor-display.ts; never-read swap-draft writes removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…stable callbacks; split below-fold + modals EndorsementSubjectRow (on deriveIdentity, info+isLoading props) replaces three drifted row copies; ProfileEndorsements calls useGivenEndorsements once and derives the endorsed-set from it; row onToggle handlers are useCallback-stable so list memos hold; below-fold views split to profile-endorsement-views.tsx and the three list modals to their own module. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…loaders/filters/results ExploreSearchField owns keystroke state + 350ms debounce (was re-rendering the full results tree per keypress, twice over via the copy-pasted block in the All view); All-view blocks fetch 2x block size instead of 4x50 for 5 rendered items (funding keeps the full window for the client-side Confirmed-by filter); use-explore.ts loader layer split to use-explore-loaders.ts; quality filters and results area extracted; explore.tsx 2131 -> ~1480 lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…modules OPERATIONS/validation moved to operations.ts + variables.ts (pure module split); new GET handler for an explicit CACHEABLE_OPS allowlist (five zero-variable counts s-maxage=300 swr=86400; AllEndorsements pages s-maxage=60 swr=600; no cache headers on upstream errors); CollectionsByUris batch op added for the Ma Earth loader; allowlist + cache-header tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ound-trips in xrpc proxy Foreign-repo/blob GETs no longer restore the caller's OAuth session (hottest read path did a full token restore per request); immutable CID-addressed blobs get s-maxage (foreign) and private max-age (own-repo); rate-limit INCR and session lookup run concurrently; lazy-restore regression tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…he headers Group-registration limit check now fails closed on CGS errors and stops walking member lists once the limit is provably unreachable; direct CGS fetches carry abort timeouts; public group profile/metadata GETs get short private cache headers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54x set-state-in-effect + 2x exhaustive-deps + 6x no-img-element triaged per docs/perf-quality-2026-07-12/findings.md: fetch-reset effects now compare previous keys inline, prop-mirrors derive during render, event work moved to handlers, mounted/matchMedia flags ride useSyncExternalStore; 20 sites where the pattern is deliberately correct carry justified eslint-disable comments. Repo lint output: 63 warnings -> 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
use-display-profile / use-pending-awards-count / use-user-activities (zero production importers) removed with their test files; dead exports dropped from account-email, app-passwords, urls, activity-uri; parseLocationCoords de-exported; stale doc references fixed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iewport shrink Onboarding modal remounts per open via key so draft seeding is initialization instead of a set-state-in-effect (also stops the bsky-graph sync mounting on every page while closed); tour context exposes a clamped stepIndex and clamps back() so a mid-tour resize across 800px cannot strand the step index past the shorter steps array; covered by new tour-context tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…contain long lists All literal z-index rules mapped onto the --z-* scale (stacking table in findings.md; new --z-local-raise + --z-skip-nav tokens); error focus ring and status colors tokenized (dark mode now legible); --shadow-modal token replaces three literal copies; 2px radius literals -> var(--radius); ~640 lines of orphaned CSS removed (live compound-selector partners preserved); content-visibility + contain-intrinsic-size on home-feed and explore lists; app-dialog latest-ref writes moved into an effect (react-hooks/refs errors surfaced when the stale disable directive was removed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…back fetchIndexerProjectsByUris batches the curated-URI set through the new CollectionsByUris proxy op (50-URI chunks, postIndexer, ok flag instead of throws); the Ma Earth loader tries the batch, restores curator order, and falls back to the untouched per-URI PDS path on error or empty-but-expected results — shippable whether or not the deployed magic-indexer supports the uri-in filter yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…b, surface HTTP errors
19 remaining hand-rolled POST /api/indexer blocks migrated onto postIndexer
with per-site semantics preserved; fetchCount rides the cacheable GET
variant (dev mock-fetch-provider taught the GET form); indexer.ts split
into indexer-{funding,closure,counts,collections}.ts behind a re-export
barrel (1558 -> 597 lines, 30+ importers unchanged);
fetchIndexerActivities/ByUris now throw on HTTP !ok instead of rendering
an empty feed, keeping warn+fail-soft only for 200-with-GraphQL-errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Image helpers replace 50+ duplicated sites 16 more identity-fallback sites ride deriveIdentity (record-level overrides via the options bag; unresolved DIDs now uniformly truncated instead of raw 40-char strings or 'Anonymous'); rkeyFromUri joins parseAtUri in lib/urls (4 private extractRkey, 2 private parseAtUri, 16 inline split-pop sites removed); formatTimePeriod shared across the four date-range copies; slot-aware projectTitle/projectImage in collection.ts fix the confirmed home-rail thumbnail precedence drift while keeping banner-first hero slots; 14 new unit tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…taleness; round-2 cleanups Object.hasOwn guards on the indexer op lookups (prototype keys die at the first gate, pinned by tests); shared indexer plumbing moved to a leaf indexer-client.ts so the domain split is acyclic; ExploreSearchField debounce reads latest onCommit/search via effect-synced refs (could previously revert a URL change made mid-window); repaintEpoch bumps coalesced to one per frame; explore content-visibility scoped to the list variants where containment can apply; CertPreview compat re-export dropped; stale operations.ts comment pointers, suppression justifications, and zero-consumer exports cleaned up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cross-repo follow-ups Live probing of the prod indexer showed the three recorded magic-indexer follow-ups need no upstream work: uri-in filtering already works (real-URI probe returns the node), avatar is already in the lexicon-built schema (the nulls mirror the PDS records, verified for 30), and authorLabels validates on the same connection. The genuine gap was app-side: Projects/UserProjects/ ProjectsContainingCert never selected avatar — now they do, so projectImage's avatar-first thumb precedence reaches indexer-fed surfaces. Evidence in docs/perf-quality-2026-07-12/followups-probe.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Performance & code-quality pass 2026-07-12: 84 verified findings, lint 63 to 0, nothing deferred
…-indexer#273 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prefer PUBLIC_URL for OAuth callbacks, then VERCEL_BRANCH_URL and VERCEL_URL. Allow same-origin CSRF requests on each exact configured deployment origin without making those origins cross-origin peers. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Add the existing npm build command to the pull-request verification job after tests. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: OpenAI Codex <noreply@openai.com>
auth: support Vercel preview origins
ci: run production build in pull requests
Posting an activity update 403'd with "collection is required and must be an allowed collection". An update is stored as an org.hypercerts.context.attachment record (contentType: "update" — there is no separate update lexicon), and that NSID was never added to ALLOWED_WRITE_COLLECTIONS. The gate covers all three REPO_METHODS, so create, edit, and delete of own-repo updates were all broken. Group-owned updates worked, because they route through /api/groups/[groupDid]/update, which allowlists the NSID itself. Not a recent regression: the allowlist has held the same entries since 3b37f3e introduced it, and this NSID never appeared in it. AGENTS.md carries it as known pitfall #5. Also fixes the blocker directly behind it. UpdateForm asked for allowAnyType so it could attach PDFs, but that flag only skipped the client-side check — both upload routes independently enforce an image-only MIME allowlist and 415'd. Adds an opt-in document set, selected by ?purpose=attachment, additive over each route's existing image list so a PDF still cannot land as an avatar or banner. text/html and image/svg+xml stay out of both: blobs are served back under the PDS origin, so either is a stored-XSS vector there. Each route keeps its own image allowlist and size cap unchanged. The client attachment cap drops from 25MB to 4MB — Vercel rejects serverless bodies over ~4.5MB, so the old ceiling only ever bought a failed round-trip — and the file picker gains a matching accept attribute. Tests: allowed-collections pins every written collection against the gate plus the app.bsky.actor.profile invariant; upload-blob-types and the group route cover the purpose switch and the html/svg exclusions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedToo many files! This PR contains 262 files, which is 162 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (262)
You can disable this status message by setting the 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. Comment |
Rolls up 45 commits on
staging. The bulk is a performance and code-quality pass (#237), with security hardening, an auth/CI change, and one user-facing bug fix.Bug fix
org.hypercerts.context.attachmentrecord, and that NSID was never in the XRPC proxy'sALLOWED_WRITE_COLLECTIONS— so create, edit, and delete of own-repo updates were all broken. Group-owned updates were unaffected (different route, which allowlisted the NSID itself). Broken since the allowlist was introduced, not a recent regression.allowAnyTypeonly skipped the client-side check, so PDFs 415'd at both upload routes. Documents are now an opt-in set selected by?purpose=attachment, additive over each route's existing image list — a PDF still cannot land as an avatar.text/htmlandimage/svg+xmlstay excluded from both, since blobs are served under the PDS origin.Performance (#237)
Lazy-loaded profile tabs, settings, the TipTap editor, and the contributor board. Memoized feed, explore, and endorsement rows. Single-flight caches for endorsement and follow hooks; batched project fetches through the indexer with PDS fallback; collapsed the hyperboard waterfall; parallel Upstash round-trips in the xrpc proxy; graph redraw paused when idle. Server-rendered landing stats and router
staleTimes.Security
SSRF filter and PDS proxy redirect/limit hardening, per-target unlock throttle, feedback email relay removed, org-limit fail-open closed, CGS timeouts. New test coverage for password-reset, clone-blob SSRF, members escalation, and upload-blob limits.
Refactors and cleanup
Shared helpers (
deriveIdentity,rkeyFromUri,formatTimePeriod,projectImage) replacing 50+ duplicated call sites;postIndexerunified across indexer calls with a domain-split lib; dead hooks, exports, and orphaned tests removed; the react-hooks lint warning backlog resolved across 36 files; type validation added at record, storage, and auth-error boundaries.CSS and tokens
Z-index, shadows, and status colors tokenized; raw
rgbareplaced with theme-aware tokens; invariant scrim tokens added.Auth and CI
Vercel preview origin support with shared loopback host validation; production build now runs in PRs with an ephemeral cookie secret.
Verification
npx tsc --noEmitclean,npm run lintsilent, 1235/1235 tests passing locally.Known follow-ups (not in this PR)
{ error: "Upstream PDS returned 400" }for publicgetRecord, destroying theRecordNotFounddiscriminator thatuse-org-marker.tschecks for. The negative-cache write never fires, so every profile mount re-issues the request.🤖 Generated with Claude Code