Skip to content

Commit 17f54d8

Browse files
committed
docs: rewrite README — install, quick start, repo map, trust ladder
1 parent 0e58448 commit 17f54d8

1 file changed

Lines changed: 119 additions & 85 deletions

File tree

README.md

Lines changed: 119 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,127 +1,161 @@
11
# NSE — Nostr Secure Enclave
22

3-
Open-source hardware-backed key management for Nostr.
3+
Open-source hardware-backed key management for Nostr. Your nsec, encrypted at rest by hardware you already own.
44

5-
**Website:** [nse.dev](https://nse.dev) · **GitHub:** [HumanjavaEnterprises/nse-dev.web.landingpage.src](https://github.com/HumanjavaEnterprises/nse-dev.web.landingpage.src)
5+
**Website:** [nse.dev](https://nse.dev) · **npm:** [nostr-secure-enclave](https://www.npmjs.com/package/nostr-secure-enclave) · **PyPI:** [nostr-secure-enclave](https://pypi.org/project/nostr-secure-enclave/)
66

77
## The Problem
88

9-
Nostr keys (secp256k1/Schnorr) can't be generated or used directly inside mobile secure enclaves (iOS Secure Enclave, Android StrongBox/TEE) — those only support P-256. Every Nostr app today stores keys in software. If the device is compromised, the key is gone.
9+
Nostr keys (secp256k1/Schnorr) can't be generated or used directly inside mobile secure enclaves (iOS Secure Enclave, Android StrongBox/TEE) — those only support P-256. Most Nostr apps today store keys in software. If the device is compromised, the key is gone.
1010

1111
## The Solution
1212

13-
NSE wraps the gap:
13+
NSE uses hardware to **protect** the key, not to sign with it. A P-256 key lives in hardware (non-exportable, biometric-gated). It encrypts the secp256k1 key at rest via AES-256-GCM. At signing time: unlock, decrypt, sign, zero.
1414

15-
1. **Generate** a secp256k1 keypair
16-
2. **Protect** it with a hardware-backed P-256 key (non-exportable, biometric-gated)
17-
3. **Sign** Nostr events with the secp256k1 key (briefly decrypted in memory, then zeroed)
18-
4. **Expose** a simple API: `generate()`, `sign()`, `getPublicKey()`
15+
```
16+
nse.sign(event)
17+
├── Biometric unlock → Secure Enclave access
18+
├── Derive AES key from hardware P-256 key
19+
├── Decrypt secp256k1 key into memory
20+
├── Schnorr sign the event
21+
├── Zero plaintext key from memory
22+
└── Return signed event
23+
```
1924

20-
The nsec never exists unprotected at rest. The P-256 key never leaves hardware.
25+
## Install
2126

22-
## Platform Support
27+
```bash
28+
# Server / CF Workers / Node.js
29+
npm install nostr-secure-enclave-server
2330

24-
| Platform | Hardware | Key Wrapping | Status |
25-
|----------|----------|-------------|--------|
26-
| iOS | Secure Enclave (SEP) | P-256 → AES-GCM → secp256k1 | Planned |
27-
| Android | StrongBox / TEE | KeyStore → AES-GCM → secp256k1 | Planned |
28-
| Server (CF Workers) | `crypto.subtle` | AES-GCM with KV-stored DEK | Planned |
29-
| Server (Node.js) | TPM 2.0 (optional) | AES-GCM with file/env key | Planned |
30-
| Browser | WebAuthn / SubtleCrypto | P-256 → AES-GCM → secp256k1 | Research |
31+
# Browser extensions / web apps
32+
npm install nostr-secure-enclave-browser
3133

32-
## API Surface
34+
# Python bots, AI entities, MCP servers
35+
pip install nostr-secure-enclave
3336

37+
# Types only (peer dependency, installed automatically)
38+
npm install nostr-secure-enclave
3439
```
35-
// All platforms — same interface
36-
nse.generate() → { pubkey, npub }
37-
nse.sign(event) → signed event (id + sig populated)
38-
nse.getPublicKey() → hex pubkey
39-
nse.getNpub() → bech32 npub
40-
nse.exists() → boolean (has a stored key?)
41-
nse.destroy() → wipe key material
40+
41+
## Quick Start
42+
43+
```typescript
44+
// Server
45+
import { NSEServer, generateMasterKey } from 'nostr-secure-enclave-server';
46+
47+
const nse = new NSEServer({ masterKey: process.env.NSE_MASTER_KEY, storage });
48+
const { pubkey, npub } = await nse.generate();
49+
const signed = await nse.sign({ kind: 1, content: 'hello', tags: [], created_at: now });
4250
```
4351

44-
## Architecture
52+
```typescript
53+
// Browser
54+
import { NSEBrowser, NSEIndexedDBStorage } from 'nostr-secure-enclave-browser';
4555

56+
const nse = new NSEBrowser({ storage: new NSEIndexedDBStorage() });
57+
const { pubkey, npub } = await nse.generate();
58+
const signed = await nse.sign(event);
4659
```
47-
┌─────────────────────────────────────┐
48-
│ Your Nostr App │
49-
│ nse.sign(event) / nse.generate() │
50-
└──────────────┬──────────────────────┘
51-
52-
┌──────────────▼──────────────────────┐
53-
│ NSE Library │
54-
│ Platform detection + unified API │
55-
└──────────────┬──────────────────────┘
56-
57-
┌───────────┼───────────┐
58-
│ │ │
59-
┌──▼──┐ ┌───▼───┐ ┌───▼───┐
60-
│ iOS │ │Android│ │Server │
61-
│ SEP │ │StrongB│ │ TPM/ │
62-
│P-256│ │ P-256 │ │ KMS │
63-
└─────┘ └───────┘ └───────┘
64-
│ │ │
65-
└───────────┼───────────┘
66-
67-
AES-GCM encrypted
68-
secp256k1 key blob
69-
(stored in Keychain/
70-
KeyStore/KV/env)
60+
61+
```python
62+
# Python
63+
from nse import NSE
64+
nse = NSE(master_key=os.environ['NSE_MASTER_KEY'])
65+
info = nse.generate()
66+
signed = nse.sign(NostrEvent(kind=1, content="hello", tags=[], created_at=now))
7167
```
7268

73-
## Prior Art
69+
## Packages
7470

75-
| Project | Scope | Gap NSE Fills |
76-
|---------|-------|---------------|
77-
| noauth-enclaved | NIP-46 signer in AWS Nitro | Server-only, not mobile |
78-
| keycrux | Key persistence for Nitro enclaves | Server-only |
79-
| K1 (Swift) | secp256k1 Schnorr signing | No enclave key wrapping |
80-
| LNbits NSD | ESP32 hardware signer | DIY device, not phone-native |
81-
| HardKey SDK | Cross-platform hardware keys | P-256 only, no secp256k1 |
71+
| Package | Platform | Registry | Status |
72+
|---------|----------|----------|--------|
73+
| [`nostr-secure-enclave`](https://www.npmjs.com/package/nostr-secure-enclave) | TypeScript types + NSEProvider interface | npm | **Published** |
74+
| [`nostr-secure-enclave-server`](https://www.npmjs.com/package/nostr-secure-enclave-server) | CF Workers / Node.js | npm | **Published** |
75+
| [`nostr-secure-enclave-browser`](https://www.npmjs.com/package/nostr-secure-enclave-browser) | WebAuthn + SubtleCrypto | npm | **Published** |
76+
| [`nostr-secure-enclave`](https://pypi.org/project/nostr-secure-enclave/) | Python (AI entities, bots, MCP) | PyPI | **Published** |
77+
| `nostr-secure-enclave-ios` | Swift via Secure Enclave | Swift Package | Planned |
78+
| `nostr-secure-enclave-android` | Kotlin via StrongBox | Maven | Planned |
8279

83-
## NIP Integration
80+
## Where NSE Fits
8481

85-
- **NIP-46** — NSE sits behind the NIP-46 signer interface. The app calls `nse.sign()`, NSE handles hardware unlock + decryption.
86-
- **NIP-49** — NSE replaces ncryptsec for key storage. Instead of passphrase-encrypted keys, the enclave protects them.
87-
- **Future NIP** — We may propose a NIP for hardware-backed key attestation (prove to a relay that a key is hardware-protected).
82+
NSE is **Level 0 infrastructure** — the cryptographic foundation that makes sovereign key management possible without asking users to understand cryptography.
8883

89-
## Packages (planned)
84+
```
85+
Level 0 NSE encrypts the key at rest
86+
└── Browser extension stores wrapped key in IndexedDB
87+
└── Server process holds encrypted identity in KV
9088
91-
| Package | Platform | Registry |
92-
|---------|----------|----------|
93-
| `nostr-secure-enclave` | TypeScript types + interface | npm |
94-
| `nostr-secure-enclave-ios` | Swift via Secure Enclave | Swift Package |
95-
| `nostr-secure-enclave-android` | Kotlin via StrongBox | Maven |
96-
| `nostr-secure-enclave-server` | CF Workers / Node.js | npm |
97-
| `nostr-secure-enclave-browser` | WebAuthn + SubtleCrypto | npm |
98-
| `nostr-secure-enclave` | Python wrapper | PyPI |
89+
Level 1 Mobile app as backup + authenticator
90+
└── iOS Secure Enclave / Android StrongBox wrap the key
9991
100-
## Hosting
92+
Level 2 NIP-46 bunker — keys never leave hardware
93+
└── NSE signs behind the NIP-46 interface
94+
└── Remote apps request signatures, never see the nsec
95+
```
10196

102-
- **Pages source:** `main` branch, `/docs` folder
103-
- **Custom domain:** `nse.dev`
97+
Products like [NostrKey](https://nostrkey.com) use NSE to protect keys in the browser. NIP-46 bunker signers use NSE on the backend. The principle: **Don't explain cryptography. Explain consequences.**
10498

105-
### DNS Configuration
99+
## Repo Structure
106100

107-
**A records** (apex domain):
108101
```
109-
185.199.108.153
110-
185.199.109.153
111-
185.199.110.153
112-
185.199.111.153
102+
docs/ ← GitHub Pages source (nse.dev)
103+
index.html ← Single-page site (HTML + inline CSS)
104+
og-image.png ← 1200x630 social card
105+
CNAME ← Custom domain: nse.dev
106+
platforms/ ← Working code for each target platform
107+
core/ ← nostr-secure-enclave — shared types + NSEProvider interface
108+
server/ ← nostr-secure-enclave-server — AES-256-GCM + nostr-crypto-utils
109+
browser/ ← nostr-secure-enclave-browser — SubtleCrypto + IndexedDB
110+
python/ ← nostr-secure-enclave (PyPI) — cryptography + secp256k1
111+
ios/ ← Planned — Swift Package (Secure Enclave)
112+
android/ ← Planned — Kotlin (StrongBox / TEE)
113+
examples/ ← 7 real-world usage patterns
114+
server-process-identity.ts
115+
cloudflare-worker-identity.ts
116+
netlify-function-identity.ts
117+
browser-extension-signer.ts
118+
python-bot-identity.py
119+
nip46-signer-backend.ts
120+
multi-key-manager.ts
113121
```
114122

115-
**CNAME** (www subdomain):
123+
## Development
124+
125+
```bash
126+
cd platforms
127+
npm install # Links workspaces (core, server, browser)
128+
npm test # Runs all 82 tests (core + server + browser + python)
129+
npm run build # Compiles TypeScript to dist/
130+
```
131+
132+
Individual test suites: `npm run test:core`, `npm run test:server`, `npm run test:browser`, `npm run test:python`
133+
134+
Python tests require: `pip install cryptography secp256k1 pytest`
135+
136+
## API
137+
138+
All platforms implement the same `NSEProvider` interface:
139+
116140
```
117-
www → humanjavaenterprises.github.io
141+
nse.generate() → { pubkey, npub, created_at, hardware_backed }
142+
nse.sign(event) → signed event (id + pubkey + sig populated)
143+
nse.getPublicKey() → hex pubkey (no unlock needed)
144+
nse.getNpub() → bech32 npub (no unlock needed)
145+
nse.exists() → boolean
146+
nse.destroy() → wipe all key material
118147
```
119148

120-
HTTPS enforced automatically by GitHub Pages.
149+
## What NSE Is Not
150+
151+
- **Not a remote signer.** NSE is a local library. Use NIP-46 for remote signing.
152+
- **Not custodial.** Keys never leave your device.
153+
- **Not a wallet.** No Lightning, no transactions. Just keys and signing.
154+
- **Not magic.** The secp256k1 key exists briefly in application memory during signing. NSE minimizes that window and zeros the key after — but a rooted/jailbroken device with memory access is out of scope.
121155

122-
## OG Image
156+
## Part of the nostr-* Family
123157

124-
Regenerate the social card: `python3 generate-og.py`
158+
NSE is built on [`nostr-crypto-utils`](https://www.npmjs.com/package/nostr-crypto-utils) and sits alongside the rest of the [Humanjava nostr-* libraries](https://www.npmjs.com/~vveerrgg).
125159

126160
## License
127161

0 commit comments

Comments
 (0)