Package Exports
- sui.ski
- sui.ski/assets/au.png
- sui.ski/assets/diamond-192.png
- sui.ski/assets/ski-192.png
- sui.ski/assets/ski-512.png
- sui.ski/assets/ski.svg
- sui.ski/assets/sui-drop.svg
- sui.ski/assets/sui-ski-qr.svg
- sui.ski/assets/tbai.svg
- sui.ski/assets/waap-icon.svg
- sui.ski/assets/waap-qr.svg
- sui.ski/styles
Readme
.SKI — .Sui Key-In
Your Sui wallet, everywhere. Connect once, authenticate everywhere.
Domain structure
| Domain | Purpose |
|---|---|
sui.ski |
Root — main application, embeddable widget, API endpoints |
<name>.sui.ski |
SuiNS profile pages (e.g. brando.sui.ski for brando.sui) |
All subdomains with 3+ characters are reserved for SuiNS profile pages. There are no dedicated subdomains for the app, API, or RPC — everything routes through the root domain.
A cross-domain session cookie (ski:xdomain) is set on domain=sui.ski so authentication persists across the root and all profile subdomains.
UI overview
The .SKI header bar renders up to four elements in a row:
Dot button (ski-dot)
Small status indicator on the far left. Shows the connected wallet's shape (diamond = address only, blue square = has SuiNS name, green circle = unconnected). Clicking it opens the SKI modal when disconnected, or toggles the SKI menu when connected. The dot carries a Splash drop overlay when a sponsor is active.
Profile pill (ski-profile)
Displays the connected wallet icon, social badge (WaaP provider), SuiNS name, and live balance. Clicking the pill opens the wallet's .sui.ski profile page in a new tab when a SuiNS name is set. The balance cycles between SUI-primary and USD-primary on click.
SKI button (ski-btn)
The main branded button showing the SKI logo and optionally the wallet shape, SuiNS name, and Splash drop. When disconnected, clicking it opens the SKI modal. When connected, it toggles the SKI menu. The modal and menu are mutually exclusive — opening one always closes the other.
Balance pill
Shows the live USD-equivalent balance with a dollar icon. Click to cycle between SUI and USD display.
SKI modal
A single-column overlay anchored below the SKI button, right-aligned with it. The modal has a fixed header (brand logo, balance, QR code) and a scrollable body containing:
- Key detail pane — the currently selected wallet with SuiNS name, address, balance, network badges, and Ika dWallet status. Defaults to the first blue-square wallet on open, falls back to the first black-diamond, then to WaaP.
- Splash legend — every key that has ever connected from this device, grouped by shape tier (diamond > blue square > green circle) with collapsible group arrows
- + new WaaP row — always present in the green tier; clicking it shows a placeholder detail pane, and clicking that detail header disconnects any active WaaP session and opens a fresh WaaP OAuth modal
- Wallet list (alternative layout) — one row per installed wallet extension with shape badges and social icons
Each legend row shows the key shape, SuiNS name badge, truncated hex address (right-justified), and the wallet provider icon (Phantom, Backpack, WaaP social icon, Slush, Suiet, etc.).
Long-press lock — hold a row for 2.2 s to lock the detail pane to that wallet. An amber ring indicates locked state. Long-press again to unlock.
Layout toggle — the Splash/List switch at the bottom lets users choose between the splash legend view and the plain wallet list. Preference is persisted in localStorage.
SKI menu
A dropdown menu beneath the SKI button (mutually exclusive with the modal). Contains:
- SuiNS name management — register new
.suinames, set default, view owned names with renewal dates - Shade orders — privacy-preserving grace-period domain sniping with commitment-reveal
- Disconnect button
- Manage Keys — opens the modal from the menu
SuiNS integration
Full SuiNS lifecycle from the SKI menu:
- Register — search and register
.suinames with instant tier pricing, pay with SUI, USDC, or NS tokens - Set default — change your primary SuiNS name (updates the SKI button and profile instantly)
- Target address — view and copy the address a name points to, with color-coded status (purple = self, green = available, orange = kiosk-listed)
- Owned names — scrollable chip grid showing all names owned by the connected wallet, with grace-period expiry warnings and renewal cost estimates
Session layer
After connecting, .SKI requests one personal message signature to prove key ownership. The signed proof is tied to a FingerprintJS visitorId (device fingerprint) and stored in localStorage. On reload the session is restored silently — no re-signing required until it expires (7 days for software wallets, 24 hours for hardware/Keystone).
Session format: { address, signature, bytes, visitorId, expiresAt } — stored under ski:session.
Splash sponsorship
Splash is a device-level gas sponsor system. A wallet owner activates Splash to cover gas fees for every key connected from the same device. Sponsored transactions use Sui's sponsored transaction flow.
- Activate via the Splash button in the modal header or the detail card
- Devices that connect through
?splash={address}are enrolled automatically - SuiNS names work as the sponsor parameter:
?splash=brando.sui - The drop badge appears on keys covered by an active sponsor
Shade
Privacy-preserving SuiNS grace-period domain sniping via on-chain commitment-reveal. A Move contract stores only keccak256(domain || execute_after_ms || target_address || salt) — the domain name, target address, and execution timestamp remain hidden until reveal at execution time.
- ShadeOrder — a shared object holding the owner address, escrowed SUI balance, and opaque commitment hash
execute()— permissionless; anyone with the preimage can call, enabling keeper botscancel()— owner-only; returns escrowed SUI- ShadeExecutorAgent — Cloudflare Durable Object that auto-executes orders at grace-period expiry via DO Alarms, using a dedicated keeper address with its own gas
- Three execution routes: SUI->NS (DeepBook), SUI->USDC->NS (two-hop), or SUI direct fallback
Contract: 0xfcd0b2b4f69758cd3ed0d35a55335417cac6304017c3c5d9a5aaff75c367aaff
See SHIELD.md for the full security model and threat analysis.
WaaP (social login)
WaaP provides custodial wallet creation via social login, email, phone, and biometrics — no browser extension required. A static placeholder wallet is always present in the SKI modal so the "+ new WaaP" row renders instantly on page load, before the WaaP SDK finishes initializing.
| Auth method | Status |
|---|---|
| Social (X, Google, Discord, GitHub) | Active |
| Active | |
| Phone | Active |
| Biometrics | Active |
WaaP accounts appear in the legend with their social provider icon (X, Google, Discord, or email envelope). Clicking an existing WaaP blue-square row reconnects via encrypted device proof (zero-modal). Clicking "+ new WaaP" forces a full disconnect and opens a fresh OAuth flow.
Install
npm install sui.ski
# or
bun add sui.skiEmbed via script tag (CDN)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sui.ski/public/styles.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/sui.ski/public/dist/ski.js"></script>Add the widget markup anywhere in your <body>:
<div class="ski-header">
<div class="ski-wallet" id="ski-wallet">
<button class="ski-btn ski-dot" id="ski-dot" style="display:none"></button>
<div id="ski-profile"></div>
<button class="ski-btn" id="ski-btn" style="display:none"></button>
<div id="ski-menu"></div>
</div>
</div>
<div id="ski-modal"></div>The script auto-initializes on load — no further JS required.
Embed via bundler
import 'sui.ski';Same DOM markup as above is required.
Events
window.addEventListener('ski:wallet-connected', (e: CustomEvent) => {
const { address, walletName } = e.detail;
});
window.addEventListener('ski:wallet-disconnected', () => {});
// Request sign-in (opens modal, then triggers sign + redirect to sui.ski)
window.dispatchEvent(new CustomEvent('ski:request-signin'));Requesting a transaction
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
// ... build your transaction
window.dispatchEvent(new CustomEvent('ski:sign-and-execute-transaction', {
detail: { transaction: tx, requestId: 'my-req-1' }
}));
window.addEventListener('ski:transaction-result', (e: CustomEvent) => {
const { requestId, success, digest, error } = e.detail;
});If a Splash sponsor is active, the sponsored flow is used automatically.
Modal API
import { openModal } from 'sui.ski';
import { setModalLayout, type ModalLayout } from 'sui.ski';
// Layouts: 'splash' (default), 'list' (wallet list only), 'layout2' (no Splash strip)
setModalLayout('list');Supported wallets
Any wallet implementing the Sui Wallet Standard:
| Wallet | Shape | Notes |
|---|---|---|
| Phantom | Diamond/Blue | |
| Backpack | Diamond/Blue | Keystone hardware via Backpack supported (24 h session) |
| Slush | Green | |
| Suiet | Green | |
| Keystone | Diamond | Direct extension; auto-prompts on connect |
| WaaP | Diamond/Blue/Green | Social/email/phone/biometrics wallet; provider badge displayed |
| Any Sui Wallet Standard extension | Varies |
Shape is determined by state: green circle = never connected, black diamond = connected (no SuiNS), blue square = connected with SuiNS name.
Self-hosting / Cloudflare Worker deploy
bun install
npx wrangler login
bun run build && npx wrangler deployThe worker hosts four Durable Objects:
| Binding | Purpose |
|---|---|
SessionAgent |
Verifies signed sessions server-side |
SponsorAgent |
Manages Splash sponsor state |
SplashDeviceAgent |
Tracks per-device Splash activation (keyed by FingerprintJS visitorId) |
ShadeExecutorAgent |
Auto-executes Shade orders at grace-period expiry via DO Alarms |
API routes served by the worker:
| Route | Purpose |
|---|---|
/agents/* |
WebSocket upgrade for Durable Object agents |
/api/health |
Health check |
/api/shade/* |
Shade order management (poke, status, schedule) |
/api/tradeport/listing/:label |
TradePort listing proxy |
Stack
@mysten/sui^2.5.1,@mysten/suins^1.0.2,@human.tech/waap-sdk^1.2.2- Transport:
SuiGrpcClientprimary withSuiGraphQLClientfallback (no JSON-RPC) - Build:
bun build src/ski.ts --outdir public/dist --target browser - Deploy: Cloudflare Workers + Wrangler 4.71
Local development
bun install
bun run dev # watches src/ski.ts, rebuilds on change
# in a second terminal:
bun run dev:wrangler # wrangler dev with hot reloadOpen http://localhost:8787 — always use http://localhost (not file://) so wallet extensions have a valid origin.
License
MIT