MoonX
moonx is how a person signs in and gets a wallet without becoming a part-time custody officer. email/oauth in. passkey-gated key material. moonx never holds the raw keys. if that sentence confuses you, stop and read it again.
mental model
- identity: email OTP or google/apple oauth via
useMoonX().start() - wallets: explicit create; passkey encrypts key material; presence tokens for step-up
- sessions: moonx issues tokens; your backend verifies against JWKS if you need an app session
package: @moon-x/react-sdk
when to use
building a product for humans. escrow, tip links, sign walls, anything where "connect wallet" is a conversion killer.
when not to use alone
agents that need to pay on a schedule. that is paybox. protocol pay-per-request without accounts is x402.
passkeys (short)
webauthn is the browser API. a passkey is the credential. moonx uses passkey step-up so sensitive wallet ops stay device-backed / MPC-gated. the same breaker is what paybox uses when an agent spend exceeds autonomous limits.
dashboard Wallets → Relying Party ID (webauthn.rp_id) must be set once to your apex (e.g. k4d.io). change it later and every existing passkey is orphaned. PSL hosts like vercel.app are not valid RP IDs.
dashboard map
dashboard.moonx-dev.com → app: Overview · Users · Developer (keys + JWKS) · Authentication · Wallets · Origins · Compliance (ToS / captcha) · Branding · Integrations (WalletConnect / Swaps / MoonPay) · Settings.
gotchas
- publishable key in the browser. secret key never in the browser. this is not a style preference.
- dashboard allowed origins must match every host you deploy (exact origin, or a single-label preview wildcard).
- wallet ops need passkey assertions + a configured RP ID. do not "simplify" that away.
- email OTP alone is a weak signup bar — the same public SDK flow a browser uses can be scripted. enable Compliance → Captcha when open signup matters; passkey enrollment binds a human device.
- copy the per-app JWKS URL from Developer for backend verification (
@moon-x/node-sdk), not a guessed global path.
sample
import { MoonXProvider, useMoonX } from "@moon-x/react-sdk";
import { useCreateWallet } from "@moon-x/react-sdk/ethereum";
function Login() {
const { start } = useMoonX();
return <button onClick={() => start()}>sign in</button>;
}
official wins
normative docs: moonx getting started · backend verification
recipe: passkey-app · skill: skills