Shogun Core is a comprehensive SDK for building decentralized applications (dApps) that simplifies authentication, wallet management, and decentralized data storage. It combines GunDB's or Holster's peer-to-peer networking with modern authentication standards and blockchain integration.
Now supports both Gun and Holster! You can use either database backend - Gun for maximum compatibility or Holster for modern ES modules and improved performance.
npm install shogun-core
# or
yarn add shogun-core
import { ShogunCore } from "shogun-core";
import Gun from "gun";
// Create Gun instance first
const gun = Gun({
peers: ['https://gun-manhattan.herokuapp.com/gun']
});
// Initialize Shogun Core with the Gun instance
const shogun = new ShogunCore({
gunInstance: gun,
// Enable authentication plugins
web3: { enabled: true },
webauthn: {
enabled: true,
rpName: "My Awesome App",
rpId: window.location.hostname,
},
nostr: { enabled: true },
zkproof: {
enabled: true,
defaultGroupId: "my-app-users",
},
});
// Register Smart Wallet plugin separately if needed
import { SmartWalletPlugin } from "shogun-core";
const smartWalletPlugin = new SmartWalletPlugin({
enabled: true,
factoryAddress: "0x...",
defaultRequiredSignatures: 1,
defaultRequiredGuardians: 2,
});
shogun.register(smartWalletPlugin);
import { ShogunCore } from "shogun-core";
import Holster from "@mblaney/holster";
// Create Holster instance first
const holster = Holster({
peers: ['ws://localhost:8765']
});
// Initialize Shogun Core with the Holster instance
const shogun = new ShogunCore({
holsterInstance: holster, // Use Holster instead of Gun
// Enable authentication plugins (same as Gun)
web3: { enabled: true },
webauthn: {
enabled: true,
rpName: "My Awesome App",
rpId: window.location.hostname,
},
nostr: { enabled: true },
zkproof: {
enabled: true,
defaultGroupId: "my-app-users",
},
});
// All other APIs work the same way!
const db = shogun.db;
// Store data using Gun chaining
await db.get('users').get('alice').get('profile').put({
name: 'Alice Smith',
email: 'alice@example.com'
});
// Read data
const profile = await db.get('users').get('alice').get('profile').once().then();
// Update specific fields
await db.get('users').get('alice').get('profile').get('name').put('Alice Johnson');
// Iterate over collections with .map()
db.get('users').map((user, userId) => {
console.log(`User ${userId}:`, user);
});
// Sign up
const signUpResult = await shogun.signUp("username", "password");
if (signUpResult.success) {
console.log("User created:", signUpResult.username);
}
// Login
const loginResult = await shogun.login("username", "password");
if (loginResult.success) {
console.log("Logged in as:", loginResult.username);
}
const web3Plugin = shogun.getPlugin("web3");
if (web3Plugin && web3Plugin.isAvailable()) {
const connectionResult = await web3Plugin.connectMetaMask();
if (connectionResult.success) {
const address = connectionResult.address!;
// Login with Web3 wallet
const loginResult = await web3Plugin.login(address);
if (loginResult.success) {
console.log("Web3 login successful");
}
// Register new user
const signUpResult = await web3Plugin.signUp(address);
if (signUpResult.success) {
console.log("Web3 registration successful");
}
}
}
const webauthnPlugin = shogun.getPlugin("webauthn");
if (webauthnPlugin && webauthnPlugin.isSupported()) {
// Register with seed phrase for multi-device support
const signUpResult = await webauthnPlugin.signUp("username", {
generateSeedPhrase: true
});
if (signUpResult.success && signUpResult.seedPhrase) {
console.log("🔑 SAVE THESE 12 WORDS:", signUpResult.seedPhrase);
}
// Import account on another device
const importResult = await webauthnPlugin.importFromSeed(
"username",
"word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12"
);
// Authenticate
const loginResult = await webauthnPlugin.login("username");
if (loginResult.success) {
console.log("WebAuthn authentication successful");
}
}
const nostrPlugin = shogun.getPlugin("nostr");
if (nostrPlugin && nostrPlugin.isAvailable()) {
const connectionResult = await nostrPlugin.connectNostrWallet();
if (connectionResult.success) {
const address = connectionResult.address!;
const loginResult = await nostrPlugin.login(address);
if (loginResult.success) {
console.log("Nostr login successful");
}
const signUpResult = await nostrPlugin.signUp(address);
if (signUpResult.success) {
console.log("Nostr registration successful");
}
}
}
const zkPlugin = shogun.getPlugin("zkproof");
if (zkPlugin && zkPlugin.isAvailable()) {
// Sign up with ZK-Proof (creates anonymous identity)
const signUpResult = await zkPlugin.signUp();
if (signUpResult.success && signUpResult.seedPhrase) {
console.log("🔑 SAVE THIS TRAPDOOR:", signUpResult.seedPhrase);
}
// Login with trapdoor (anonymous authentication)
const loginResult = await zkPlugin.login(trapdoor);
if (loginResult.success) {
console.log("ZK-Proof login successful (anonymous)");
}
}
import { SmartWalletPlugin } from "shogun-core";
// Register Smart Wallet plugin
const smartWalletPlugin = new SmartWalletPlugin({
enabled: true,
factoryAddress: "0x...", // Smart Wallet Factory contract address
defaultRequiredSignatures: 1,
defaultRequiredGuardians: 2,
});
shogun.register(smartWalletPlugin);
// Configure signer (derive EOA from WebAuthn seed phrase)
const webauthnPlugin = shogun.getPlugin("webauthn");
const signUpResult = await webauthnPlugin.signUp("alice", {
generateSeedPhrase: true
});
import { derive } from "shogun-core";
const wallet = await derive(signUpResult.seedPhrase!, "alice", {
includeSecp256k1Ethereum: true
});
await smartWalletPlugin.setSigner(wallet.secp256k1Ethereum.privateKey);
// Create Smart Wallet with guardians
const result = await smartWalletPlugin.createWalletWithGuardians(
wallet.secp256k1Ethereum.address,
[guardian1, guardian2],
1, // 1 signature required
2 // 2 guardians for recovery
);
if (result.success) {
console.log("Smart Wallet created:", result.walletAddress);
}
<!DOCTYPE html>
<html>
<head>
<title>Shogun Core in Browser</title>
</head>
<body>
<h1>My dApp</h1>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
<script src="https://cdn.jsdelivr.net/npm/shogun-core/dist/browser/shogun-core.js"></script>
<script>
// Create Gun instance first
const gun = Gun({
peers: ["https://gun-manhattan.herokuapp.com/gun"]
});
// Initialize Shogun Core
const shogunCore = new window.SHOGUN_CORE({
gunInstance: gun,
web3: { enabled: true },
webauthn: {
enabled: true,
rpName: "My Browser dApp",
rpId: window.location.hostname,
},
});
console.log("Shogun Core initialized in browser!", shogunCore);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Shogun Core with Holster</title>
</head>
<body>
<h1>My dApp</h1>
<script type="module">
import Holster from 'https://cdn.jsdelivr.net/npm/@mblaney/holster/build/holster.js';
import { ShogunCore } from 'https://cdn.jsdelivr.net/npm/shogun-core/dist/src/index.js';
// Create Holster instance
const holster = Holster({
peers: ['ws://localhost:8765']
});
// Initialize Shogun Core
const shogunCore = new ShogunCore({
holsterInstance: holster,
web3: { enabled: true },
webauthn: {
enabled: true,
rpName: "My Browser dApp",
rpId: window.location.hostname,
},
});
console.log("Shogun Core initialized with Holster!", shogunCore);
</script>
</body>
</html>
// Listen for authentication events
shogun.on("auth:login", (data) => {
console.log("User logged in:", data.username);
console.log("Authentication method:", data.method);
});
shogun.on("auth:logout", () => {
console.log("User logged out");
});
shogun.on("auth:signup", (data) => {
console.log("New user signed up:", data.username);
});
// Listen for errors
shogun.on("error", (error) => {
console.error("Shogun error:", error.message);
});
Shogun Core supports both Gun and Holster as database backends. Choose based on your needs:
Note: The API is identical regardless of which backend you choose. Shogun Core handles all the differences internally.
.get().get(), Holster uses .get().next() (handled automatically via proxy)interface ShogunCoreConfig {
gunInstance?: IGunInstance<any>; // Optional: existing Gun instance (required if holsterInstance not provided)
holsterInstance?: any; // Optional: existing Holster instance (required if gunInstance not provided)
// Plugin configurations
webauthn?: {
enabled?: boolean;
rpName?: string;
rpId?: string;
};
web3?: {
enabled?: boolean;
};
nostr?: {
enabled?: boolean;
};
zkproof?: {
enabled?: boolean;
defaultGroupId?: string;
deterministic?: boolean;
minEntropy?: number;
};
postAuth?: {
enabled?: boolean;
};
// Timeouts
timeouts?: {
login?: number;
signup?: number;
operation?: number;
};
plugins?: {
autoRegister?: ShogunPlugin[];
};
disableAutoRecall?: boolean; // Disable automatic session recall on init
silent?: boolean; // Disable console logs
}
Note:
gunInstance or holsterInstance must be provided (but not both)SmartWalletPlugin must be registered separately using shogun.register() as it's not included in the main configurationIf you're currently using Gun and want to switch to Holster:
Install Holster:
npm install @mblaney/holster
# or
yarn add @mblaney/holster
Update your initialization:
// Before (Gun)
import Gun from "gun";
const gun = Gun({ peers: [...] });
const shogun = new ShogunCore({ gunInstance: gun });
// After (Holster)
import Holster from "@mblaney/holster";
const holster = Holster({ peers: [...] });
const shogun = new ShogunCore({ holsterInstance: holster });
That's it! All other code remains the same. The API is identical.
loginWithPair()) is not yet supported - use username/password instead# Install dependencies
yarn install
# Run all tests with coverage
yarn test:ci
# Watch mode
yarn test:watch
# Coverage report
yarn coverage
# Plugin tests only
yarn test src/__tests__/plugins
This project is licensed under the MIT License - see the LICENSE file for details.