shogun-core 3.3.1 → 3.3.2
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/dist/ship/examples/ephemeral-cli.js +234 -0
- package/dist/ship/examples/identity-cli.js +503 -0
- package/dist/ship/examples/messenger-cli.js +745 -0
- package/dist/ship/examples/stealth-cli.js +433 -0
- package/dist/ship/examples/storage-cli.js +615 -0
- package/dist/ship/examples/vault-cli.js +444 -0
- package/dist/ship/examples/wallet-cli.js +767 -0
- package/dist/ship/implementation/SHIP_00.js +478 -0
- package/dist/ship/implementation/SHIP_01.js +433 -0
- package/dist/ship/implementation/SHIP_02.js +1366 -0
- package/dist/ship/implementation/SHIP_03.js +855 -0
- package/dist/ship/implementation/SHIP_04.js +589 -0
- package/dist/ship/implementation/SHIP_05.js +1064 -0
- package/dist/ship/implementation/SHIP_06.js +350 -0
- package/dist/ship/implementation/SHIP_07.js +635 -0
- package/dist/ship/index.js +17 -0
- package/dist/ship/interfaces/ISHIP_00.js +135 -0
- package/dist/ship/interfaces/ISHIP_01.js +128 -0
- package/dist/ship/interfaces/ISHIP_02.js +57 -0
- package/dist/ship/interfaces/ISHIP_03.js +61 -0
- package/dist/ship/interfaces/ISHIP_04.js +62 -0
- package/dist/ship/interfaces/ISHIP_05.js +59 -0
- package/dist/ship/interfaces/ISHIP_06.js +144 -0
- package/dist/ship/interfaces/ISHIP_07.js +194 -0
- package/dist/types/ship/examples/ephemeral-cli.d.ts +13 -0
- package/dist/types/ship/examples/identity-cli.d.ts +40 -0
- package/dist/types/ship/examples/messenger-cli.d.ts +37 -0
- package/dist/types/ship/examples/stealth-cli.d.ts +31 -0
- package/dist/types/ship/examples/storage-cli.d.ts +48 -0
- package/dist/types/ship/examples/vault-cli.d.ts +13 -0
- package/dist/types/ship/examples/wallet-cli.d.ts +131 -0
- package/dist/types/ship/implementation/SHIP_00.d.ts +113 -0
- package/dist/types/ship/implementation/SHIP_01.d.ts +80 -0
- package/dist/types/ship/implementation/SHIP_02.d.ts +297 -0
- package/dist/types/ship/implementation/SHIP_03.d.ts +127 -0
- package/dist/types/ship/implementation/SHIP_04.d.ts +76 -0
- package/dist/types/ship/implementation/SHIP_05.d.ts +70 -0
- package/dist/types/ship/implementation/SHIP_06.d.ts +66 -0
- package/dist/types/ship/implementation/SHIP_07.d.ts +101 -0
- package/dist/types/ship/index.d.ts +14 -0
- package/dist/types/ship/interfaces/ISHIP_00.d.ts +410 -0
- package/dist/types/ship/interfaces/ISHIP_01.d.ts +343 -0
- package/dist/types/ship/interfaces/ISHIP_02.d.ts +470 -0
- package/dist/types/ship/interfaces/ISHIP_03.d.ts +295 -0
- package/dist/types/ship/interfaces/ISHIP_04.d.ts +245 -0
- package/dist/types/ship/interfaces/ISHIP_05.d.ts +234 -0
- package/dist/types/ship/interfaces/ISHIP_06.d.ts +370 -0
- package/dist/types/ship/interfaces/ISHIP_07.d.ts +522 -0
- package/package.json +1 -1
- /package/dist/{config → src/config}/simplified-config.js +0 -0
- /package/dist/{core.js → src/core.js} +0 -0
- /package/dist/{examples → src/examples}/api-test.js +0 -0
- /package/dist/{examples → src/examples}/simple-api-test.js +0 -0
- /package/dist/{gundb → src/gundb}/api.js +0 -0
- /package/dist/{gundb → src/gundb}/crypto.js +0 -0
- /package/dist/{gundb → src/gundb}/db.js +0 -0
- /package/dist/{gundb → src/gundb}/derive.js +0 -0
- /package/dist/{gundb → src/gundb}/errors.js +0 -0
- /package/dist/{gundb → src/gundb}/index.js +0 -0
- /package/dist/{gundb → src/gundb}/rxjs.js +0 -0
- /package/dist/{gundb → src/gundb}/types.js +0 -0
- /package/dist/{index.js → src/index.js} +0 -0
- /package/dist/{interfaces → src/interfaces}/common.js +0 -0
- /package/dist/{interfaces → src/interfaces}/events.js +0 -0
- /package/dist/{interfaces → src/interfaces}/plugin.js +0 -0
- /package/dist/{interfaces → src/interfaces}/shogun.js +0 -0
- /package/dist/{managers → src/managers}/AuthManager.js +0 -0
- /package/dist/{managers → src/managers}/CoreInitializer.js +0 -0
- /package/dist/{managers → src/managers}/EventManager.js +0 -0
- /package/dist/{managers → src/managers}/PluginManager.js +0 -0
- /package/dist/{migration-test.js → src/migration-test.js} +0 -0
- /package/dist/{plugins → src/plugins}/base.js +0 -0
- /package/dist/{plugins → src/plugins}/index.js +0 -0
- /package/dist/{plugins → src/plugins}/nostr/index.js +0 -0
- /package/dist/{plugins → src/plugins}/nostr/nostrConnector.js +0 -0
- /package/dist/{plugins → src/plugins}/nostr/nostrConnectorPlugin.js +0 -0
- /package/dist/{plugins → src/plugins}/nostr/nostrSigner.js +0 -0
- /package/dist/{plugins → src/plugins}/nostr/types.js +0 -0
- /package/dist/{plugins → src/plugins}/oauth/index.js +0 -0
- /package/dist/{plugins → src/plugins}/oauth/oauthConnector.js +0 -0
- /package/dist/{plugins → src/plugins}/oauth/oauthPlugin.js +0 -0
- /package/dist/{plugins → src/plugins}/oauth/types.js +0 -0
- /package/dist/{plugins → src/plugins}/web3/index.js +0 -0
- /package/dist/{plugins → src/plugins}/web3/types.js +0 -0
- /package/dist/{plugins → src/plugins}/web3/web3Connector.js +0 -0
- /package/dist/{plugins → src/plugins}/web3/web3ConnectorPlugin.js +0 -0
- /package/dist/{plugins → src/plugins}/web3/web3Signer.js +0 -0
- /package/dist/{plugins → src/plugins}/webauthn/index.js +0 -0
- /package/dist/{plugins → src/plugins}/webauthn/types.js +0 -0
- /package/dist/{plugins → src/plugins}/webauthn/webauthn.js +0 -0
- /package/dist/{plugins → src/plugins}/webauthn/webauthnPlugin.js +0 -0
- /package/dist/{plugins → src/plugins}/webauthn/webauthnSigner.js +0 -0
- /package/dist/{storage → src/storage}/storage.js +0 -0
- /package/dist/{types → src/types}/events.js +0 -0
- /package/dist/{types → src/types}/shogun.js +0 -0
- /package/dist/{utils → src/utils}/errorHandler.js +0 -0
- /package/dist/{utils → src/utils}/eventEmitter.js +0 -0
- /package/dist/{utils → src/utils}/validation.js +0 -0
- /package/dist/types/{config → src/config}/simplified-config.d.ts +0 -0
- /package/dist/types/{core.d.ts → src/core.d.ts} +0 -0
- /package/dist/types/{examples → src/examples}/api-test.d.ts +0 -0
- /package/dist/types/{examples → src/examples}/simple-api-test.d.ts +0 -0
- /package/dist/types/{gundb → src/gundb}/api.d.ts +0 -0
- /package/dist/types/{gundb → src/gundb}/crypto.d.ts +0 -0
- /package/dist/types/{gundb → src/gundb}/db.d.ts +0 -0
- /package/dist/types/{gundb → src/gundb}/derive.d.ts +0 -0
- /package/dist/types/{gundb → src/gundb}/errors.d.ts +0 -0
- /package/dist/types/{gundb → src/gundb}/index.d.ts +0 -0
- /package/dist/types/{gundb → src/gundb}/rxjs.d.ts +0 -0
- /package/dist/types/{gundb → src/gundb}/types.d.ts +0 -0
- /package/dist/types/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/types/{interfaces → src/interfaces}/common.d.ts +0 -0
- /package/dist/types/{interfaces → src/interfaces}/events.d.ts +0 -0
- /package/dist/types/{interfaces → src/interfaces}/plugin.d.ts +0 -0
- /package/dist/types/{interfaces → src/interfaces}/shogun.d.ts +0 -0
- /package/dist/types/{managers → src/managers}/AuthManager.d.ts +0 -0
- /package/dist/types/{managers → src/managers}/CoreInitializer.d.ts +0 -0
- /package/dist/types/{managers → src/managers}/EventManager.d.ts +0 -0
- /package/dist/types/{managers → src/managers}/PluginManager.d.ts +0 -0
- /package/dist/types/{migration-test.d.ts → src/migration-test.d.ts} +0 -0
- /package/dist/types/{plugins → src/plugins}/base.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/index.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/nostr/index.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/nostr/nostrConnector.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/nostr/nostrConnectorPlugin.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/nostr/nostrSigner.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/nostr/types.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/oauth/index.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/oauth/oauthConnector.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/oauth/oauthPlugin.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/oauth/types.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/web3/index.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/web3/types.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/web3/web3Connector.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/web3/web3ConnectorPlugin.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/web3/web3Signer.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/webauthn/index.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/webauthn/types.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/webauthn/webauthn.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/webauthn/webauthnPlugin.d.ts +0 -0
- /package/dist/types/{plugins → src/plugins}/webauthn/webauthnSigner.d.ts +0 -0
- /package/dist/types/{storage → src/storage}/storage.d.ts +0 -0
- /package/dist/types/{types → src/types}/events.d.ts +0 -0
- /package/dist/types/{types → src/types}/shogun.d.ts +0 -0
- /package/dist/types/{utils → src/utils}/errorHandler.d.ts +0 -0
- /package/dist/types/{utils → src/utils}/eventEmitter.d.ts +0 -0
- /package/dist/types/{utils → src/utils}/validation.d.ts +0 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHIP-05: Decentralized File Storage Interface
|
|
3
|
+
*
|
|
4
|
+
* @title ISHIP_05 - IPFS Storage with Relay Network
|
|
5
|
+
* @notice Interface for decentralized file storage with encryption
|
|
6
|
+
*
|
|
7
|
+
* ## Abstract
|
|
8
|
+
*
|
|
9
|
+
* This standard extends SHIP-00 to enable:
|
|
10
|
+
* - Encrypted file storage on IPFS
|
|
11
|
+
* - Relay network management for storage providers
|
|
12
|
+
* - On-chain subscription system for storage
|
|
13
|
+
* - Deterministic encryption from wallet signatures
|
|
14
|
+
* - File sharing with access control
|
|
15
|
+
*
|
|
16
|
+
* ## Specification
|
|
17
|
+
*
|
|
18
|
+
* Based on:
|
|
19
|
+
* - SHIP-00 for identity foundation
|
|
20
|
+
* - IPFS for decentralized storage
|
|
21
|
+
* - Smart contracts for relay payment system
|
|
22
|
+
* - Deterministic encryption from wallet signatures
|
|
23
|
+
*
|
|
24
|
+
* ## Dependencies
|
|
25
|
+
*
|
|
26
|
+
* - SHIP-00: Identity and authentication foundation
|
|
27
|
+
* - IPFS: Decentralized file storage
|
|
28
|
+
* - Ethereum: For relay subscription payments
|
|
29
|
+
* - shogun-ipfs: IPFS wrapper library
|
|
30
|
+
*
|
|
31
|
+
* ## Usage
|
|
32
|
+
*
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const identity = new SHIP_00({ gunOptions: { peers: ['...'] } });
|
|
35
|
+
* await identity.login('alice', 'password123');
|
|
36
|
+
*
|
|
37
|
+
* const storage = new SHIP_05(identity);
|
|
38
|
+
* await storage.initialize();
|
|
39
|
+
*
|
|
40
|
+
* // Upload encrypted file
|
|
41
|
+
* const result = await storage.uploadFile(file, { encrypt: true });
|
|
42
|
+
*
|
|
43
|
+
* // Download and decrypt
|
|
44
|
+
* const data = await storage.downloadFile(result.hash, { decrypt: true });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
import type { ISHIP_00 } from "./ISHIP_00";
|
|
48
|
+
/**
|
|
49
|
+
* @notice File upload result
|
|
50
|
+
*/
|
|
51
|
+
export interface UploadResult {
|
|
52
|
+
success: boolean;
|
|
53
|
+
hash?: string;
|
|
54
|
+
size?: number;
|
|
55
|
+
encrypted?: boolean;
|
|
56
|
+
error?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* @notice File metadata
|
|
60
|
+
*/
|
|
61
|
+
export interface FileMetadata {
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
hash: string;
|
|
65
|
+
sizeMB: number;
|
|
66
|
+
uploadedAt: number;
|
|
67
|
+
encrypted: boolean;
|
|
68
|
+
type?: string;
|
|
69
|
+
owner?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @notice Upload options
|
|
73
|
+
*/
|
|
74
|
+
export interface UploadOptions {
|
|
75
|
+
encrypt?: boolean;
|
|
76
|
+
pin?: boolean;
|
|
77
|
+
metadata?: Record<string, any>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* @notice Download options
|
|
81
|
+
*/
|
|
82
|
+
export interface DownloadOptions {
|
|
83
|
+
decrypt?: boolean;
|
|
84
|
+
returnBlob?: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* @notice Encryption options for SEA
|
|
88
|
+
*/
|
|
89
|
+
export interface EncryptionOptions {
|
|
90
|
+
useUserPair?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* @title ISHIP_05 - Decentralized File Storage
|
|
94
|
+
* @notice Main interface for IPFS file storage with encryption
|
|
95
|
+
*/
|
|
96
|
+
export interface ISHIP_05 {
|
|
97
|
+
/**
|
|
98
|
+
* @notice Initialize the storage system
|
|
99
|
+
* @dev Must be called after SHIP-00 authentication
|
|
100
|
+
* @param options Configuration options
|
|
101
|
+
* @returns Promise that resolves when initialization is complete
|
|
102
|
+
*/
|
|
103
|
+
initialize(options?: SHIP_05_Config): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* @notice Check if system is initialized
|
|
106
|
+
* @returns True if initialized
|
|
107
|
+
*/
|
|
108
|
+
isInitialized(): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* @notice Get the underlying SHIP-00 identity provider
|
|
111
|
+
* @returns SHIP-00 instance
|
|
112
|
+
*/
|
|
113
|
+
getIdentity(): ISHIP_00;
|
|
114
|
+
/**
|
|
115
|
+
* @notice Upload file to IPFS
|
|
116
|
+
* @dev Optionally encrypts file with deterministic key from wallet signature
|
|
117
|
+
* @param file File to upload (File or Buffer)
|
|
118
|
+
* @param options Upload options
|
|
119
|
+
* @returns Promise resolving to upload result with IPFS hash
|
|
120
|
+
*
|
|
121
|
+
* Process:
|
|
122
|
+
* 1. If encrypt=true, get wallet signature and generate key
|
|
123
|
+
* 2. Upload to IPFS
|
|
124
|
+
* 3. Record file metadata on GunDB
|
|
125
|
+
*/
|
|
126
|
+
uploadFile(file: File | Buffer, options?: UploadOptions): Promise<UploadResult>;
|
|
127
|
+
/**
|
|
128
|
+
* @notice Upload JSON data to IPFS
|
|
129
|
+
* @param data JSON object to upload
|
|
130
|
+
* @param options Upload options
|
|
131
|
+
* @returns Promise resolving to upload result
|
|
132
|
+
*/
|
|
133
|
+
uploadJson(data: any, options?: UploadOptions): Promise<UploadResult>;
|
|
134
|
+
/**
|
|
135
|
+
* @notice Download file from IPFS
|
|
136
|
+
* @dev Automatically decrypts if file was encrypted by user
|
|
137
|
+
* @param hash IPFS hash to download
|
|
138
|
+
* @param options Download options
|
|
139
|
+
* @returns Promise resolving to file data
|
|
140
|
+
*/
|
|
141
|
+
downloadFile(hash: string, options?: DownloadOptions): Promise<string | Blob>;
|
|
142
|
+
/**
|
|
143
|
+
* @notice Get file metadata
|
|
144
|
+
* @param hash IPFS hash
|
|
145
|
+
* @returns Promise resolving to file metadata or null
|
|
146
|
+
*/
|
|
147
|
+
getFileMetadata(hash: string): Promise<FileMetadata | null>;
|
|
148
|
+
/**
|
|
149
|
+
* @notice Delete file from IPFS
|
|
150
|
+
* @dev Removes file and updates MB usage
|
|
151
|
+
* @param hash IPFS hash to delete
|
|
152
|
+
* @returns Promise resolving to deletion result
|
|
153
|
+
*/
|
|
154
|
+
deleteFile(hash: string): Promise<{
|
|
155
|
+
success: boolean;
|
|
156
|
+
error?: string;
|
|
157
|
+
}>;
|
|
158
|
+
/**
|
|
159
|
+
* @notice Get all files uploaded by user
|
|
160
|
+
* @returns Promise resolving to array of file metadata
|
|
161
|
+
*/
|
|
162
|
+
getUserFiles(): Promise<FileMetadata[]>;
|
|
163
|
+
/**
|
|
164
|
+
* @notice Encrypt data using SEA (from SHIP-00)
|
|
165
|
+
* @dev Uses user's SEA keypair for encryption
|
|
166
|
+
* @param data Data to encrypt (string or Buffer)
|
|
167
|
+
* @param options Encryption options
|
|
168
|
+
* @returns Promise resolving to encrypted data
|
|
169
|
+
*
|
|
170
|
+
* Process:
|
|
171
|
+
* 1. Get SEA pair from SHIP-00 identity
|
|
172
|
+
* 2. Use SEA.encrypt(data, pair) for symmetric encryption
|
|
173
|
+
* 3. Return encrypted string
|
|
174
|
+
*/
|
|
175
|
+
encryptData(data: string | Buffer, options?: EncryptionOptions): Promise<string>;
|
|
176
|
+
/**
|
|
177
|
+
* @notice Decrypt data using SEA (from SHIP-00)
|
|
178
|
+
* @dev Uses user's SEA keypair for decryption
|
|
179
|
+
* @param encryptedData Encrypted data
|
|
180
|
+
* @param options Encryption options
|
|
181
|
+
* @returns Promise resolving to decrypted data
|
|
182
|
+
*/
|
|
183
|
+
decryptData(encryptedData: string, options?: EncryptionOptions): Promise<string>;
|
|
184
|
+
/**
|
|
185
|
+
* @notice Check if file is accessible
|
|
186
|
+
* @param hash IPFS hash
|
|
187
|
+
* @returns Promise resolving to boolean
|
|
188
|
+
*/
|
|
189
|
+
isFileAccessible(hash: string): Promise<boolean>;
|
|
190
|
+
/**
|
|
191
|
+
* @notice Get storage usage statistics
|
|
192
|
+
* @returns Promise resolving to usage stats
|
|
193
|
+
*/
|
|
194
|
+
getStorageStats(): Promise<{
|
|
195
|
+
totalFiles: number;
|
|
196
|
+
totalMB: number;
|
|
197
|
+
encryptedFiles: number;
|
|
198
|
+
plainFiles: number;
|
|
199
|
+
}>;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* @notice SHIP-05 Configuration
|
|
203
|
+
*/
|
|
204
|
+
export interface SHIP_05_Config {
|
|
205
|
+
/** IPFS service configuration */
|
|
206
|
+
ipfsService?: "PINATA" | "IPFS-CLIENT" | "CUSTOM";
|
|
207
|
+
/** IPFS service config */
|
|
208
|
+
ipfsConfig?: {
|
|
209
|
+
pinataJwt?: string;
|
|
210
|
+
pinataGateway?: string;
|
|
211
|
+
url?: string;
|
|
212
|
+
customApiUrl?: string;
|
|
213
|
+
customToken?: string;
|
|
214
|
+
};
|
|
215
|
+
/** Max file size in MB */
|
|
216
|
+
maxFileSizeMB?: number;
|
|
217
|
+
}
|
|
218
|
+
export declare enum SHIP_05_EventType {
|
|
219
|
+
FILE_UPLOADED = "fileUploaded",
|
|
220
|
+
FILE_DOWNLOADED = "fileDownloaded",
|
|
221
|
+
FILE_DELETED = "fileDeleted",
|
|
222
|
+
ERROR = "error"
|
|
223
|
+
}
|
|
224
|
+
export interface SHIP_05_Event {
|
|
225
|
+
type: SHIP_05_EventType;
|
|
226
|
+
data?: any;
|
|
227
|
+
timestamp: number;
|
|
228
|
+
}
|
|
229
|
+
export type SHIP_05_Events = {
|
|
230
|
+
fileUploaded: (result: UploadResult) => void;
|
|
231
|
+
fileDownloaded: (hash: string) => void;
|
|
232
|
+
fileDeleted: (hash: string) => void;
|
|
233
|
+
error: (error: Error) => void;
|
|
234
|
+
};
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHIP-06: Ephemeral P2P Messaging Interface
|
|
3
|
+
*
|
|
4
|
+
* @title ISHIP_06 - Ephemeral P2P Messaging
|
|
5
|
+
* @notice Interface for ephemeral peer-to-peer messaging via Gun Relay
|
|
6
|
+
* @dev Can work standalone OR with ISHIP_00 for authenticated sessions
|
|
7
|
+
*
|
|
8
|
+
* ## Abstract
|
|
9
|
+
*
|
|
10
|
+
* This standard defines an interface for ephemeral P2P messaging that allows:
|
|
11
|
+
* - Relay-based connections via Gun network
|
|
12
|
+
* - End-to-end encrypted messages (no storage)
|
|
13
|
+
* - Broadcast and direct messaging
|
|
14
|
+
* - Deterministic room discovery (SHA-256)
|
|
15
|
+
* - Standalone mode (no authentication needed!)
|
|
16
|
+
*
|
|
17
|
+
* ## Dependencies
|
|
18
|
+
*
|
|
19
|
+
* - Gun: Relay-based P2P database
|
|
20
|
+
* - Gun SEA: Cryptography (ECDH + AES-GCM)
|
|
21
|
+
* - ISHIP_00 (OPTIONAL): For authenticated sessions
|
|
22
|
+
*
|
|
23
|
+
* ## Modes
|
|
24
|
+
*
|
|
25
|
+
* **Standalone**: new SHIP_06(gunPeers[], roomId)
|
|
26
|
+
* **With Identity**: new SHIP_06(ISHIP_00, roomId)
|
|
27
|
+
*
|
|
28
|
+
* ## Inspiration
|
|
29
|
+
*
|
|
30
|
+
* Based on Bugoff (https://github.com/draeder/bugoff)
|
|
31
|
+
* Simplified for Gun relay instead of WebRTC
|
|
32
|
+
*/
|
|
33
|
+
import type { SEAPair } from "./ISHIP_00";
|
|
34
|
+
/**
|
|
35
|
+
* @notice Ephemeral message structure
|
|
36
|
+
*/
|
|
37
|
+
export interface EphemeralMessage {
|
|
38
|
+
from: string;
|
|
39
|
+
fromPubKey: string;
|
|
40
|
+
content: string;
|
|
41
|
+
timestamp: number;
|
|
42
|
+
type: "broadcast" | "direct";
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @notice Encrypted message data (internal)
|
|
46
|
+
*/
|
|
47
|
+
export interface EncryptedMessageData {
|
|
48
|
+
content: string;
|
|
49
|
+
fromPubKey: string;
|
|
50
|
+
timestamp: string;
|
|
51
|
+
type: "broadcast" | "direct";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @notice Peer information
|
|
55
|
+
*/
|
|
56
|
+
export interface PeerInfo {
|
|
57
|
+
address: string;
|
|
58
|
+
pubKey?: string;
|
|
59
|
+
epub?: string;
|
|
60
|
+
connectedAt: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @title ISHIP_06 - Ephemeral P2P Messaging
|
|
64
|
+
* @notice Main interface for ephemeral messaging system
|
|
65
|
+
* @dev Can work standalone (array of peers) OR with ISHIP_00 identity
|
|
66
|
+
*
|
|
67
|
+
* Constructor patterns:
|
|
68
|
+
* ```typescript
|
|
69
|
+
* // Standalone mode (no authentication)
|
|
70
|
+
* class EphemeralMessaging implements ISHIP_06 {
|
|
71
|
+
* constructor(
|
|
72
|
+
* peers: string[], // Gun relay peers
|
|
73
|
+
* roomId: string,
|
|
74
|
+
* config?: { debug?: boolean }
|
|
75
|
+
* ) {}
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* // Identity mode (with SHIP-00)
|
|
79
|
+
* class EphemeralMessaging implements ISHIP_06 {
|
|
80
|
+
* constructor(
|
|
81
|
+
* identity: ISHIP_00, // Authenticated identity
|
|
82
|
+
* roomId: string,
|
|
83
|
+
* config?: EphemeralConfig
|
|
84
|
+
* ) {}
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export interface ISHIP_06 {
|
|
89
|
+
/**
|
|
90
|
+
* @notice Connect to ephemeral swarm
|
|
91
|
+
* @dev Joins WebTorrent swarm and establishes P2P connections
|
|
92
|
+
*
|
|
93
|
+
* Flow:
|
|
94
|
+
* 1. Hash room ID with SHA-256 for swarm identifier
|
|
95
|
+
* 2. Join WebTorrent swarm with hashed ID
|
|
96
|
+
* 3. Listen for peer connections
|
|
97
|
+
* 4. Exchange SEA public keys with peers
|
|
98
|
+
* 5. Establish encrypted channels
|
|
99
|
+
*/
|
|
100
|
+
connect(): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* @notice Disconnect from swarm
|
|
103
|
+
* @dev Closes all P2P connections and leaves swarm
|
|
104
|
+
*/
|
|
105
|
+
disconnect(): void;
|
|
106
|
+
/**
|
|
107
|
+
* @notice Check if connected to swarm
|
|
108
|
+
* @return True if connected
|
|
109
|
+
*/
|
|
110
|
+
isConnected(): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* @notice Get swarm identifier (SHA-256 hash of room ID)
|
|
113
|
+
* @return Hashed swarm identifier
|
|
114
|
+
*/
|
|
115
|
+
getSwarmId(): string;
|
|
116
|
+
/**
|
|
117
|
+
* @notice Get own peer address in swarm
|
|
118
|
+
* @return Peer address
|
|
119
|
+
*/
|
|
120
|
+
getAddress(): string;
|
|
121
|
+
/**
|
|
122
|
+
* @notice Send broadcast message to all peers in swarm
|
|
123
|
+
* @dev Message is encrypted with each peer's public key
|
|
124
|
+
* @param message Plain text message content
|
|
125
|
+
*
|
|
126
|
+
* Prerequisites:
|
|
127
|
+
* - Must be connected to swarm
|
|
128
|
+
* - User must be authenticated (via ISHIP_00)
|
|
129
|
+
*
|
|
130
|
+
* Flow:
|
|
131
|
+
* 1. Get all connected peers
|
|
132
|
+
* 2. For each peer:
|
|
133
|
+
* a. Derive shared secret (ECDH)
|
|
134
|
+
* b. Encrypt message
|
|
135
|
+
* c. Send via WebRTC data channel
|
|
136
|
+
*/
|
|
137
|
+
sendBroadcast(message: string): Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* @notice Send direct message to specific peer
|
|
140
|
+
* @dev Message encrypted only for target peer
|
|
141
|
+
* @param peerAddress Target peer address
|
|
142
|
+
* @param message Plain text message content
|
|
143
|
+
*
|
|
144
|
+
* Prerequisites:
|
|
145
|
+
* - Must be connected to swarm
|
|
146
|
+
* - Target peer must be connected
|
|
147
|
+
* - User must be authenticated (via ISHIP_00)
|
|
148
|
+
*
|
|
149
|
+
* Flow:
|
|
150
|
+
* 1. Get target peer's public key
|
|
151
|
+
* 2. Derive shared secret (ECDH)
|
|
152
|
+
* 3. Encrypt message
|
|
153
|
+
* 4. Send via WebRTC data channel
|
|
154
|
+
*/
|
|
155
|
+
sendDirect(peerAddress: string, message: string): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* @notice Listen for decrypted messages
|
|
158
|
+
* @dev Automatically decrypts received messages
|
|
159
|
+
* @param callback Called for each received message
|
|
160
|
+
*
|
|
161
|
+
* Prerequisites:
|
|
162
|
+
* - Must be connected to swarm
|
|
163
|
+
*
|
|
164
|
+
* Decryption flow:
|
|
165
|
+
* 1. Receive encrypted message via WebRTC
|
|
166
|
+
* 2. Retrieve sender's epub from message
|
|
167
|
+
* 3. Derive shared secret (ECDH)
|
|
168
|
+
* 4. Decrypt message content
|
|
169
|
+
* 5. Call callback with decrypted message
|
|
170
|
+
*/
|
|
171
|
+
onMessage(callback: (message: EphemeralMessage) => void): void;
|
|
172
|
+
/**
|
|
173
|
+
* @notice Listen for encrypted messages (debugging)
|
|
174
|
+
* @dev Raw encrypted messages before decryption
|
|
175
|
+
* @param callback Called for each encrypted message
|
|
176
|
+
*/
|
|
177
|
+
onEncryptedMessage(callback: (address: string, data: any) => void): void;
|
|
178
|
+
/**
|
|
179
|
+
* @notice Listen for peer join events
|
|
180
|
+
* @dev Called when a new peer connects to swarm
|
|
181
|
+
* @param callback Called with peer address
|
|
182
|
+
*/
|
|
183
|
+
onPeerSeen(callback: (address: string) => void): void;
|
|
184
|
+
/**
|
|
185
|
+
* @notice Listen for peer leave events
|
|
186
|
+
* @dev Called when a peer disconnects from swarm
|
|
187
|
+
* @param callback Called with peer address
|
|
188
|
+
*/
|
|
189
|
+
onPeerLeft(callback: (address: string) => void): void;
|
|
190
|
+
/**
|
|
191
|
+
* @notice Get list of connected peers
|
|
192
|
+
* @return Array of peer addresses
|
|
193
|
+
*/
|
|
194
|
+
getPeers(): string[];
|
|
195
|
+
/**
|
|
196
|
+
* @notice Get detailed peer information
|
|
197
|
+
* @param address Peer address
|
|
198
|
+
* @return Peer info or null if not found
|
|
199
|
+
*/
|
|
200
|
+
getPeerInfo(address: string): PeerInfo | null;
|
|
201
|
+
/**
|
|
202
|
+
* @notice Get current ephemeral SEA pair
|
|
203
|
+
* @dev New pair generated per session for perfect forward secrecy
|
|
204
|
+
* @return SEA key pair
|
|
205
|
+
*/
|
|
206
|
+
getEphemeralPair(): Promise<SEAPair>;
|
|
207
|
+
/**
|
|
208
|
+
* @notice Manually set SEA pair (optional)
|
|
209
|
+
* @dev By default, SHIP-06 generates ephemeral pair automatically
|
|
210
|
+
* @param pair SEA key pair
|
|
211
|
+
*/
|
|
212
|
+
setEphemeralPair(pair: SEAPair): Promise<void>;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* @notice Ephemeral messaging configuration
|
|
216
|
+
*/
|
|
217
|
+
export interface EphemeralConfig {
|
|
218
|
+
/**
|
|
219
|
+
* @notice Room identifier (will be hashed)
|
|
220
|
+
*/
|
|
221
|
+
roomId: string;
|
|
222
|
+
/**
|
|
223
|
+
* @notice Enable debug logging
|
|
224
|
+
*/
|
|
225
|
+
debug?: boolean;
|
|
226
|
+
/**
|
|
227
|
+
* @notice Operation timeout (ms)
|
|
228
|
+
*/
|
|
229
|
+
timeout?: number;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* @notice Event emitter interface for SHIP-06
|
|
233
|
+
*/
|
|
234
|
+
export interface ISHIP_06Events {
|
|
235
|
+
/**
|
|
236
|
+
* Emitted when connected to swarm
|
|
237
|
+
*/
|
|
238
|
+
connected: () => void;
|
|
239
|
+
/**
|
|
240
|
+
* Emitted when disconnected from swarm
|
|
241
|
+
*/
|
|
242
|
+
disconnected: () => void;
|
|
243
|
+
/**
|
|
244
|
+
* Emitted when a peer joins
|
|
245
|
+
*/
|
|
246
|
+
peerSeen: (address: string) => void;
|
|
247
|
+
/**
|
|
248
|
+
* Emitted when a peer leaves
|
|
249
|
+
*/
|
|
250
|
+
peerLeft: (address: string) => void;
|
|
251
|
+
/**
|
|
252
|
+
* Emitted when a message is received and decrypted
|
|
253
|
+
*/
|
|
254
|
+
message: (message: EphemeralMessage) => void;
|
|
255
|
+
/**
|
|
256
|
+
* Emitted when an encrypted message is received (before decryption)
|
|
257
|
+
*/
|
|
258
|
+
encryptedMessage: (address: string, data: any) => void;
|
|
259
|
+
/**
|
|
260
|
+
* Emitted on error
|
|
261
|
+
*/
|
|
262
|
+
error: (error: Error) => void;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Example of how to implement ISHIP_06 with ISHIP_00 dependency
|
|
266
|
+
*
|
|
267
|
+
* ```typescript
|
|
268
|
+
* import { ISHIP_00 } from './ISHIP_00';
|
|
269
|
+
* import { ISHIP_06, EphemeralMessage } from './ISHIP_06';
|
|
270
|
+
* import Bugout from 'bugout';
|
|
271
|
+
*
|
|
272
|
+
* class EphemeralMessaging implements ISHIP_06 {
|
|
273
|
+
* private bugout: any;
|
|
274
|
+
* private ephemeralPair: SEAPair | null = null;
|
|
275
|
+
* private peers: Map<string, PeerInfo> = new Map();
|
|
276
|
+
* private messageCallbacks: ((msg: EphemeralMessage) => void)[] = [];
|
|
277
|
+
*
|
|
278
|
+
* constructor(
|
|
279
|
+
* private identity: ISHIP_00,
|
|
280
|
+
* private roomId: string,
|
|
281
|
+
* private config?: EphemeralConfig
|
|
282
|
+
* ) {
|
|
283
|
+
* if (!identity.isLoggedIn()) {
|
|
284
|
+
* throw new Error('User must be authenticated via SHIP-00');
|
|
285
|
+
* }
|
|
286
|
+
* }
|
|
287
|
+
*
|
|
288
|
+
* getIdentity(): ISHIP_00 {
|
|
289
|
+
* return this.identity;
|
|
290
|
+
* }
|
|
291
|
+
*
|
|
292
|
+
* async connect(): Promise<void> {
|
|
293
|
+
* // 1. Generate ephemeral SEA pair
|
|
294
|
+
* const crypto = this.identity.shogun.db.crypto;
|
|
295
|
+
* this.ephemeralPair = await crypto.pair();
|
|
296
|
+
*
|
|
297
|
+
* // 2. Hash room ID
|
|
298
|
+
* const swarmId = await crypto.hashText(this.roomId);
|
|
299
|
+
*
|
|
300
|
+
* // 3. Create Bugout swarm
|
|
301
|
+
* this.bugout = new Bugout(swarmId, {
|
|
302
|
+
* iceServers: this.config?.iceServers
|
|
303
|
+
* });
|
|
304
|
+
*
|
|
305
|
+
* // 4. Set SEA pair
|
|
306
|
+
* await this.bugout.SEA(this.ephemeralPair);
|
|
307
|
+
*
|
|
308
|
+
* // 5. Listen for events
|
|
309
|
+
* this.bugout.on('seen', (address: string) => {
|
|
310
|
+
* this.handlePeerSeen(address);
|
|
311
|
+
* });
|
|
312
|
+
*
|
|
313
|
+
* this.bugout.on('decrypted', (address: string, pubkeys: any, message: string) => {
|
|
314
|
+
* this.handleMessage(address, pubkeys, message);
|
|
315
|
+
* });
|
|
316
|
+
* }
|
|
317
|
+
*
|
|
318
|
+
* disconnect(): void {
|
|
319
|
+
* if (this.bugout) {
|
|
320
|
+
* this.bugout.destroy();
|
|
321
|
+
* }
|
|
322
|
+
* }
|
|
323
|
+
*
|
|
324
|
+
* async sendBroadcast(message: string): Promise<void> {
|
|
325
|
+
* if (!this.bugout) {
|
|
326
|
+
* throw new Error('Not connected to swarm');
|
|
327
|
+
* }
|
|
328
|
+
*
|
|
329
|
+
* this.bugout.send(message);
|
|
330
|
+
* }
|
|
331
|
+
*
|
|
332
|
+
* async sendDirect(peerAddress: string, message: string): Promise<void> {
|
|
333
|
+
* if (!this.bugout) {
|
|
334
|
+
* throw new Error('Not connected to swarm');
|
|
335
|
+
* }
|
|
336
|
+
*
|
|
337
|
+
* this.bugout.send(peerAddress, message);
|
|
338
|
+
* }
|
|
339
|
+
*
|
|
340
|
+
* onMessage(callback: (message: EphemeralMessage) => void): void {
|
|
341
|
+
* this.messageCallbacks.push(callback);
|
|
342
|
+
* }
|
|
343
|
+
*
|
|
344
|
+
* private handleMessage(address: string, pubkeys: any, content: string) {
|
|
345
|
+
* const message: EphemeralMessage = {
|
|
346
|
+
* from: address,
|
|
347
|
+
* fromPubKey: pubkeys.pub,
|
|
348
|
+
* content,
|
|
349
|
+
* timestamp: Date.now(),
|
|
350
|
+
* type: 'broadcast'
|
|
351
|
+
* };
|
|
352
|
+
*
|
|
353
|
+
* this.messageCallbacks.forEach(cb => cb(message));
|
|
354
|
+
* }
|
|
355
|
+
* }
|
|
356
|
+
*
|
|
357
|
+
* // Usage
|
|
358
|
+
* const identity = new SHIP_00(config);
|
|
359
|
+
* await identity.login('alice', 'password123');
|
|
360
|
+
*
|
|
361
|
+
* const ephemeral = new EphemeralMessaging(identity, 'my-room');
|
|
362
|
+
* await ephemeral.connect();
|
|
363
|
+
*
|
|
364
|
+
* ephemeral.onMessage((msg) => {
|
|
365
|
+
* console.log(`${msg.from}: ${msg.content}`);
|
|
366
|
+
* });
|
|
367
|
+
*
|
|
368
|
+
* await ephemeral.sendBroadcast('Hello everyone!');
|
|
369
|
+
* ```
|
|
370
|
+
*/
|