@scopeblind/passport 0.3.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/README.md +184 -0
- package/dist/browser.d.mts +44 -0
- package/dist/browser.d.ts +44 -0
- package/dist/browser.js +180 -0
- package/dist/browser.mjs +148 -0
- package/dist/index.d.mts +421 -0
- package/dist/index.d.ts +421 -0
- package/dist/index.js +630 -0
- package/dist/index.mjs +568 -0
- package/dist/types-DHZHv2EE.d.mts +278 -0
- package/dist/types-DHZHv2EE.d.ts +278 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# @scopeblind/passport
|
|
2
|
+
|
|
3
|
+
Portable cryptographic identity for AI agents and coaches. Ed25519 keypairs, immutable manifests, ownership attestations, and verifiable evidence receipts.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
Machine decisions need portable, verifiable proof. This SDK provides the identity layer: agents and coaches get Ed25519 keypairs, sign immutable manifests declaring their capabilities, and produce receipts that anyone can verify without calling ScopeBlind.
|
|
8
|
+
|
|
9
|
+
- **Portable** — Export a passport bundle to any platform. No vendor lock-in.
|
|
10
|
+
- **Verifiable** — Ed25519 signatures over canonical JSON. Verify offline, no API calls.
|
|
11
|
+
- **Privacy-preserving** — No PII required. Identity is a key fingerprint.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @scopeblind/passport
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Requires Node.js >= 18. Browser entrypoint available at `@scopeblind/passport/browser`.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import {
|
|
25
|
+
generatePassportKey,
|
|
26
|
+
createCoachManifest,
|
|
27
|
+
verifyManifest,
|
|
28
|
+
} from '@scopeblind/passport';
|
|
29
|
+
|
|
30
|
+
// 1. Generate an Ed25519 keypair
|
|
31
|
+
const key = generatePassportKey('coach');
|
|
32
|
+
console.log(key.kid); // sb:coach:7Xq9...
|
|
33
|
+
|
|
34
|
+
// 2. Create an immutable manifest
|
|
35
|
+
const manifest = createCoachManifest(key, {
|
|
36
|
+
display_name: 'Atlas Nova',
|
|
37
|
+
model_family: 'claude',
|
|
38
|
+
system_prompt_hash: 'sha256:a1b2c3...',
|
|
39
|
+
capabilities: ['debate', 'analysis'],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// 3. Verify the signature
|
|
43
|
+
const result = verifyManifest(manifest);
|
|
44
|
+
console.log(result.valid); // true
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### Key Generation
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Generate a new Ed25519 keypair for a coach or agent
|
|
53
|
+
const coachKey = generatePassportKey('coach');
|
|
54
|
+
const agentKey = generatePassportKey('agent');
|
|
55
|
+
|
|
56
|
+
// Derive a passport ID from an existing public key
|
|
57
|
+
const kid = derivePassportId('coach', publicKeyBytes);
|
|
58
|
+
// => "sb:coach:7Xq9kM..."
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Manifests
|
|
62
|
+
|
|
63
|
+
Manifests are **immutable once signed**. Config changes produce a new version.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Coach manifest (for AI coaches/tutors)
|
|
67
|
+
const manifest = createCoachManifest(key, {
|
|
68
|
+
display_name: 'Atlas Nova',
|
|
69
|
+
model_family: 'claude',
|
|
70
|
+
system_prompt_hash: 'sha256:...',
|
|
71
|
+
capabilities: ['debate', 'analysis', 'coaching'],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Agent manifest (for autonomous AI agents)
|
|
75
|
+
const agentManifest = createAgentManifest(key, {
|
|
76
|
+
display_name: 'Research Bot',
|
|
77
|
+
model_family: 'gpt-4',
|
|
78
|
+
tool_names: ['web_search', 'file_read'],
|
|
79
|
+
max_actions_per_turn: 10,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Verify any manifest
|
|
83
|
+
const result = verifyManifest(manifest);
|
|
84
|
+
// { valid: true, kid: 'sb:coach:...', type: 'scopeblind:coach-manifest' }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Ownership Attestations
|
|
88
|
+
|
|
89
|
+
Prove a coach owns an agent (or vice versa):
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const attestation = createOwnershipAttestation(coachKey, {
|
|
93
|
+
subject_kid: agentKey.kid,
|
|
94
|
+
relationship: 'owns',
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Evidence Receipts
|
|
99
|
+
|
|
100
|
+
Sign verifiable evidence of machine decisions:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import {
|
|
104
|
+
generateIssuerKey,
|
|
105
|
+
signReceipt,
|
|
106
|
+
createArenaBattleReceipt,
|
|
107
|
+
} from '@scopeblind/passport';
|
|
108
|
+
|
|
109
|
+
// Generate an issuer keypair (server-side)
|
|
110
|
+
const issuer = generateIssuerKey();
|
|
111
|
+
|
|
112
|
+
// Sign a battle receipt
|
|
113
|
+
const receipt = createArenaBattleReceipt(issuer, {
|
|
114
|
+
battle_id: 'battle_abc123',
|
|
115
|
+
arena_id: 'blindllm',
|
|
116
|
+
participants: [
|
|
117
|
+
{ kid: coach1.kid, role: 'coach', model_family: 'claude' },
|
|
118
|
+
{ kid: coach2.kid, role: 'coach', model_family: 'gpt-4' },
|
|
119
|
+
],
|
|
120
|
+
outcome: { winner_kid: coach1.kid, method: 'judge_decision' },
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Anyone can verify the receipt
|
|
124
|
+
const verified = verifyEnvelope(receipt);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Portable Export/Import
|
|
128
|
+
|
|
129
|
+
Export a passport for migration to another platform:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import {
|
|
133
|
+
exportPassportBundle,
|
|
134
|
+
serializeBundle,
|
|
135
|
+
importPassportBundle,
|
|
136
|
+
} from '@scopeblind/passport';
|
|
137
|
+
|
|
138
|
+
// Export (includes secret key — handle securely)
|
|
139
|
+
const bundle = exportPassportBundle(key, manifest);
|
|
140
|
+
const json = serializeBundle(bundle);
|
|
141
|
+
|
|
142
|
+
// Import on another platform
|
|
143
|
+
const imported = importPassportBundle(json);
|
|
144
|
+
console.log(imported.key.kid); // same kid, different machine
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Display Helpers
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { formatKidShort, getCoachSummary } from '@scopeblind/passport';
|
|
151
|
+
|
|
152
|
+
formatKidShort('sb:coach:7Xq9kMvR...'); // "7Xq9kM"
|
|
153
|
+
getCoachSummary(manifest); // "Atlas Nova (claude) — sb:coach:7Xq9kM"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Browser Entrypoint
|
|
157
|
+
|
|
158
|
+
For browser environments (uses IndexedDB for key storage):
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { ... } from '@scopeblind/passport/browser';
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Types
|
|
165
|
+
|
|
166
|
+
All types are exported and fully documented:
|
|
167
|
+
|
|
168
|
+
- `PassportKeyPair` — Ed25519 keypair with kid and role
|
|
169
|
+
- `CoachManifest` / `AgentManifest` — Immutable signed manifests
|
|
170
|
+
- `OwnershipAttestation` — Cross-key ownership proof
|
|
171
|
+
- `SignedEnvelope` — Canonical JSON wrapper with Ed25519 signature
|
|
172
|
+
- `ArenaBattleReceipt` / `CoachUpliftReceipt` / `FormalDebateReceipt` — Evidence types
|
|
173
|
+
- `PortablePassportBundle` — Exportable passport + manifest bundle
|
|
174
|
+
|
|
175
|
+
## Design Decisions
|
|
176
|
+
|
|
177
|
+
- **Ed25519 only** — Single algorithm, no negotiation. P-256 DPoP bindings reserved for future lease validation.
|
|
178
|
+
- **Canonical JSON** — Deterministic serialization (ASCII-only keys, sorted). Same input always produces same bytes.
|
|
179
|
+
- **Immutable manifests** — Config changes produce a new version with `previous_version` link. No in-place mutation.
|
|
180
|
+
- **No blockchain** — Verification is pure cryptography. No tokens, no chain, no consensus.
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
FSL-1.1-MIT — Source-available, converts to MIT after 2 years.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { P as PassportBundle, a as PassportKeyPair, S as SignedEnvelope, M as Manifest } from './types-DHZHv2EE.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @scopeblind/passport — Browser Storage Adapter
|
|
5
|
+
*
|
|
6
|
+
* IndexedDB-based storage for passport keys and manifests.
|
|
7
|
+
* Separate from core package — import from '@scopeblind/passport/browser'.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Store a passport keypair in IndexedDB.
|
|
12
|
+
*/
|
|
13
|
+
declare function storeKey(key: PassportKeyPair): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Load a passport keypair by kid.
|
|
16
|
+
*/
|
|
17
|
+
declare function loadKey(kid: string): Promise<PassportKeyPair | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Load the first coach passport found (for single-coach UX).
|
|
20
|
+
*/
|
|
21
|
+
declare function loadCoachKey(): Promise<PassportKeyPair | null>;
|
|
22
|
+
/**
|
|
23
|
+
* List all stored passport keys.
|
|
24
|
+
*/
|
|
25
|
+
declare function listKeys(): Promise<PassportKeyPair[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Store a signed manifest.
|
|
28
|
+
*/
|
|
29
|
+
declare function storeManifest(envelope: SignedEnvelope<Manifest>): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Load a manifest by passport ID.
|
|
32
|
+
*/
|
|
33
|
+
declare function loadManifest(passportId: string): Promise<SignedEnvelope<Manifest> | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Export all passport data as a PassportBundle (unencrypted).
|
|
36
|
+
* Encryption (EncryptedPassportBundle) is Sprint 3.
|
|
37
|
+
*/
|
|
38
|
+
declare function exportAllPassports(): Promise<PassportBundle[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all passport data from IndexedDB.
|
|
41
|
+
*/
|
|
42
|
+
declare function clearAll(): Promise<void>;
|
|
43
|
+
|
|
44
|
+
export { clearAll, exportAllPassports, listKeys, loadCoachKey, loadKey, loadManifest, storeKey, storeManifest };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { P as PassportBundle, a as PassportKeyPair, S as SignedEnvelope, M as Manifest } from './types-DHZHv2EE.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @scopeblind/passport — Browser Storage Adapter
|
|
5
|
+
*
|
|
6
|
+
* IndexedDB-based storage for passport keys and manifests.
|
|
7
|
+
* Separate from core package — import from '@scopeblind/passport/browser'.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Store a passport keypair in IndexedDB.
|
|
12
|
+
*/
|
|
13
|
+
declare function storeKey(key: PassportKeyPair): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Load a passport keypair by kid.
|
|
16
|
+
*/
|
|
17
|
+
declare function loadKey(kid: string): Promise<PassportKeyPair | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Load the first coach passport found (for single-coach UX).
|
|
20
|
+
*/
|
|
21
|
+
declare function loadCoachKey(): Promise<PassportKeyPair | null>;
|
|
22
|
+
/**
|
|
23
|
+
* List all stored passport keys.
|
|
24
|
+
*/
|
|
25
|
+
declare function listKeys(): Promise<PassportKeyPair[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Store a signed manifest.
|
|
28
|
+
*/
|
|
29
|
+
declare function storeManifest(envelope: SignedEnvelope<Manifest>): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Load a manifest by passport ID.
|
|
32
|
+
*/
|
|
33
|
+
declare function loadManifest(passportId: string): Promise<SignedEnvelope<Manifest> | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Export all passport data as a PassportBundle (unencrypted).
|
|
36
|
+
* Encryption (EncryptedPassportBundle) is Sprint 3.
|
|
37
|
+
*/
|
|
38
|
+
declare function exportAllPassports(): Promise<PassportBundle[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all passport data from IndexedDB.
|
|
41
|
+
*/
|
|
42
|
+
declare function clearAll(): Promise<void>;
|
|
43
|
+
|
|
44
|
+
export { clearAll, exportAllPassports, listKeys, loadCoachKey, loadKey, loadManifest, storeKey, storeManifest };
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/browser.ts
|
|
21
|
+
var browser_exports = {};
|
|
22
|
+
__export(browser_exports, {
|
|
23
|
+
clearAll: () => clearAll,
|
|
24
|
+
exportAllPassports: () => exportAllPassports,
|
|
25
|
+
listKeys: () => listKeys,
|
|
26
|
+
loadCoachKey: () => loadCoachKey,
|
|
27
|
+
loadKey: () => loadKey,
|
|
28
|
+
loadManifest: () => loadManifest,
|
|
29
|
+
storeKey: () => storeKey,
|
|
30
|
+
storeManifest: () => storeManifest
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(browser_exports);
|
|
33
|
+
var DB_NAME = "scopeblind-passport";
|
|
34
|
+
var DB_VERSION = 1;
|
|
35
|
+
var STORE_KEYS = "keys";
|
|
36
|
+
var STORE_MANIFESTS = "manifests";
|
|
37
|
+
var STORE_ATTESTATIONS = "attestations";
|
|
38
|
+
var STORE_STATUS = "status";
|
|
39
|
+
function openDB() {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
42
|
+
request.onupgradeneeded = () => {
|
|
43
|
+
const db = request.result;
|
|
44
|
+
if (!db.objectStoreNames.contains(STORE_KEYS)) {
|
|
45
|
+
db.createObjectStore(STORE_KEYS, { keyPath: "kid" });
|
|
46
|
+
}
|
|
47
|
+
if (!db.objectStoreNames.contains(STORE_MANIFESTS)) {
|
|
48
|
+
db.createObjectStore(STORE_MANIFESTS, { keyPath: "payload.id" });
|
|
49
|
+
}
|
|
50
|
+
if (!db.objectStoreNames.contains(STORE_ATTESTATIONS)) {
|
|
51
|
+
db.createObjectStore(STORE_ATTESTATIONS, { autoIncrement: true });
|
|
52
|
+
}
|
|
53
|
+
if (!db.objectStoreNames.contains(STORE_STATUS)) {
|
|
54
|
+
db.createObjectStore(STORE_STATUS, { autoIncrement: true });
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
request.onsuccess = () => resolve(request.result);
|
|
58
|
+
request.onerror = () => reject(request.error);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function serializeKey(key) {
|
|
62
|
+
return {
|
|
63
|
+
kid: key.kid,
|
|
64
|
+
role: key.role,
|
|
65
|
+
created_at: key.created_at,
|
|
66
|
+
publicKey: Array.from(key.publicKey),
|
|
67
|
+
secretKey: Array.from(key.secretKey)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function deserializeKey(stored) {
|
|
71
|
+
return {
|
|
72
|
+
kid: stored.kid,
|
|
73
|
+
role: stored.role,
|
|
74
|
+
created_at: stored.created_at,
|
|
75
|
+
publicKey: new Uint8Array(stored.publicKey),
|
|
76
|
+
secretKey: new Uint8Array(stored.secretKey)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function storeKey(key) {
|
|
80
|
+
const db = await openDB();
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
const tx = db.transaction(STORE_KEYS, "readwrite");
|
|
83
|
+
tx.objectStore(STORE_KEYS).put(serializeKey(key));
|
|
84
|
+
tx.oncomplete = () => resolve();
|
|
85
|
+
tx.onerror = () => reject(tx.error);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async function loadKey(kid) {
|
|
89
|
+
const db = await openDB();
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const tx = db.transaction(STORE_KEYS, "readonly");
|
|
92
|
+
const request = tx.objectStore(STORE_KEYS).get(kid);
|
|
93
|
+
request.onsuccess = () => {
|
|
94
|
+
resolve(request.result ? deserializeKey(request.result) : null);
|
|
95
|
+
};
|
|
96
|
+
request.onerror = () => reject(request.error);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async function loadCoachKey() {
|
|
100
|
+
const db = await openDB();
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
const tx = db.transaction(STORE_KEYS, "readonly");
|
|
103
|
+
const request = tx.objectStore(STORE_KEYS).getAll();
|
|
104
|
+
request.onsuccess = () => {
|
|
105
|
+
const keys = (request.result || []).map(deserializeKey);
|
|
106
|
+
const coach = keys.find((k) => k.role === "coach");
|
|
107
|
+
resolve(coach || null);
|
|
108
|
+
};
|
|
109
|
+
request.onerror = () => reject(request.error);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async function listKeys() {
|
|
113
|
+
const db = await openDB();
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
const tx = db.transaction(STORE_KEYS, "readonly");
|
|
116
|
+
const request = tx.objectStore(STORE_KEYS).getAll();
|
|
117
|
+
request.onsuccess = () => {
|
|
118
|
+
resolve((request.result || []).map(deserializeKey));
|
|
119
|
+
};
|
|
120
|
+
request.onerror = () => reject(request.error);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async function storeManifest(envelope) {
|
|
124
|
+
const db = await openDB();
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
const tx = db.transaction(STORE_MANIFESTS, "readwrite");
|
|
127
|
+
tx.objectStore(STORE_MANIFESTS).put(envelope);
|
|
128
|
+
tx.oncomplete = () => resolve();
|
|
129
|
+
tx.onerror = () => reject(tx.error);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async function loadManifest(passportId) {
|
|
133
|
+
const db = await openDB();
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const tx = db.transaction(STORE_MANIFESTS, "readonly");
|
|
136
|
+
const request = tx.objectStore(STORE_MANIFESTS).get(passportId);
|
|
137
|
+
request.onsuccess = () => resolve(request.result || null);
|
|
138
|
+
request.onerror = () => reject(request.error);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async function exportAllPassports() {
|
|
142
|
+
const keys = await listKeys();
|
|
143
|
+
const bundles = [];
|
|
144
|
+
for (const key of keys) {
|
|
145
|
+
const manifest = await loadManifest(key.kid);
|
|
146
|
+
bundles.push({
|
|
147
|
+
key,
|
|
148
|
+
manifests: manifest ? [manifest] : [],
|
|
149
|
+
ownership_attestations: [],
|
|
150
|
+
status_records: []
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return bundles;
|
|
154
|
+
}
|
|
155
|
+
async function clearAll() {
|
|
156
|
+
const db = await openDB();
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
const tx = db.transaction(
|
|
159
|
+
[STORE_KEYS, STORE_MANIFESTS, STORE_ATTESTATIONS, STORE_STATUS],
|
|
160
|
+
"readwrite"
|
|
161
|
+
);
|
|
162
|
+
tx.objectStore(STORE_KEYS).clear();
|
|
163
|
+
tx.objectStore(STORE_MANIFESTS).clear();
|
|
164
|
+
tx.objectStore(STORE_ATTESTATIONS).clear();
|
|
165
|
+
tx.objectStore(STORE_STATUS).clear();
|
|
166
|
+
tx.oncomplete = () => resolve();
|
|
167
|
+
tx.onerror = () => reject(tx.error);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
171
|
+
0 && (module.exports = {
|
|
172
|
+
clearAll,
|
|
173
|
+
exportAllPassports,
|
|
174
|
+
listKeys,
|
|
175
|
+
loadCoachKey,
|
|
176
|
+
loadKey,
|
|
177
|
+
loadManifest,
|
|
178
|
+
storeKey,
|
|
179
|
+
storeManifest
|
|
180
|
+
});
|
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// src/browser.ts
|
|
2
|
+
var DB_NAME = "scopeblind-passport";
|
|
3
|
+
var DB_VERSION = 1;
|
|
4
|
+
var STORE_KEYS = "keys";
|
|
5
|
+
var STORE_MANIFESTS = "manifests";
|
|
6
|
+
var STORE_ATTESTATIONS = "attestations";
|
|
7
|
+
var STORE_STATUS = "status";
|
|
8
|
+
function openDB() {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
11
|
+
request.onupgradeneeded = () => {
|
|
12
|
+
const db = request.result;
|
|
13
|
+
if (!db.objectStoreNames.contains(STORE_KEYS)) {
|
|
14
|
+
db.createObjectStore(STORE_KEYS, { keyPath: "kid" });
|
|
15
|
+
}
|
|
16
|
+
if (!db.objectStoreNames.contains(STORE_MANIFESTS)) {
|
|
17
|
+
db.createObjectStore(STORE_MANIFESTS, { keyPath: "payload.id" });
|
|
18
|
+
}
|
|
19
|
+
if (!db.objectStoreNames.contains(STORE_ATTESTATIONS)) {
|
|
20
|
+
db.createObjectStore(STORE_ATTESTATIONS, { autoIncrement: true });
|
|
21
|
+
}
|
|
22
|
+
if (!db.objectStoreNames.contains(STORE_STATUS)) {
|
|
23
|
+
db.createObjectStore(STORE_STATUS, { autoIncrement: true });
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
request.onsuccess = () => resolve(request.result);
|
|
27
|
+
request.onerror = () => reject(request.error);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function serializeKey(key) {
|
|
31
|
+
return {
|
|
32
|
+
kid: key.kid,
|
|
33
|
+
role: key.role,
|
|
34
|
+
created_at: key.created_at,
|
|
35
|
+
publicKey: Array.from(key.publicKey),
|
|
36
|
+
secretKey: Array.from(key.secretKey)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function deserializeKey(stored) {
|
|
40
|
+
return {
|
|
41
|
+
kid: stored.kid,
|
|
42
|
+
role: stored.role,
|
|
43
|
+
created_at: stored.created_at,
|
|
44
|
+
publicKey: new Uint8Array(stored.publicKey),
|
|
45
|
+
secretKey: new Uint8Array(stored.secretKey)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function storeKey(key) {
|
|
49
|
+
const db = await openDB();
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const tx = db.transaction(STORE_KEYS, "readwrite");
|
|
52
|
+
tx.objectStore(STORE_KEYS).put(serializeKey(key));
|
|
53
|
+
tx.oncomplete = () => resolve();
|
|
54
|
+
tx.onerror = () => reject(tx.error);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function loadKey(kid) {
|
|
58
|
+
const db = await openDB();
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const tx = db.transaction(STORE_KEYS, "readonly");
|
|
61
|
+
const request = tx.objectStore(STORE_KEYS).get(kid);
|
|
62
|
+
request.onsuccess = () => {
|
|
63
|
+
resolve(request.result ? deserializeKey(request.result) : null);
|
|
64
|
+
};
|
|
65
|
+
request.onerror = () => reject(request.error);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function loadCoachKey() {
|
|
69
|
+
const db = await openDB();
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const tx = db.transaction(STORE_KEYS, "readonly");
|
|
72
|
+
const request = tx.objectStore(STORE_KEYS).getAll();
|
|
73
|
+
request.onsuccess = () => {
|
|
74
|
+
const keys = (request.result || []).map(deserializeKey);
|
|
75
|
+
const coach = keys.find((k) => k.role === "coach");
|
|
76
|
+
resolve(coach || null);
|
|
77
|
+
};
|
|
78
|
+
request.onerror = () => reject(request.error);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function listKeys() {
|
|
82
|
+
const db = await openDB();
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
const tx = db.transaction(STORE_KEYS, "readonly");
|
|
85
|
+
const request = tx.objectStore(STORE_KEYS).getAll();
|
|
86
|
+
request.onsuccess = () => {
|
|
87
|
+
resolve((request.result || []).map(deserializeKey));
|
|
88
|
+
};
|
|
89
|
+
request.onerror = () => reject(request.error);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async function storeManifest(envelope) {
|
|
93
|
+
const db = await openDB();
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
const tx = db.transaction(STORE_MANIFESTS, "readwrite");
|
|
96
|
+
tx.objectStore(STORE_MANIFESTS).put(envelope);
|
|
97
|
+
tx.oncomplete = () => resolve();
|
|
98
|
+
tx.onerror = () => reject(tx.error);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async function loadManifest(passportId) {
|
|
102
|
+
const db = await openDB();
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
const tx = db.transaction(STORE_MANIFESTS, "readonly");
|
|
105
|
+
const request = tx.objectStore(STORE_MANIFESTS).get(passportId);
|
|
106
|
+
request.onsuccess = () => resolve(request.result || null);
|
|
107
|
+
request.onerror = () => reject(request.error);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async function exportAllPassports() {
|
|
111
|
+
const keys = await listKeys();
|
|
112
|
+
const bundles = [];
|
|
113
|
+
for (const key of keys) {
|
|
114
|
+
const manifest = await loadManifest(key.kid);
|
|
115
|
+
bundles.push({
|
|
116
|
+
key,
|
|
117
|
+
manifests: manifest ? [manifest] : [],
|
|
118
|
+
ownership_attestations: [],
|
|
119
|
+
status_records: []
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return bundles;
|
|
123
|
+
}
|
|
124
|
+
async function clearAll() {
|
|
125
|
+
const db = await openDB();
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const tx = db.transaction(
|
|
128
|
+
[STORE_KEYS, STORE_MANIFESTS, STORE_ATTESTATIONS, STORE_STATUS],
|
|
129
|
+
"readwrite"
|
|
130
|
+
);
|
|
131
|
+
tx.objectStore(STORE_KEYS).clear();
|
|
132
|
+
tx.objectStore(STORE_MANIFESTS).clear();
|
|
133
|
+
tx.objectStore(STORE_ATTESTATIONS).clear();
|
|
134
|
+
tx.objectStore(STORE_STATUS).clear();
|
|
135
|
+
tx.oncomplete = () => resolve();
|
|
136
|
+
tx.onerror = () => reject(tx.error);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
clearAll,
|
|
141
|
+
exportAllPassports,
|
|
142
|
+
listKeys,
|
|
143
|
+
loadCoachKey,
|
|
144
|
+
loadKey,
|
|
145
|
+
loadManifest,
|
|
146
|
+
storeKey,
|
|
147
|
+
storeManifest
|
|
148
|
+
};
|