stint-signer
v0.5.7
Published
Short-lived, non-custodial session signer using passkeys for Cosmos SDK
Maintainers
Readme
Stint
Zero-balance session signers for smooth Web3 UX. Post, vote, and transact without wallet popups using Passkeys + Cosmos SDK authz + feegrant modules.
⚠️ EXPERIMENTAL SOFTWARE - TESTNET ONLY
This project is experimental and has NOT undergone a security audit. Only use on testnets with test tokens that have no real value. Do not use with real funds or in production environments.
What is Stint?
Stint creates temporary signers that can perform limited actions on behalf of your main wallet without holding any funds. Perfect for social dApps, games, and frequent interactions.
How it works:
- Create a session signer using your device's Passkey (fingerprint/Face ID)
- Authorize specific actions (like posting to social networks) with spending limits
- Interact seamlessly - no more wallet popups for every small transaction
Your main wallet stays secure, session signers are time-limited, and you can revoke access anytime.
Why Use Stint?
Perfect for apps that need frequent, small transactions:
Social Media dApps
- Post messages without wallet popups
- Like/react to content instantly
- Comment and interact seamlessly
Gaming & NFTs
- In-game transactions and trades
- Achievement claims and rewards
- Tournament entries
DAOs & Governance
- Vote on multiple proposals
- Delegate voting power
- Submit proposals without friction
Micro-payments
- Content tips and donations
- Subscription payments
- Pay-per-use services
Installation
npm install stint-signer
# or
pnpm add stint-signer
# or
yarn add stint-signerQuick Start
import { newSessionSigner } from 'stint-signer'
// 1. Create session signer (triggers Passkey prompt)
const sessionSigner = await newSessionSigner({
primaryClient // Your existing SigningStargateClient
})
// 2. Define authorized recipient for security
const authorizedRecipient = 'atone1dither123...' // Only allow sends to this address
// 3. Set up permissions (one-time setup)
const setupMessages = sessionSigner.generateDelegationMessages({
sessionExpiration: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
spendLimit: { denom: 'uphoton', amount: '1000000' }, // Max 1 PHOTON
gasLimit: { denom: 'uphoton', amount: '500000' }, // 0.5 PHOTON for gas
allowedRecipients: [authorizedRecipient] // Restrict to specific address
})
await primaryClient.signAndBroadcast(
sessionSigner.primaryAddress(),
setupMessages,
'auto'
)
// 4. Now send transactions instantly! 🚀
await sessionSigner.execute.send({
toAddress: authorizedRecipient, // Must match allowedRecipients
amount: [{ denom: 'uphoton', amount: '100000' }],
memo: 'Posted via session signer!'
})That's it! No more wallet popups for authorized transactions.
Live Example
🎯 Try the Dither Demo - Post to a decentralized social network without wallet popups!
The demo shows how to:
- Create session signers with WebAuthn Passkeys
- Set up permissions in one transaction
- Post messages instantly using session signers
Basic API
Creating a Session Signer
import { newSessionSigner } from 'stint-signer'
const sessionSigner = await newSessionSigner({
primaryClient, // Your SigningStargateClient
saltName?: 'my-app', // Optional: isolate different apps
stintWindowHours?: 24, // Optional: key rotation interval (default: 24 hours)
usePreviousWindow?: false, // Optional: use previous time window for grace period
logger?: consoleLogger, // Optional: enable debug logs
keyMode?: 'passkey' // Optional: 'passkey' (default) or 'random' (ephemeral)
})Setting Up Permissions
// Generate permission messages
const messages = sessionSigner.generateDelegationMessages({
sessionExpiration: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
spendLimit: { denom: 'uphoton', amount: '1000000' }, // Max spending
gasLimit: { denom: 'uphoton', amount: '500000' }, // Gas allowance
allowedRecipients: ['atone1...'] // Optional: restrict recipients
})
// Sign with your main wallet (one time)
await primaryClient.signAndBroadcast(primaryAddress, messages, 'auto')Using Session Signers
// Send tokens instantly (no wallet popup!)
await sessionSigner.execute.send({
toAddress: 'atone1recipient...',
amount: [{ denom: 'uphoton', amount: '100000' }],
memo: 'Instant transaction!'
})
// Check permissions
const hasPermission = await sessionSigner.hasAuthzGrant()
const hasGasAllowance = await sessionSigner.hasFeegrant()Learn More
📖 Complete Guide - Advanced usage, security considerations, and detailed examples
🎯 Example App - Full working demo with Dither social network
Advanced Configuration
Window-Based Key Rotation
Stint implements automatic key rotation using time-based windows for enhanced security:
const sessionSigner = await newSessionSigner({
primaryClient,
saltName: 'my-app',
// Key rotates every 8 hours for high-security apps
stintWindowHours: 8,
// Use previous window during transitions to avoid interruptions
usePreviousWindow: false,
})Window Configuration Options:
stintWindowHours: 1- Hourly rotation (maximum security)stintWindowHours: 8- 8-hour rotation (high security)stintWindowHours: 24- Daily rotation (default, balanced)stintWindowHours: 168- Weekly rotation (convenience)
Grace Period Usage:
When users might be near a window boundary, use usePreviousWindow: true to access the previous time window's keys, ensuring uninterrupted access during transitions.
Debugging Window Boundaries
import { getWindowBoundaries } from 'stint-signer'
const boundaries = getWindowBoundaries(24) // 24-hour windows
console.log('Current window:', boundaries.windowNumber)
console.log('Window start:', boundaries.start)
console.log('Window end:', boundaries.end)Key Mode Options
Stint supports two key generation modes:
Passkey Mode (Default)
- Uses device biometrics (fingerprint/Face ID) via WebAuthn
- Keys are deterministically derived from Passkey PRF extension
- Survives page refreshes within the same time window
- Most secure option
Random Mode (Fallback)
- For environments without Passkey support
- Generates cryptographically secure random keys
- Keys are ephemeral - lost on page refresh
- Use when Passkeys aren't available
// Random mode example - keys won't persist
const ephemeralSigner = await newSessionSigner({
primaryClient,
keyMode: 'random' // Ephemeral keys, lost on refresh
})Key Features
✅ Zero-balance signers - Session signers never hold funds
✅ Passkey security - Uses device biometrics for key derivation (in passkey mode)
✅ Automatic key rotation - Time-windowed keys rotate automatically
✅ Time-limited - Sessions expire automatically
✅ Revocable - Cancel permissions anytime
✅ Scoped permissions - Limit spending, recipients, and actions
✅ Multi-wallet support - Works with Keplr, Leap, Cosmostation
License
Unlicense (Public Domain)
