JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 28
  • Score
    100M100P100Q97551F
  • License MIT

MILL — Multi-Interface Login Layer. Zero-dependency Web Component for Nostr account access. Supports NIP-07, NIP-46, NIP-55, private key, read-only, and new keypair generation.

Package Exports

  • nostr-mill
  • nostr-mill/themes

Readme

MILL — Multi-Interface Login Layer

Zero-dependency Nostr signer UI as a Web Component.
Drop it into any web app with a <script> tag. Works with every Nostr signing method.

npm license


Supported Methods

Method NIP Description
Browser Extension NIP-07 Alby, nos2x, Flamingo, Nostore
Remote Signer NIP-46 Bunker URL or QR scan
Android Signer NIP-55 Amber (via Android intents)
Private Key nsec/hex, AES-256 encrypted in sessionStorage
Read Only Public key / npub view-only access
New Identity Generate keypair in-browser

Install

CDN (zero config)

<!-- Latest from your self-hosted CDN -->
<script src="https://cdn.yourdomain.com/mill/mill.umd.js"></script>

<!-- Or via jsDelivr (once published to npm as nostr-mill) -->
<script src="https://cdn.jsdelivr.net/npm/nostr-mill/dist/mill.umd.js"></script>

npm

npm install nostr-mill
# nostr-tools is an optional peer dep for real key derivation:
npm install nostr-tools

Usage

Script tag / CDN

<script src="mill.umd.js"></script>

<button onclick="MILL.open({ onConnected: console.log })">
  Connect Nostr Account
</button>

Web Component

<nostr-signer id="signer" theme="dark"></nostr-signer>

<script>
  const signer = document.getElementById('signer');

  // Open programmatically
  signer.open({
    onConnected: (result) => {
      console.log(result.method);   // 'nip07' | 'nip46' | 'nip55' | 'privatekey' | 'readonly' | 'newkey'
      console.log(result.pubkey);   // hex pubkey
    }
  });

  // Or listen via events
  signer.addEventListener('mill:connected', (e) => {
    const { method, pubkey } = e.detail;
  });

  signer.addEventListener('mill:disconnected', () => {
    console.log('user disconnected');
  });
</script>

ESM / bundler

import MILL from 'nostr-mill';

MILL.open({
  theme: 'dark',
  onConnected: (result) => {
    // result.method  — which method the user chose
    // result.pubkey  — hex public key
    // result.signer  — window.nostr-compatible interface (where available)
  },
  onClose: () => console.log('modal closed'),
});

Theming

MILL uses CSS custom properties scoped to the Shadow DOM :host. Override them externally:

nostr-signer {
  --mill-accent:   #00c896;
  --mill-bg:       #0a0a0a;
  --mill-radius:   8px;
  --mill-font:     'Your App Font', sans-serif;
}

Built-in themes

// Named themes: 'dark' (default), 'light', 'minimal', 'grain'
MILL.open({ theme: 'light' });

// Or pass a partial token object — merged onto the dark baseline
MILL.open({
  theme: {
    '--mill-accent':     '#ff6b35',
    '--mill-bg':         '#0f0f0f',
    '--mill-radius':     '4px',
    '--mill-font':       "'IBM Plex Sans', sans-serif",
  }
});

// Or use brandTheme() helper — pass just a few inputs
import { brandTheme } from 'nostr-mill/themes';
MILL.open({ theme: brandTheme({ accent: '#7c3aed', radius: '6px' }) });

Full CSS variable reference

Variable Default Description
--mill-bg #09080f Modal backdrop background
--mill-surface #100e1b Modal surface
--mill-card #181528 Method card background
--mill-card-hover #1f1c35 Method card hover
--mill-border #2a2544 Default border
--mill-border-light #3e3860 Highlighted border
--mill-accent oklch(0.67 0.28 282) Primary accent (purple)
--mill-accent-dim …/ 0.13 Accent tint background
--mill-teal oklch(0.67 0.18 195) Secondary accent
--mill-text #ede8fc Primary text
--mill-text-secondary #9d94c0 Secondary text
--mill-muted #5e5880 Muted / placeholder text
--mill-danger oklch(0.65 0.24 15) Error / danger states
--mill-warning oklch(0.78 0.18 65) Caution states
--mill-success oklch(0.7 0.2 155) Success / positive states
--mill-radius 14px Base border radius
--mill-font 'Space Grotesk', system-ui UI font stack
--mill-font-mono 'JetBrains Mono', monospace Monospace font stack

Events

Event e.detail Description
mill:connected { method, pubkey, signer?, perms? } User successfully connected
mill:disconnected {} User disconnected

Return value (result object)

type MillResult = {
  method:    'nip07' | 'nip46' | 'nip55' | 'privatekey' | 'readonly' | 'newkey';
  pubkey:    string;          // hex-encoded public key, always present
  perms?:    SigningPerms;    // per-kind signing preferences (privatekey / newkey only)
  bunkerUrl?: string;         // NIP-46 only
  nsec?:     string;          // newkey flow only — the generated nsec (handle carefully)
};

Security notes

  • Private key flows: nsec is encrypted with AES-256-GCM (PBKDF2, 100k iterations) and stored only in sessionStorage — wiped on tab close.
  • NIP-07: MILL never sees the private key. Only the public key and completed signed events pass through.
  • NIP-46: Only signed event payloads travel over the relay — never the key.
  • NIP-55: On-device intent — no network between apps.

Browser support

Modern browsers with Shadow DOM v1, CSS custom properties, and crypto.subtle (all evergreen browsers). No IE11.


License

MIT © Your Name