xdamman/microbunker

small bunker to keep your nsec safe

โ˜… 0Forks 0TypeScriptGitHub โ†—Compare

README

๐Ÿ” MicroBunker

A minimal, self-hosted NIP-46 remote signing server for Nostr.

One dependency. One binary. One SQLite file.

Why

You want to use your Nostr identity across multiple devices (phone, laptop, tablet) without copying your nsec everywhere. MicroBunker holds your key securely on a server and signs events on behalf of connected clients using the NIP-46 protocol.

Features

  • NIP-46 compliant โ€” works with any client that supports bunker:// URIs (Primal, Amethyst, Coracle, etc.)
  • Encrypted at rest โ€” keys stored with AES-256-GCM, derived from passphrase via scrypt
  • Rotatable encryption โ€” change your passphrase without losing keys
  • Multi-key โ€” manage multiple Nostr identities from one bunker
  • Device management โ€” connect/revoke devices, see activity
  • Web dashboard โ€” minimal UI for status and device management
  • 1 dependency โ€” just nostr-tools. Everything else is Bun built-ins.

Requirements

Quick Start

# Install
git clone https://github.com/opencollective/microbunker.git
cd microbunker
bun install

# Initialize (generates bunker keypair, stores your nsec encrypted)
export BUNKER_PASSPHRASE="your-secret-passphrase"
bun run src/index.ts init

# Start the daemon
bun run src/index.ts start

# Generate a connection URI for your Nostr client
bun run src/index.ts connect
# โ†’ bunker://abc123...?relay=wss://relay.nsec.app&secret=xyz789...
# Paste this into Primal, Amethyst, etc.

CLI Commands

Command Description
init Interactive setup wizard
start Start the NIP-46 daemon + web dashboard
connect Generate a bunker:// connection URI
add-key Add another Nostr identity
devices List connected devices
revoke <id> Revoke a device
rotate-key Re-encrypt all keys with a new passphrase

Options

  • --data-dir <path> โ€” Custom data directory (default: ~/.microbunker)

Environment

  • BUNKER_PASSPHRASE โ€” Required. Used to encrypt/decrypt keys at rest.

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  MicroBunker                                โ”‚
โ”‚                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  NIP-46 Daemon  โ”‚  โ”‚  Web Dashboard   โ”‚  โ”‚
โ”‚  โ”‚  (relay listener)โ”‚  โ”‚  (Bun.serve)     โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚           โ”‚                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚  โ”‚  SQLite (bun:sqlite)                    โ”‚โ”‚
โ”‚  โ”‚  โ€ข keys (encrypted nsecs)               โ”‚โ”‚
โ”‚  โ”‚  โ€ข devices (connected clients)          โ”‚โ”‚
โ”‚  โ”‚  โ€ข connection_tokens                    โ”‚โ”‚
โ”‚  โ”‚  โ€ข bunker_identity                      โ”‚โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

How NIP-46 Works

  1. Your bunker listens on Nostr relays for kind:24133 events addressed to it
  2. When a client (e.g., Primal) wants to sign as you, it sends an encrypted request
  3. The bunker decrypts the request, signs the event with your key, and sends back the signed event
  4. The client publishes the signed event โ€” it looks like it came from you

Encryption

  • Keys are encrypted at rest using AES-256-GCM
  • The encryption key is derived from your passphrase using scrypt (N=16384, r=8, p=1)
  • Each key has a unique salt and IV
  • Format: version:salt:iv:ciphertext:tag
  • Use rotate-key to re-encrypt everything with a new passphrase

Configuration

Stored in ~/.microbunker/config.json:

{
  "relays": ["wss://relay.nsec.app", "wss://relay.damus.io"],
  "port": 7547,
  "host": "127.0.0.1",
  "dashboardToken": "your-dashboard-access-token",
  "tokenExpiryMinutes": 15,
  "logLevel": "info"
}

Deployment

systemd

[Unit]
Description=MicroBunker NIP-46 Server
After=network.target

[Service]
Type=simple
User=microbunker
Environment=BUNKER_PASSPHRASE=your-passphrase
ExecStart=/usr/local/bin/bun /opt/microbunker/src/index.ts start
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Reverse Proxy (for dashboard)

The dashboard runs on 127.0.0.1:7547 by default. Put nginx/caddy in front if you want HTTPS access.

server {
    server_name bunker.yourdomain.com;
    location / {
        proxy_pass http://127.0.0.1:7547;
    }
}

Security

  • Custodial: This bunker holds your private key. The passphrase protects it at rest.
  • Network: The dashboard should be behind a reverse proxy or accessed only over a trusted network.
  • Tokens: Connection tokens are single-use and expire (default: 15 minutes).
  • Revocation: Revoked devices are immediately rejected for all subsequent requests.

Supported NIP-46 Methods

  • connect โ€” Establish a new session
  • get_public_key โ€” Return the user's public key
  • sign_event โ€” Sign an event
  • nip04_encrypt / nip04_decrypt โ€” NIP-04 encryption/decryption
  • nip44_encrypt / nip44_decrypt โ€” NIP-44 encryption/decryption
  • ping โ€” Health check

License

MIT

Contributors

xdammanxdamman-bot

Issues