Conversation
Delegating, voting and publishing a proposal are settled on-chain by the wallet's own transaction signature, so they only ever needed a connected account. They were routed through the sign-in modal instead, forcing a SIWE message on anyone who just wanted to act on-chain. Connecting and signing in are now separate: - useWalletPrompt always opens the wallet picker, never the sign-in modal - LoginProvider no longer force-disconnects a wallet that has no session, and no longer re-opens the sign-in modal when the account changes - the header connect buttons (Anticapture and whitelabel) open the wallet picker directly - the sign-in modal, still raised by the surfaces that need a server session (proposal drafts, API keys), labels its wallet option "Sign in with wallet" Wallet-born sessions stay bound to their wallet: disconnecting or switching accounts still signs them out, it just never prompts again. Co-Authored-By: Claude Opus 5 <[email protected]>
Co-Authored-By: Claude Opus 5 <[email protected]>
…anges Switching accounts on a SIWE session fired signOut() and left the old session usable while the request was in flight, and indefinitely if it failed: the header showed wallet B while drafts and API keys still acted for wallet A. Sign-out is now the cleanup, not the gate. isWalletSessionStale derives the broken binding synchronously from wallet state, and useAuthSession serves the session through it, so a mismatched or disconnected wallet reads as no session from the render it happens. The API-key create and delete modals live outside the auth branch, so they close on the account change instead of submitting against the previous account. Co-Authored-By: Claude Opus 5 <[email protected]>
- serve before initial metrics refresh so slow Authful can't keep /health down - publish DB counts independently of Authful in metrics refresh - derive daily-active from usage records, not lastUsedAt (survives Gateful cache hits) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- export user_api_keys_created_total as a real counter incremented on creation instead of a mutable row count (cascade delete on account removal read as a counter reset / false increase() spike) - guard MetricsSnapshotService.refresh against overlapping runs so a slow Authful can't clobber a newer snapshot or pile up fetches Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
It never emitted a single railway_service_* sample in either environment — its account-level RAILWAY_API_KEY was rejected for the whole retained history (~15d), so the only visible effect was a permanently firing RailwayExporterStale critical alert and three blank Grafana panels. Drops the scrape job, the three Railway alert rules, the three dashboard panels, and the Dockerfile/railway.toml pair. Also removes the need for a workspace-wide Railway API token in service env vars, and a build-time git clone of a third-party repo. Railway's own dashboard still shows per-service CPU/RAM/egress; only alerting on them is gone. Co-Authored-By: Claude <[email protected]>
chore(monitoring): remove railway-exporter
Save-key modal: default width on both steps, no key name or em-dash in the description, 'Key' and 'MCP' titles at the same font size, no 'waiting for the first call' status, and animated code-block height when switching client tabs. Keys table truncates long names with an ellipsis. Usage section: key switcher is a dropdown with a max width, blank slate for the empty state, and reduced height. Connect section retitled 'MCP' with 'connect your AI agent' in the description. Modal close button uses the small icon-button size. ClickUp: 86ajr888u Co-Authored-By: Claude Fable 5 <[email protected]>
…erflow Long API key names are truncated to 24 chars in the usage chart series (tooltip and legend), with the id suffix disambiguating names that collapse to the same truncated label. The stacked-bar-chart legend now scrolls instead of wrapping, so many/long series names no longer overflow into the x-axis on narrow screens. ClickUp: 86ajr888u Co-Authored-By: Claude Fable 5 <[email protected]>
User token metrics
feat(dashboard): connect wallet without signing in
Returning DEFEATED conflated "nobody showed up" with "voted down" - the enum and full ProposalItem styling for NO_QUORUM already exist.
Every caller of getProposalStatus/getProposalState feeds it the onchain API's proposal.status, which never emits "passed" - only the offchain path constructs ProposalStatus.PASSED directly.
The quorum row in ProposalInfoSection never appeared because the Snapshot proposal was adapted with a hardcoded quorum of "0", even though the API now indexes and exposes the real value.
The reconcile comment implied deletion was bounded to the 14-day window, but backfillProposalMetadata walks and can delete proposals across all history. Document that explicitly instead of hiding it behind the word "backfill".
"Succeeded" was wrong - the code returns PASSED specifically to avoid the on-chain "Pending Queue"/"succeeded" semantics for Snapshot proposals.
Fix Uniswap offchain proposal status
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
🚅 Deployed to the anticapture-pr-2093 environment in anticapture-infra
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Revoked keys skew age buckets
- Filtered revoked keys out of keysForActiveTokenIds so active user age buckets use only live keys' createdAt.
Or push these changes by commenting:
@cursor push a7e8999e9f
You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 6b63c61. Configure here.
| } | ||
| const activeUsers = emptyActiveUsers(); | ||
| for (const createdAt of newestKeyByUser.values()) { | ||
| activeUsers[ageBucket(now.getTime() - createdAt.getTime())] += 1; |
There was a problem hiding this comment.
Revoked keys skew age buckets
Low Severity
user_api_active_users age buckets use each user’s newest API key by createdAt, but keysForActiveTokenIds returns revoked keys too. A user active on an older key can be bucketed by a newer revoked key’s age, so retention metrics misstate who is active today.
Reviewed by Cursor Bugbot for commit 6b63c61. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b63c611bf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .where( | ||
| and( | ||
| like(tokens.tenant, "user:%"), | ||
| gte(tokenUsageDaily.day, utcDay(since)), |
There was a problem hiding this comment.
Preserve the São Paulo activity boundary
When User API requests activity since São Paulo midnight (03:00 UTC), utcDay(since) truncates that instant to the UTC calendar date. Because usage is stored in UTC-day buckets, the query also includes requests from 00:00–02:59 UTC, which belong to the previous São Paulo day; users active only during that interval are therefore reported as active today and their usage is overcounted. The stored bucket timezone must match the dashboard day boundary, or the data needs enough time precision to filter from the requested instant.
Useful? React with 👍 / 👎.
🎨 UI ReviewScope: UI-only pass over Path A — code-only + UX-heuristic review. No Wallet/session decouplingNo issues found. Snapshot / offchain governance statusStatus derivation ( API keys UX
Design system / a11y
Summary0 must-fix, 3 nice-to-have (all minor UX/DS-consistency notes, no functional blockers found). No Figma spec or ClickUp task was located for this PR, so nothing could be validated against a source-of-truth design — the notes above are heuristic-only. Generated by Claude Code |
|
🔍 Vercel preview: https://anticapture-ltvwi5shu-ful.vercel.app |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d547dc5ae
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if ( | ||
| data.AUTHFUL_URL && | ||
| data.AUTHFUL_PROVISIONING_API_KEY && | ||
| !data.USER_API_METRICS_TOKEN | ||
| ) { |
There was a problem hiding this comment.
Provision the metrics token before making it startup-critical
Any existing environment with AUTHFUL_URL and AUTHFUL_PROVISIONING_API_KEY configured will now reject its environment and fail to start unless USER_API_METRICS_TOKEN is added during the same rollout. The tracked apps/user-api/.env.example and README still describe the Authful pair as sufficient, so following the repository's documented API-key configuration also produces an unbootable service; document and provision the new variable before enforcing this dependency.
Useful? React with 👍 / 👎.



Note
High Risk
Wallet/session decoupling and immediate stale-session gating change core auth UX and security boundaries; per-user Prometheus labels carry PII and require protected metrics and Grafana access.
Overview
This PR bundles several product and platform changes across the dashboard, offchain indexer, authful, user-api, and monitoring.
Wallet vs session: Connecting a wallet no longer implies signing in. Vote, delegate, and publish flows open RainbowKit directly without SIWE; header Connect opens the wallet picker instead of the login modal. Session-gated surfaces (drafts, API keys) still use the sign-in modal.
useAuthSessionandisWalletSessionStalehide wallet-born sessions immediately when the wallet disconnects or the account switches, instead of waiting on sign-out or force-disconnecting connected wallets without a session.Snapshot / offchain governance: The indexer and API persist
scoresTotalandquorum, with a historical metadata backfill job. The dashboard derives Passed, No quorum, and non-stale Ongoing states instead of mapping winning Snapshot proposals to on-chain-style Executed / queue labels.API keys UX: Save-key modal layout, MCP section copy, animated client tabs, truncated names, usage dropdown/BlankSlate, shorter chart, and scrollable chart legend on mobile.
Validation metrics: Authful adds
GET /tokens/active(usage from daily counters). User API exposes bearer-protected/metricswith per-user gauges (email/wallet identifiers), a Grafana User API Validation dashboard, andUSER_API_METRICS_TOKENrequired when Authful provisioning is enabled.Monitoring: Removes the non-functional railway-exporter (scrape job, alerts, panels). Extends Prometheus retention; PR Grafana can use basic auth instead of Google OAuth.
Reviewed by Cursor Bugbot for commit 6b63c61. Configure here.