@le-space/p2pass 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +258 -0
- package/dist/backup/registry-backup.d.ts +26 -0
- package/dist/backup/registry-backup.js +51 -0
- package/dist/identity/identity-service.d.ts +116 -0
- package/dist/identity/identity-service.js +524 -0
- package/dist/identity/mode-detector.d.ts +29 -0
- package/dist/identity/mode-detector.js +124 -0
- package/dist/identity/signing-preference.d.ts +30 -0
- package/dist/identity/signing-preference.js +55 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +91 -0
- package/dist/p2p/setup.d.ts +48 -0
- package/dist/p2p/setup.js +283 -0
- package/dist/recovery/ipns-key.d.ts +41 -0
- package/dist/recovery/ipns-key.js +127 -0
- package/dist/recovery/manifest.d.ts +106 -0
- package/dist/recovery/manifest.js +243 -0
- package/dist/registry/device-registry.d.ts +122 -0
- package/dist/registry/device-registry.js +275 -0
- package/dist/registry/index.d.ts +3 -0
- package/dist/registry/index.js +46 -0
- package/dist/registry/manager.d.ts +76 -0
- package/dist/registry/manager.js +376 -0
- package/dist/registry/pairing-protocol.d.ts +61 -0
- package/dist/registry/pairing-protocol.js +653 -0
- package/dist/ucan/storacha-auth.d.ts +45 -0
- package/dist/ucan/storacha-auth.js +164 -0
- package/dist/ui/StorachaFab.svelte +134 -0
- package/dist/ui/StorachaFab.svelte.d.ts +23 -0
- package/dist/ui/StorachaIntegration.svelte +2467 -0
- package/dist/ui/StorachaIntegration.svelte.d.ts +23 -0
- package/dist/ui/fonts/dm-mono-400.ttf +0 -0
- package/dist/ui/fonts/dm-mono-500.ttf +0 -0
- package/dist/ui/fonts/dm-sans-400.ttf +0 -0
- package/dist/ui/fonts/dm-sans-500.ttf +0 -0
- package/dist/ui/fonts/dm-sans-600.ttf +0 -0
- package/dist/ui/fonts/dm-sans-700.ttf +0 -0
- package/dist/ui/fonts/epilogue-400.ttf +0 -0
- package/dist/ui/fonts/epilogue-500.ttf +0 -0
- package/dist/ui/fonts/epilogue-600.ttf +0 -0
- package/dist/ui/fonts/epilogue-700.ttf +0 -0
- package/dist/ui/fonts/storacha-fonts.css +152 -0
- package/dist/ui/storacha-backup.d.ts +44 -0
- package/dist/ui/storacha-backup.js +218 -0
- package/package.json +112 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sabysachi Patra & Nico Krause
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# P2Pass (`@le-space/p2pass`)
|
|
2
|
+
|
|
3
|
+
[](https://github.com/asabya/p2p-passkeys/actions/workflows/tests.yml)
|
|
4
|
+
|
|
5
|
+
Standalone Svelte component for passkey-based DID identities replicating p2p between devices and [Storacha](https://storacha.network) decentralized backup. Published on npm as **`@le-space/p2pass`** (Le Space).
|
|
6
|
+
|
|
7
|
+
Drop in `<StorachaFab />` and get:
|
|
8
|
+
|
|
9
|
+
- WebAuthn passkey authentication (hardware Ed25519, P-256, or worker Ed25519 fallback)
|
|
10
|
+
- UCAN delegation-based Storacha access
|
|
11
|
+
- OrbitDB backup/restore with progress tracking
|
|
12
|
+
- P2P device linking via libp2p with copy/paste peer info
|
|
13
|
+
- Multi-device registry with automatic device detection
|
|
14
|
+
- Floating action button with Storacha branding
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @le-space/p2pass
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```svelte
|
|
25
|
+
<script>
|
|
26
|
+
import { StorachaFab } from '@le-space/p2pass';
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<StorachaFab {orbitdb} {libp2p} onAuthenticate={handleAuthenticate} />
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The component handles everything internally:
|
|
33
|
+
|
|
34
|
+
1. Click the floating Storacha button (bottom-right)
|
|
35
|
+
2. Choose a **signing mode** (hardware Ed25519 with P-256 fallback, hardware P-256 only, or worker Ed25519), then **Authenticate with Passkey** → biometric prompt → DID created
|
|
36
|
+
3. Two tabs appear: **P2P Passkeys** (device linking) and **Storacha** (backup/restore); P2P Passkeys is the default
|
|
37
|
+
4. Paste a UCAN delegation → connected to Storacha → backup/restore enabled
|
|
38
|
+
5. P2P Passkeys tab shows connection status, peer info, and linked devices
|
|
39
|
+
|
|
40
|
+
## Worker Ed25519 Passkey Flow
|
|
41
|
+
|
|
42
|
+
Worker mode (`preferWorkerMode={true}`) uses WebAuthn purely for user verification and PRF seed extraction — the actual signing key is an Ed25519 keypair generated in a web worker, encrypted with the PRF-derived key.
|
|
43
|
+
|
|
44
|
+
### First Visit (Registration)
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
User clicks "Authenticate with Passkey"
|
|
48
|
+
│
|
|
49
|
+
├─ navigator.credentials.create()
|
|
50
|
+
│ ├─ Biometric prompt (Face ID / Touch ID / PIN)
|
|
51
|
+
│ ├─ Creates discoverable credential (resident key)
|
|
52
|
+
│ └─ PRF extension: eval({ first: deterministicSalt })
|
|
53
|
+
│
|
|
54
|
+
├─ Extract PRF seed from credential response
|
|
55
|
+
│ └─ Deterministic 32-byte seed derived from biometric + salt
|
|
56
|
+
│
|
|
57
|
+
├─ Initialize Ed25519 keystore with PRF seed
|
|
58
|
+
│ └─ PRF seed used as AES-GCM encryption key for the keystore
|
|
59
|
+
│
|
|
60
|
+
├─ Generate Ed25519 keypair in web worker
|
|
61
|
+
│ ├─ Random Ed25519 keypair created
|
|
62
|
+
│ ├─ DID derived: did:key:z6Mk...
|
|
63
|
+
│ └─ Archive exported (private key material)
|
|
64
|
+
│
|
|
65
|
+
├─ Encrypt archive with PRF-derived AES key
|
|
66
|
+
│ └─ { ciphertext, iv } stored as hex strings
|
|
67
|
+
│
|
|
68
|
+
├─ Derive IPNS keypair from PRF seed (for recovery)
|
|
69
|
+
│ └─ Deterministic Ed25519 key for IPNS manifest publishing
|
|
70
|
+
│
|
|
71
|
+
└─ Store credentials
|
|
72
|
+
├─ Encrypted archive → localStorage (bootstrap cache)
|
|
73
|
+
├─ Keypair + archive → OrbitDB registry (when available)
|
|
74
|
+
└─ WebAuthn credential metadata → localStorage (for re-auth)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Return Visit (Restoration)
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
User clicks "Authenticate with Passkey"
|
|
81
|
+
│
|
|
82
|
+
├─ Find cached archive in localStorage
|
|
83
|
+
│ └─ { did, ciphertext, iv, publicKeyHex }
|
|
84
|
+
│
|
|
85
|
+
├─ Load stored WebAuthn credential metadata
|
|
86
|
+
│
|
|
87
|
+
├─ navigator.credentials.get()
|
|
88
|
+
│ ├─ Biometric prompt (same passkey as registration)
|
|
89
|
+
│ └─ PRF extension: eval({ first: sameSalt })
|
|
90
|
+
│
|
|
91
|
+
├─ Extract PRF seed → same seed as registration
|
|
92
|
+
│
|
|
93
|
+
├─ Decrypt archive with PRF seed → same Ed25519 keypair
|
|
94
|
+
│
|
|
95
|
+
└─ Same DID restored: did:key:z6Mk...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Recovery (New Device / Cleared Storage)
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
User clicks "Recover Identity"
|
|
102
|
+
│
|
|
103
|
+
├─ navigator.credentials.get() (discoverable credential)
|
|
104
|
+
│ ├─ Biometric prompt — passkey synced via iCloud/Google/etc.
|
|
105
|
+
│ └─ PRF extension → same PRF seed
|
|
106
|
+
│
|
|
107
|
+
├─ Derive IPNS keypair from PRF seed
|
|
108
|
+
│
|
|
109
|
+
├─ Resolve IPNS manifest via w3name
|
|
110
|
+
│ └─ Manifest contains: { ownerDid, archiveCID, delegation, registryAddress }
|
|
111
|
+
│
|
|
112
|
+
├─ Fetch encrypted archive from IPFS gateway (no auth needed)
|
|
113
|
+
│ └─ GET https://{archiveCID}.ipfs.w3s.link/ → { ciphertext, iv }
|
|
114
|
+
│
|
|
115
|
+
├─ Decrypt archive with PRF seed → Ed25519 keypair restored
|
|
116
|
+
│
|
|
117
|
+
├─ Connect to Storacha using delegation from manifest
|
|
118
|
+
│
|
|
119
|
+
└─ Same DID restored on new device
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Key Insight
|
|
123
|
+
|
|
124
|
+
The WebAuthn credential never signs anything — it's only used for:
|
|
125
|
+
|
|
126
|
+
1. **User verification** (biometric gate)
|
|
127
|
+
2. **PRF seed extraction** (deterministic secret derived from biometric + salt)
|
|
128
|
+
|
|
129
|
+
The PRF seed is the root of all derived keys:
|
|
130
|
+
|
|
131
|
+
- **Ed25519 DID keypair** — encrypted with PRF-derived AES key
|
|
132
|
+
- **IPNS recovery key** — deterministically derived from PRF seed
|
|
133
|
+
- **Keystore encryption** — PRF seed used as AES-GCM key
|
|
134
|
+
|
|
135
|
+
This means the same passkey on any device (via passkey sync) produces the same PRF seed, which unlocks the same DID identity.
|
|
136
|
+
|
|
137
|
+
### Signing Modes
|
|
138
|
+
|
|
139
|
+
| Mode | Security | Key Storage | Biometric |
|
|
140
|
+
| ---------------- | -------- | ----------------------------------- | ------------- |
|
|
141
|
+
| Hardware Ed25519 | Highest | TPM/Secure Enclave | Per signature |
|
|
142
|
+
| Hardware P-256 | High | TPM/Secure Enclave | Per signature |
|
|
143
|
+
| Worker Ed25519 | Medium | Web worker + encrypted localStorage | On init only |
|
|
144
|
+
|
|
145
|
+
**Worker Ed25519** matches typical OrbitDB multi-device flows (signing key in a worker). **Hardware** modes keep private keys in the authenticator; hardware Ed25519 lists Ed25519 first and may obtain **P-256** if the device does not support hardware Ed25519. Pick the mode in the panel before authenticating, or set `signingPreference="worker"` / `preferWorkerMode` on the component.
|
|
146
|
+
|
|
147
|
+
## Props
|
|
148
|
+
|
|
149
|
+
When using `StorachaFab` or `StorachaIntegration` directly:
|
|
150
|
+
|
|
151
|
+
| Prop | Type | Default | Description |
|
|
152
|
+
| ------------------- | -------- | --------------- | ---------------------------------------------------------------------------------------- |
|
|
153
|
+
| `orbitdb` | object | `null` | OrbitDB instance (for backup/restore) |
|
|
154
|
+
| `database` | object | `null` | Database instance to backup |
|
|
155
|
+
| `isInitialized` | boolean | `false` | Whether OrbitDB is ready |
|
|
156
|
+
| `entryCount` | number | `0` | Database entry count |
|
|
157
|
+
| `databaseName` | string | `'restored-db'` | Name for restored database |
|
|
158
|
+
| `onRestore` | function | `() => {}` | Called when restore completes |
|
|
159
|
+
| `onBackup` | function | `() => {}` | Called when backup completes |
|
|
160
|
+
| `onAuthenticate` | function | `() => {}` | Called after passkey auth (receives signingMode) |
|
|
161
|
+
| `libp2p` | object | `null` | libp2p instance for P2P connectivity |
|
|
162
|
+
| `signingPreference` | `string` | `null` | `'hardware-ed25519'`, `'hardware-p256'`, or `'worker'` — overrides the in-panel selector |
|
|
163
|
+
| `preferWorkerMode` | boolean | `false` | Deprecated; same as `signingPreference="worker"` |
|
|
164
|
+
|
|
165
|
+
## Components
|
|
166
|
+
|
|
167
|
+
### `StorachaFab`
|
|
168
|
+
|
|
169
|
+
Floating action button (bottom-right) with the Storacha rooster logo. Opens the integration panel as an overlay. Self-contained — no Tailwind or external CSS required.
|
|
170
|
+
|
|
171
|
+
### `StorachaIntegration`
|
|
172
|
+
|
|
173
|
+
The panel component itself. Can be embedded inline instead of as a floating panel.
|
|
174
|
+
|
|
175
|
+
## Programmatic API
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
import {
|
|
179
|
+
IdentityService,
|
|
180
|
+
createStorachaClient,
|
|
181
|
+
parseDelegation,
|
|
182
|
+
setupP2PStack,
|
|
183
|
+
createLibp2pInstance,
|
|
184
|
+
cleanupP2PStack,
|
|
185
|
+
} from '@le-space/p2pass';
|
|
186
|
+
|
|
187
|
+
// Create identity (worker mode for P2P)
|
|
188
|
+
const identity = new IdentityService();
|
|
189
|
+
const { mode, did, algorithm } = await identity.initialize(undefined, { preferWorkerMode: true });
|
|
190
|
+
|
|
191
|
+
// Get UCAN principal
|
|
192
|
+
const principal = await identity.getPrincipal();
|
|
193
|
+
|
|
194
|
+
// Connect to Storacha
|
|
195
|
+
const delegation = await parseDelegation(delegationBase64);
|
|
196
|
+
const client = await createStorachaClient(principal, delegation);
|
|
197
|
+
|
|
198
|
+
// Start P2P stack
|
|
199
|
+
const libp2p = await createLibp2pInstance();
|
|
200
|
+
// After auth, upgrade to full stack with OrbitDB:
|
|
201
|
+
const stack = await setupP2PStack(credential);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
npm run dev:example # Run example app
|
|
208
|
+
npm test # Run unit tests
|
|
209
|
+
npm run package # Build library
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### End-to-end tests
|
|
213
|
+
|
|
214
|
+
Playwright drives the **example app** in Chromium with virtual WebAuthn. Tests live in `e2e/` (for example `link-devices.spec.js` for multi-device pairing).
|
|
215
|
+
|
|
216
|
+
**Run:**
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npm run test:e2e # headless; starts relay + Vite automatically
|
|
220
|
+
npm run test:e2e:ui # Playwright UI mode (debugging)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`playwright.config.js` starts **`scripts/e2e-with-relay.mjs`**, which:
|
|
224
|
+
|
|
225
|
+
1. Launches **orbitdb-relay-pinner** (local libp2p relay).
|
|
226
|
+
2. Fetches WebSocket bootstrap multiaddrs from the relay’s HTTP API and passes them to Vite as **`VITE_BOOTSTRAP_PEERS`** so browsers can connect.
|
|
227
|
+
3. Runs **`svelte-package`** and the **example Vite dev server** on port **5173**.
|
|
228
|
+
|
|
229
|
+
First-time setup may require browser binaries:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
npx playwright install chromium
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Reuse a running dev server** (you must still provide a relay and matching `VITE_BOOTSTRAP_PEERS` yourself if you skip the script):
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
PW_REUSE_SERVER=1 npm run test:e2e
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Failed runs write HTML reports, screenshots, traces, and video under `test-results/` (see Playwright output for paths).
|
|
242
|
+
|
|
243
|
+
**Signing mode in e2e:** set `E2E_SIGNING_MODE` to `worker`, `hardware-ed25519`, or `hardware-p256` (default in helpers is `worker`). CI runs the link-devices spec **three times** (matrix), one per mode.
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
E2E_SIGNING_MODE=hardware-ed25519 npm run test:e2e
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Dependencies
|
|
250
|
+
|
|
251
|
+
- [`@le-space/orbitdb-identity-provider-webauthn-did`](https://github.com/Le-Space/orbitdb-identity-provider-webauthn-did) — WebAuthn crypto primitives
|
|
252
|
+
- [`@storacha/client`](https://github.com/storacha/w3up) — Storacha storage client
|
|
253
|
+
- [`orbitdb-storacha-bridge`](https://github.com/nicobao/orbitdb-storacha-bridge) — OrbitDB backup/restore
|
|
254
|
+
- Forked `@le-space/ucanto-*` packages for WebAuthn Ed25519/P-256 UCAN support
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
MIT
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup registry DB to Storacha via bridge.
|
|
3
|
+
* @param {Object} bridge - Initialized OrbitDBStorachaBridge instance
|
|
4
|
+
* @param {Object} orbitdb - OrbitDB instance
|
|
5
|
+
* @param {Object} registryDb - Registry database to backup
|
|
6
|
+
* @returns {Promise<{success: boolean, blocksUploaded?: number, blocksTotal?: number, error?: string}>}
|
|
7
|
+
*/
|
|
8
|
+
export function backupRegistryDb(bridge: Object, orbitdb: Object, registryDb: Object): Promise<{
|
|
9
|
+
success: boolean;
|
|
10
|
+
blocksUploaded?: number;
|
|
11
|
+
blocksTotal?: number;
|
|
12
|
+
error?: string;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Restore registry from Storacha backup.
|
|
16
|
+
* @param {Object} bridge - Initialized OrbitDBStorachaBridge instance
|
|
17
|
+
* @param {Object} orbitdb - OrbitDB instance
|
|
18
|
+
* @param {Object} [options]
|
|
19
|
+
* @returns {Promise<{success: boolean, database?: Object, entriesRecovered?: number, error?: string}>}
|
|
20
|
+
*/
|
|
21
|
+
export function restoreRegistryDb(bridge: Object, orbitdb: Object, options?: Object): Promise<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
database?: Object;
|
|
24
|
+
entriesRecovered?: number;
|
|
25
|
+
error?: string;
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup registry DB to Storacha via bridge.
|
|
3
|
+
* @param {Object} bridge - Initialized OrbitDBStorachaBridge instance
|
|
4
|
+
* @param {Object} orbitdb - OrbitDB instance
|
|
5
|
+
* @param {Object} registryDb - Registry database to backup
|
|
6
|
+
* @returns {Promise<{success: boolean, blocksUploaded?: number, blocksTotal?: number, error?: string}>}
|
|
7
|
+
*/
|
|
8
|
+
export async function backupRegistryDb(bridge, orbitdb, registryDb) {
|
|
9
|
+
try {
|
|
10
|
+
const result = await bridge.backup(orbitdb, registryDb.address);
|
|
11
|
+
return {
|
|
12
|
+
success: true,
|
|
13
|
+
blocksUploaded: result.blocksUploaded,
|
|
14
|
+
blocksTotal: result.blocksTotal,
|
|
15
|
+
};
|
|
16
|
+
} catch (err) {
|
|
17
|
+
return {
|
|
18
|
+
success: false,
|
|
19
|
+
error: err.message,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Restore registry from Storacha backup.
|
|
26
|
+
* @param {Object} bridge - Initialized OrbitDBStorachaBridge instance
|
|
27
|
+
* @param {Object} orbitdb - OrbitDB instance
|
|
28
|
+
* @param {Object} [options]
|
|
29
|
+
* @returns {Promise<{success: boolean, database?: Object, entriesRecovered?: number, error?: string}>}
|
|
30
|
+
*/
|
|
31
|
+
export async function restoreRegistryDb(bridge, orbitdb, options = {}) {
|
|
32
|
+
try {
|
|
33
|
+
const result = await bridge.restoreFromSpace(orbitdb, {
|
|
34
|
+
timeout: 120000,
|
|
35
|
+
preferredDatabaseName: 'multi-device-registry',
|
|
36
|
+
restartAfterRestore: true,
|
|
37
|
+
verifyIntegrity: true,
|
|
38
|
+
...options,
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
database: result.database,
|
|
43
|
+
entriesRecovered: result.entriesRecovered,
|
|
44
|
+
};
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: err.message,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort: this origin likely already has passkey / identity material (no WebAuthn prompt).
|
|
3
|
+
* Uses the same signals as restore paths — false negatives are OK (same handlers still apply).
|
|
4
|
+
*
|
|
5
|
+
* @returns {boolean}
|
|
6
|
+
*/
|
|
7
|
+
export function hasLocalPasskeyHint(): boolean;
|
|
8
|
+
export class IdentityService {
|
|
9
|
+
/**
|
|
10
|
+
* Bind an OrbitDB registry database for credential storage.
|
|
11
|
+
* If pending credentials exist from a prior initialize() call, flushes them.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} db - OrbitDB KeyValue database (from openDeviceRegistry)
|
|
14
|
+
*/
|
|
15
|
+
setRegistry(db: Object): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Get the bound registry DB (or null).
|
|
18
|
+
* @returns {Object|null}
|
|
19
|
+
*/
|
|
20
|
+
getRegistry(): Object | null;
|
|
21
|
+
/**
|
|
22
|
+
* Initialize identity — auto-detect hardware vs worker mode.
|
|
23
|
+
* If existing credentials found, restores them (may prompt biometric for worker PRF).
|
|
24
|
+
* If no credentials, creates new passkey.
|
|
25
|
+
*
|
|
26
|
+
* @param {'platform'|'cross-platform'} [authenticatorType]
|
|
27
|
+
* @param {{ preferWorkerMode?: boolean, signingPreference?: import('./signing-preference.js').SigningPreference }} [options]
|
|
28
|
+
* @returns {Promise<{ mode: string, did: string, algorithm: string }>}
|
|
29
|
+
*/
|
|
30
|
+
initialize(authenticatorType?: "platform" | "cross-platform", options?: {
|
|
31
|
+
preferWorkerMode?: boolean;
|
|
32
|
+
signingPreference?: import("./signing-preference.js").SigningPreference;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
mode: string;
|
|
35
|
+
did: string;
|
|
36
|
+
algorithm: string;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Force create a new identity (discards existing).
|
|
40
|
+
* @param {'platform'|'cross-platform'} [authenticatorType]
|
|
41
|
+
* @param {{ preferWorkerMode?: boolean, signingPreference?: import('./signing-preference.js').SigningPreference }} [options]
|
|
42
|
+
* @returns {Promise<{ mode: string, did: string, algorithm: string }>}
|
|
43
|
+
*/
|
|
44
|
+
createNewIdentity(authenticatorType?: "platform" | "cross-platform", options?: {
|
|
45
|
+
preferWorkerMode?: boolean;
|
|
46
|
+
signingPreference?: import("./signing-preference.js").SigningPreference;
|
|
47
|
+
}): Promise<{
|
|
48
|
+
mode: string;
|
|
49
|
+
did: string;
|
|
50
|
+
algorithm: string;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Recovery entry point — uses discoverable credentials to derive IPNS key.
|
|
54
|
+
* Does NOT restore the DID — caller must resolve manifest and restore registry first.
|
|
55
|
+
* @returns {Promise<{ prfSeed: Uint8Array, ipnsKeyPair: Object, rawCredentialId: Uint8Array }>}
|
|
56
|
+
*/
|
|
57
|
+
initializeFromRecovery(): Promise<{
|
|
58
|
+
prfSeed: Uint8Array;
|
|
59
|
+
ipnsKeyPair: Object;
|
|
60
|
+
rawCredentialId: Uint8Array;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Restore DID from an encrypted archive entry (from registry DB after recovery).
|
|
64
|
+
* Uses the PRF seed stored during initializeFromRecovery().
|
|
65
|
+
* @param {Object} archiveEntry - { ciphertext: string (hex), iv: string (hex) }
|
|
66
|
+
* @param {string} did - The owner DID from the manifest
|
|
67
|
+
* @returns {Promise<void>}
|
|
68
|
+
*/
|
|
69
|
+
restoreFromManifest(archiveEntry: Object, did: string): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Get current signing mode info.
|
|
72
|
+
* @returns {{ mode: string|null, did: string|null, algorithm: string|null, secure: boolean }}
|
|
73
|
+
*/
|
|
74
|
+
getSigningMode(): {
|
|
75
|
+
mode: string | null;
|
|
76
|
+
did: string | null;
|
|
77
|
+
algorithm: string | null;
|
|
78
|
+
secure: boolean;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Get a UCAN-compatible principal/signer.
|
|
82
|
+
* For hardware mode: returns varsig signer via toUcantoSigner()
|
|
83
|
+
* For worker mode: returns Ed25519 principal from archive
|
|
84
|
+
*
|
|
85
|
+
* @returns {Promise<any>} UCAN signer
|
|
86
|
+
*/
|
|
87
|
+
getPrincipal(): Promise<any>;
|
|
88
|
+
/**
|
|
89
|
+
* @returns {boolean}
|
|
90
|
+
*/
|
|
91
|
+
isInitialized(): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Get the derived IPNS keypair (available after initialize or recovery).
|
|
94
|
+
* @returns {{ privateKey: Object, publicKey: Object }|null}
|
|
95
|
+
*/
|
|
96
|
+
getIPNSKeyPair(): {
|
|
97
|
+
privateKey: Object;
|
|
98
|
+
publicKey: Object;
|
|
99
|
+
} | null;
|
|
100
|
+
/**
|
|
101
|
+
* Get the stored PRF seed (available after initialize or recovery).
|
|
102
|
+
* @returns {Uint8Array|null}
|
|
103
|
+
*/
|
|
104
|
+
getPrfSeed(): Uint8Array | null;
|
|
105
|
+
/**
|
|
106
|
+
* Get the encrypted archive data (ciphertext + iv as hex strings) for the current identity.
|
|
107
|
+
* Used by manifest publishing to upload the archive to IPFS for auth-free recovery.
|
|
108
|
+
*
|
|
109
|
+
* @returns {Promise<{ ciphertext: string, iv: string }|null>}
|
|
110
|
+
*/
|
|
111
|
+
getEncryptedArchiveData(): Promise<{
|
|
112
|
+
ciphertext: string;
|
|
113
|
+
iv: string;
|
|
114
|
+
} | null>;
|
|
115
|
+
#private;
|
|
116
|
+
}
|