Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4b858c4
fix(server): log redis errors in rate limit record_in_window
Ashwin-3cS Apr 2, 2026
94a8cc2
fix(sidecar): limit getDynamicField concurrency to 5 + retry on 429
ducnmm Apr 2, 2026
5bb1669
Merge pull request #75 from Ashwin-3cS/fix/rate-limit-silent-redis-error
ducnmm Apr 2, 2026
509254e
Merge pull request #78 from MystenLabs/fix/query-blobs-rate-limit
Aaron1924 Apr 3, 2026
357bc6b
fix(security): resolve 4 audit findings — sidecar auth, rate-limit f…
hien-p Apr 8, 2026
de0ae48
fix(security): rename SIDECAR_SECRET→SIDECAR_AUTH_TOKEN, use Authoriz…
hien-p Apr 8, 2026
3bc98b6
fix(security): complete HIGH-1 and HIGH-4 wiring
hien-p Apr 9, 2026
c51f35e
test(security): use env vars for delegate key in rate limit test
hien-p Apr 9, 2026
0579eee
Feat: Add Enoki zkLogin and profile page to researcher app (#79)
hungtranphamminh Apr 9, 2026
21aa609
fix(auth): gate /sponsor with Ed25519-only auth, fix
hien-p Apr 9, 2026
81bad00
fix(docker): pass NEXT_PUBLIC env vars as build args for Enoki login …
hungtranphamminh Apr 9, 2026
deb316b
Feat: Add Enoki login and clean up noter app (#82)
hungtranphamminh Apr 9, 2026
e02ad0b
revert(security): defer HIGH-4 to follow-up PR, keep HIGH-1/2/8
hien-p Apr 9, 2026
42d6e0a
Merge pull request #84 from MystenLabs/fix/security-p0-four-issues
ducnmm Apr 9, 2026
79efef2
chore(deps): update pnpm-lock.yaml for noter enoki dependencies (#86)
hungtranphamminh Apr 9, 2026
ef7dfae
chore(noter): trigger Railway deploy for lockfile sync (#88)
hungtranphamminh Apr 9, 2026
998af42
Feat: Replace setup wizard with inline Enoki login flow (#81)
hungtranphamminh Apr 9, 2026
ff72a8f
Fix: Prevent wallet connect modal from closing prematurely (#89)
hungtranphamminh Apr 10, 2026
bee09b6
fix(noter): disconnect Enoki wallet on logout to prevent autoConnect …
hungtranphamminh Apr 10, 2026
85379cc
Revert "Merge pull request #84 from MystenLabs/fix/security-p0-four-i…
ducnmm Apr 10, 2026
bf7fa0c
Merge pull request #93 from MystenLabs/revert/pr-84-security-p0
harrymove-ctrl Apr 13, 2026
a1a0639
feat: pass delegate_public_key to walrus metadata on upload
ducnmm Apr 14, 2026
1aafba4
docs: clarify self-hosting personas, namespace isolation, and rate po…
ducnmm Apr 14, 2026
61c77f0
Merge pull request #101 from MystenLabs/feature/walrus-delegate-key-m…
harrymove-ctrl Apr 14, 2026
00b17ad
Merge pull request #102 from MystenLabs/docs/rate-limiting-personas
harrymove-ctrl Apr 14, 2026
05ab676
fix: remove sidecar_secret logic not yet in dev
ducnmm Apr 14, 2026
5e7c20c
Merge pull request #103 from MystenLabs/feature/walrus-delegate-key-m…
harrymove-ctrl Apr 14, 2026
25cf1e5
feat: rename memwal_delegate_key to memwal_agent_id
ducnmm Apr 14, 2026
17c2eac
Merge pull request #108 from MystenLabs/feature/rename-delegate-to-ag…
ducnmm Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions apps/app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
# To switch networks: uncomment one block, comment out the other.
# ══════════════════════════════════════════════════════════════

# Enoki API Key (get from https://portal.enoki.mystenlabs.com)
VITE_ENOKI_API_KEY=
# Enoki zkLogin — enables "Sign in with Google"
# Get Enoki API key from https://portal.enoki.mystenlabs.com
VITE_ENOKI_API_KEY=enoki_public_9aac56cf5c1e5b1254d1fa09bb6e9f0c

# Google OAuth Client ID (from Google Cloud Console)
VITE_GOOGLE_CLIENT_ID=
VITE_GOOGLE_CLIENT_ID=386692102434-pn0enkrr12r5q3arflsfrrvb14rvhs10.apps.googleusercontent.com

# MemWal Server URL
VITE_MEMWAL_SERVER_URL=http://localhost:8000
# MemWal Server URL (also handles /sponsor and /sponsor/execute for gasless tx)
VITE_MEMWAL_SERVER_URL=https://relayer.dev.memwal.ai

# Docs URL (separate deployment)
VITE_DOCS_URL=http://localhost:5174
Expand Down
33 changes: 33 additions & 0 deletions apps/app/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,39 @@ h1, h2, h3 {
box-shadow: 5px 5px 0 #000000 !important;
}

/* ── Login popover: ConnectButton full-width + matching style ── */

.lp-login-wallet-btn {
width: 100%;
}

.lp-login-wallet-btn [class*="ConnectButton"],
.lp-login-wallet-btn div {
width: 100% !important;
}

.lp-login-wallet-btn [class*="ConnectButton"] button,
.lp-login-wallet-btn button[data-dapp-kit] {
width: 100% !important;
background: #E8FF75 !important;
color: #000000 !important;
border: 2px solid #000000 !important;
border-radius: 10px !important;
padding: 10px 16px !important;
font-size: 0.88rem !important;
font-weight: 700 !important;
font-family: var(--font-sans) !important;
box-shadow: 3px 3px 0 #000000 !important;
transition: transform 0.15s, box-shadow 0.15s !important;
justify-content: center !important;
}

.lp-login-wallet-btn [class*="ConnectButton"] button:hover,
.lp-login-wallet-btn button[data-dapp-kit]:hover {
transform: translate(-1px, -1px) !important;
box-shadow: 4px 4px 0 #000000 !important;
}

/* ── Responsive ── */

@media (max-width: 860px) {
Expand Down
129 changes: 114 additions & 15 deletions apps/app/src/pages/LandingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/**
* Landing Page — Sign in with Google (Enoki zkLogin) or any Sui wallet
* Landing Page — Two login options via "SDK Playground" popover:
*
* 1. Sign in with Google (Enoki)
* 2. Connect Wallet (any Sui wallet)
*
* After login, redirects to /dashboard where SetupWizard handles
* delegate key generation if needed.
*/

import {
Expand All @@ -10,47 +16,99 @@ import {
} from '@mysten/dapp-kit'
import { isEnokiWallet, type EnokiWallet, type AuthProvider } from '@mysten/enoki'
import { ChevronDown, Github } from 'lucide-react'
import { useRef, useState, useEffect } from 'react'
import { useRef, useState, useEffect, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
import { useDelegateKey } from '../App'
import { config } from '../config'
import memwalLogo from '../assets/memwal-logo.svg'

type AuthMethod = 'enoki' | 'wallet' | null

const AUTH_METHOD_KEY = 'memwal_auth_method'

function persistAuthMethod(method: AuthMethod) {
if (method) {
sessionStorage.setItem(AUTH_METHOD_KEY, method)
} else {
sessionStorage.removeItem(AUTH_METHOD_KEY)
}
}

function getPersistedAuthMethod(): AuthMethod {
const val = sessionStorage.getItem(AUTH_METHOD_KEY)
if (val === 'enoki' || val === 'wallet') return val
return null
}

export default function LandingPage() {
const currentAccount = useCurrentAccount()
const { mutate: connect } = useConnectWallet()
const wallets = useWallets()
const enokiWallets = wallets.filter(isEnokiWallet)
const { delegateKey } = useDelegateKey()

// Find Google wallet from registered Enoki wallets
const walletsByProvider = enokiWallets.reduce(
(map, wallet) => map.set(wallet.provider, wallet),
new Map<AuthProvider, EnokiWallet>(),
)
const googleWallet = walletsByProvider.get('google')

const navigate = useNavigate()
const hasEnokiConfig = config.enokiApiKey && config.googleClientId
const hasEnokiConfig = !!(config.enokiApiKey && config.googleClientId)
const demoUrls = config.demoUrls

// ── Dropdown states ──
const [demoOpen, setDemoOpen] = useState(false)
const demoRef = useRef<HTMLDivElement>(null)
const [loginOpen, setLoginOpen] = useState(false)
const loginRef = useRef<HTMLDivElement>(null)

// ── Track wallet click for ConnectButton flow ──
const walletClickedRef = useRef(false)

// ── Close dropdowns on outside click ──
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (demoRef.current && !demoRef.current.contains(e.target as Node)) {
setDemoOpen(false)
}
if (loginRef.current && !loginRef.current.contains(e.target as Node) && !walletClickedRef.current) {
setLoginOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [])

const handleConnect = () => {
if (currentAccount) {
navigate('/dashboard')
} else if (hasEnokiConfig && googleWallet) {
connect({ wallet: googleWallet })
// ── Detect wallet connection via ConnectButton ──
const updateAuthMethod = useCallback((method: AuthMethod) => {
persistAuthMethod(method)
}, [])

useEffect(() => {
if (currentAccount && !delegateKey) {
const persisted = getPersistedAuthMethod()
if (!persisted && walletClickedRef.current) {
walletClickedRef.current = false
setLoginOpen(false)
updateAuthMethod('wallet')
}
// Navigate to dashboard/setup after connection
navigate('/dashboard')
}
}, [currentAccount, delegateKey, updateAuthMethod, navigate])

// ── Button handlers ──
const handleEnokiConnect = () => {
if (!googleWallet) return
updateAuthMethod('enoki')
setLoginOpen(false)
connect({ wallet: googleWallet })
}

const handleWalletClick = () => {
walletClickedRef.current = true
updateAuthMethod('wallet')
}

return (
Expand All @@ -63,6 +121,7 @@ export default function LandingPage() {
</a>

<div className="lp-nav-links">
{/* Demo dropdown */}
{demoUrls.length > 0 && (
<div className="lp-demo-dropdown" ref={demoRef}>
<button
Expand All @@ -89,16 +148,56 @@ export default function LandingPage() {
)}
</div>
)}
{currentAccount ? (

{/* SDK Playground — direct link if logged in, popover with login options if not */}
{currentAccount && delegateKey ? (
<button className="lp-nav-cta" onClick={() => navigate('/dashboard')}>
SDK Playground <span className="lp-arrow">↗</span>
</button>
) : hasEnokiConfig && googleWallet ? (
<button className="lp-nav-cta" onClick={handleConnect}>
SDK Playground <span className="lp-arrow">↗</span>
</button>
) : (
<ConnectButton connectText="SDK Playground ↗" />
<div className="lp-demo-dropdown" ref={loginRef}>
<button
className="lp-nav-cta"
onClick={() => setLoginOpen(o => !o)}
>
SDK Playground <span className="lp-arrow">↗</span>
</button>
{loginOpen && (
<div className="lp-demo-menu" style={{ minWidth: 240, padding: 12, display: 'flex', flexDirection: 'column', gap: 8 }}>
{hasEnokiConfig && googleWallet && (
<button
onClick={handleEnokiConnect}
style={{
display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
width: '100%', padding: '10px 16px',
background: '#E8FF75', color: '#000', border: '2px solid #000',
borderRadius: 10, fontSize: '0.88rem', fontWeight: 700,
fontFamily: 'var(--font-sans)', cursor: 'pointer',
boxShadow: '3px 3px 0 #000',
transition: 'transform 0.15s, box-shadow 0.15s',
}}
onMouseEnter={e => { e.currentTarget.style.transform = 'translate(-1px,-1px)'; e.currentTarget.style.boxShadow = '4px 4px 0 #000' }}
onMouseLeave={e => { e.currentTarget.style.transform = 'none'; e.currentTarget.style.boxShadow = '3px 3px 0 #000' }}
>
<svg width="16" height="16" viewBox="0 0 24 24">
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4" />
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853" />
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05" />
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335" />
</svg>
Sign in with Google
</button>
)}

<div
onClick={handleWalletClick}
className="lp-login-wallet-btn"
>
<ConnectButton connectText="Connect Wallet" />
</div>
</div>
)}
</div>
)}
</div>
</div>
Expand Down
Loading
Loading