@haven-chat-org/core 1.0.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.
Files changed (81) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +71 -0
  3. package/dist/crypto/backup.d.ts +87 -0
  4. package/dist/crypto/backup.js +62 -0
  5. package/dist/crypto/backup.js.map +1 -0
  6. package/dist/crypto/double-ratchet.d.ts +104 -0
  7. package/dist/crypto/double-ratchet.js +274 -0
  8. package/dist/crypto/double-ratchet.js.map +1 -0
  9. package/dist/crypto/file.d.ts +14 -0
  10. package/dist/crypto/file.js +20 -0
  11. package/dist/crypto/file.js.map +1 -0
  12. package/dist/crypto/index.d.ts +9 -0
  13. package/dist/crypto/index.js +10 -0
  14. package/dist/crypto/index.js.map +1 -0
  15. package/dist/crypto/keys.d.ts +61 -0
  16. package/dist/crypto/keys.js +79 -0
  17. package/dist/crypto/keys.js.map +1 -0
  18. package/dist/crypto/passphrase.d.ts +10 -0
  19. package/dist/crypto/passphrase.js +142 -0
  20. package/dist/crypto/passphrase.js.map +1 -0
  21. package/dist/crypto/profile.d.ts +31 -0
  22. package/dist/crypto/profile.js +73 -0
  23. package/dist/crypto/profile.js.map +1 -0
  24. package/dist/crypto/sender-keys.d.ts +76 -0
  25. package/dist/crypto/sender-keys.js +170 -0
  26. package/dist/crypto/sender-keys.js.map +1 -0
  27. package/dist/crypto/sender-keys.test.d.ts +1 -0
  28. package/dist/crypto/sender-keys.test.js +272 -0
  29. package/dist/crypto/sender-keys.test.js.map +1 -0
  30. package/dist/crypto/utils.d.ts +41 -0
  31. package/dist/crypto/utils.js +102 -0
  32. package/dist/crypto/utils.js.map +1 -0
  33. package/dist/crypto/x3dh.d.ts +45 -0
  34. package/dist/crypto/x3dh.js +106 -0
  35. package/dist/crypto/x3dh.js.map +1 -0
  36. package/dist/export/__tests__/archive.test.d.ts +1 -0
  37. package/dist/export/__tests__/archive.test.js +276 -0
  38. package/dist/export/__tests__/archive.test.js.map +1 -0
  39. package/dist/export/archive.d.ts +38 -0
  40. package/dist/export/archive.js +107 -0
  41. package/dist/export/archive.js.map +1 -0
  42. package/dist/export/index.d.ts +4 -0
  43. package/dist/export/index.js +4 -0
  44. package/dist/export/index.js.map +1 -0
  45. package/dist/export/reader.d.ts +27 -0
  46. package/dist/export/reader.js +101 -0
  47. package/dist/export/reader.js.map +1 -0
  48. package/dist/export/signing.d.ts +15 -0
  49. package/dist/export/signing.js +44 -0
  50. package/dist/export/signing.js.map +1 -0
  51. package/dist/export/types.d.ts +128 -0
  52. package/dist/export/types.js +3 -0
  53. package/dist/export/types.js.map +1 -0
  54. package/dist/index.d.ts +5 -0
  55. package/dist/index.js +6 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/net/api.d.ts +200 -0
  58. package/dist/net/api.js +715 -0
  59. package/dist/net/api.js.map +1 -0
  60. package/dist/net/api.test.d.ts +1 -0
  61. package/dist/net/api.test.js +884 -0
  62. package/dist/net/api.test.js.map +1 -0
  63. package/dist/net/index.d.ts +2 -0
  64. package/dist/net/index.js +3 -0
  65. package/dist/net/index.js.map +1 -0
  66. package/dist/net/ws.d.ts +71 -0
  67. package/dist/net/ws.js +257 -0
  68. package/dist/net/ws.js.map +1 -0
  69. package/dist/store/index.d.ts +2 -0
  70. package/dist/store/index.js +2 -0
  71. package/dist/store/index.js.map +1 -0
  72. package/dist/store/memory.d.ts +24 -0
  73. package/dist/store/memory.js +50 -0
  74. package/dist/store/memory.js.map +1 -0
  75. package/dist/store/types.d.ts +23 -0
  76. package/dist/store/types.js +2 -0
  77. package/dist/store/types.js.map +1 -0
  78. package/dist/types.d.ts +850 -0
  79. package/dist/types.js +35 -0
  80. package/dist/types.js.map +1 -0
  81. package/package.json +41 -0
@@ -0,0 +1,45 @@
1
+ /**
2
+ * X3DH (Extended Triple Diffie-Hellman) key agreement.
3
+ *
4
+ * Establishes a shared secret between Alice (initiator) and Bob (responder)
5
+ * even if Bob is offline. Alice fetches Bob's key bundle from the server.
6
+ *
7
+ * Protocol:
8
+ * DH1 = DH(IK_A, SPK_B) — Alice's identity × Bob's signed prekey
9
+ * DH2 = DH(EK_A, IK_B) — Alice's ephemeral × Bob's identity
10
+ * DH3 = DH(EK_A, SPK_B) — Alice's ephemeral × Bob's signed prekey
11
+ * DH4 = DH(EK_A, OPK_B) — Alice's ephemeral × Bob's one-time prekey (if available)
12
+ * SK = HKDF(DH1 || DH2 || DH3 || DH4)
13
+ *
14
+ * All identity keys are Ed25519 (converted to X25519 for DH).
15
+ * All prekeys and ephemeral keys are X25519 natively.
16
+ */
17
+ import { type IdentityKeyPair, type DHKeyPair } from "./keys.js";
18
+ import type { KeyBundle } from "../types.js";
19
+ export interface X3DHResult {
20
+ /** The derived shared secret (32 bytes). Initializes the Double Ratchet. */
21
+ sharedKey: Uint8Array;
22
+ /** Associated data: IK_A || IK_B (for AEAD binding in Double Ratchet). */
23
+ associatedData: Uint8Array;
24
+ /** The ephemeral public key Alice used (sent to Bob in the initial message). */
25
+ ephemeralPublicKey: Uint8Array;
26
+ }
27
+ /**
28
+ * Alice (initiator): compute X3DH shared secret from Bob's key bundle.
29
+ *
30
+ * @param aliceIdentity Alice's long-term Ed25519 identity keypair
31
+ * @param bobBundle Bob's key bundle fetched from the server
32
+ * @returns The shared key, associated data, and ephemeral key to include in the initial message
33
+ */
34
+ export declare function x3dhInitiate(aliceIdentity: IdentityKeyPair, bobBundle: KeyBundle): X3DHResult;
35
+ /**
36
+ * Bob (responder): compute X3DH shared secret from Alice's initial message.
37
+ *
38
+ * @param bobIdentity Bob's long-term Ed25519 identity keypair
39
+ * @param bobSignedPreKey Bob's signed prekey pair (the one used in his bundle)
40
+ * @param bobOneTimePreKey Bob's one-time prekey pair (null if not used)
41
+ * @param aliceIdentityPub Alice's Ed25519 public identity key (from the initial message)
42
+ * @param aliceEphemeralPub Alice's ephemeral X25519 public key (from the initial message)
43
+ * @returns The shared key and associated data
44
+ */
45
+ export declare function x3dhRespond(bobIdentity: IdentityKeyPair, bobSignedPreKey: DHKeyPair, bobOneTimePreKey: DHKeyPair | null, aliceIdentityPub: Uint8Array, aliceEphemeralPub: Uint8Array): Omit<X3DHResult, "ephemeralPublicKey">;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * X3DH (Extended Triple Diffie-Hellman) key agreement.
3
+ *
4
+ * Establishes a shared secret between Alice (initiator) and Bob (responder)
5
+ * even if Bob is offline. Alice fetches Bob's key bundle from the server.
6
+ *
7
+ * Protocol:
8
+ * DH1 = DH(IK_A, SPK_B) — Alice's identity × Bob's signed prekey
9
+ * DH2 = DH(EK_A, IK_B) — Alice's ephemeral × Bob's identity
10
+ * DH3 = DH(EK_A, SPK_B) — Alice's ephemeral × Bob's signed prekey
11
+ * DH4 = DH(EK_A, OPK_B) — Alice's ephemeral × Bob's one-time prekey (if available)
12
+ * SK = HKDF(DH1 || DH2 || DH3 || DH4)
13
+ *
14
+ * All identity keys are Ed25519 (converted to X25519 for DH).
15
+ * All prekeys and ephemeral keys are X25519 natively.
16
+ */
17
+ import { dh, ed25519PkToX25519, ed25519SkToX25519, generateDHKeyPair, verifySignature, } from "./keys.js";
18
+ import { hkdf, fromBase64 } from "./utils.js";
19
+ const X3DH_INFO = new TextEncoder().encode("haven_x3dh");
20
+ const PADDING = new Uint8Array(32).fill(0xff);
21
+ /**
22
+ * Alice (initiator): compute X3DH shared secret from Bob's key bundle.
23
+ *
24
+ * @param aliceIdentity Alice's long-term Ed25519 identity keypair
25
+ * @param bobBundle Bob's key bundle fetched from the server
26
+ * @returns The shared key, associated data, and ephemeral key to include in the initial message
27
+ */
28
+ export function x3dhInitiate(aliceIdentity, bobBundle) {
29
+ // Decode Bob's keys from base64
30
+ const bobIdentityEd = fromBase64(bobBundle.identity_key);
31
+ const bobSignedPreKey = fromBase64(bobBundle.signed_prekey);
32
+ const bobSignedPreKeySig = fromBase64(bobBundle.signed_prekey_sig);
33
+ const bobOneTimePreKey = bobBundle.one_time_prekey
34
+ ? fromBase64(bobBundle.one_time_prekey)
35
+ : null;
36
+ // Verify Bob's signed prekey signature using his Ed25519 identity key
37
+ if (!verifySignature(bobSignedPreKeySig, bobSignedPreKey, bobIdentityEd)) {
38
+ throw new Error("X3DH: invalid signed prekey signature");
39
+ }
40
+ // Convert identity keys to X25519 for DH
41
+ const aliceIdentityX = ed25519SkToX25519(aliceIdentity.privateKey);
42
+ const bobIdentityX = ed25519PkToX25519(bobIdentityEd);
43
+ // Generate Alice's ephemeral X25519 keypair
44
+ const ephemeral = generateDHKeyPair();
45
+ // Compute DH values
46
+ const dh1 = dh(aliceIdentityX, bobSignedPreKey);
47
+ const dh2 = dh(ephemeral.privateKey, bobIdentityX);
48
+ const dh3 = dh(ephemeral.privateKey, bobSignedPreKey);
49
+ // Concatenate: F || DH1 || DH2 || DH3 [|| DH4]
50
+ const parts = [PADDING, dh1, dh2, dh3];
51
+ if (bobOneTimePreKey) {
52
+ parts.push(dh(ephemeral.privateKey, bobOneTimePreKey));
53
+ }
54
+ const totalLen = parts.reduce((sum, p) => sum + p.length, 0);
55
+ const ikm = new Uint8Array(totalLen);
56
+ let offset = 0;
57
+ for (const part of parts) {
58
+ ikm.set(part, offset);
59
+ offset += part.length;
60
+ }
61
+ // Derive shared key: HKDF(salt=zeros, ikm, info="haven_x3dh", L=32)
62
+ const salt = new Uint8Array(32);
63
+ const sharedKey = hkdf(salt, ikm, X3DH_INFO, 32);
64
+ // Associated data binds the session to both identities
65
+ const associatedData = new Uint8Array(64);
66
+ associatedData.set(aliceIdentity.publicKey, 0);
67
+ associatedData.set(bobIdentityEd, 32);
68
+ return { sharedKey, associatedData, ephemeralPublicKey: ephemeral.publicKey };
69
+ }
70
+ /**
71
+ * Bob (responder): compute X3DH shared secret from Alice's initial message.
72
+ *
73
+ * @param bobIdentity Bob's long-term Ed25519 identity keypair
74
+ * @param bobSignedPreKey Bob's signed prekey pair (the one used in his bundle)
75
+ * @param bobOneTimePreKey Bob's one-time prekey pair (null if not used)
76
+ * @param aliceIdentityPub Alice's Ed25519 public identity key (from the initial message)
77
+ * @param aliceEphemeralPub Alice's ephemeral X25519 public key (from the initial message)
78
+ * @returns The shared key and associated data
79
+ */
80
+ export function x3dhRespond(bobIdentity, bobSignedPreKey, bobOneTimePreKey, aliceIdentityPub, aliceEphemeralPub) {
81
+ // Convert identity keys to X25519 for DH
82
+ const bobIdentityX = ed25519SkToX25519(bobIdentity.privateKey);
83
+ const aliceIdentityX = ed25519PkToX25519(aliceIdentityPub);
84
+ // Compute DH values (mirror of Alice's computation)
85
+ const dh1 = dh(bobSignedPreKey.privateKey, aliceIdentityX);
86
+ const dh2 = dh(bobIdentityX, aliceEphemeralPub);
87
+ const dh3 = dh(bobSignedPreKey.privateKey, aliceEphemeralPub);
88
+ const parts = [PADDING, dh1, dh2, dh3];
89
+ if (bobOneTimePreKey) {
90
+ parts.push(dh(bobOneTimePreKey.privateKey, aliceEphemeralPub));
91
+ }
92
+ const totalLen = parts.reduce((sum, p) => sum + p.length, 0);
93
+ const ikm = new Uint8Array(totalLen);
94
+ let offset = 0;
95
+ for (const part of parts) {
96
+ ikm.set(part, offset);
97
+ offset += part.length;
98
+ }
99
+ const salt = new Uint8Array(32);
100
+ const sharedKey = hkdf(salt, ikm, X3DH_INFO, 32);
101
+ const associatedData = new Uint8Array(64);
102
+ associatedData.set(aliceIdentityPub, 0);
103
+ associatedData.set(bobIdentity.publicKey, 32);
104
+ return { sharedKey, associatedData };
105
+ }
106
+ //# sourceMappingURL=x3dh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x3dh.js","sourceRoot":"","sources":["../../src/crypto/x3dh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,276 @@
1
+ import { describe, it, expect, beforeAll } from "vitest";
2
+ import { initSodium, getSodium, toBase64 } from "../../crypto/utils.js";
3
+ import { HavenArchiveBuilder } from "../archive.js";
4
+ import { HavenArchiveReader } from "../reader.js";
5
+ import { signManifest, verifyManifest, computeFileHash } from "../signing.js";
6
+ // ── Test Data ───────────────────────────────────────────
7
+ function makeChannelExport(name = "general") {
8
+ return {
9
+ channel: {
10
+ id: "ch-001",
11
+ name,
12
+ type: "text",
13
+ encrypted: true,
14
+ category: "Text Channels",
15
+ created_at: "2025-03-01T00:00:00Z",
16
+ },
17
+ exported_at: "2026-02-22T12:00:00Z",
18
+ exported_by: "user-001",
19
+ message_count: 2,
20
+ date_range: { from: "2025-03-15T14:30:00Z", to: "2025-03-15T15:00:00Z" },
21
+ messages: [
22
+ {
23
+ id: "msg-001",
24
+ sender_id: "user-001",
25
+ sender_name: "alice",
26
+ sender_display_name: "Alice",
27
+ timestamp: "2025-03-15T14:30:00Z",
28
+ text: "Hello everyone!",
29
+ content_type: "text/plain",
30
+ formatting: null,
31
+ edited: false,
32
+ reply_to: null,
33
+ type: "user",
34
+ reactions: [{ emoji: "wave", count: 2, users: ["bob", "charlie"] }],
35
+ pinned: false,
36
+ attachments: [],
37
+ },
38
+ {
39
+ id: "msg-002",
40
+ sender_id: "user-002",
41
+ sender_name: "bob",
42
+ sender_display_name: "Bob",
43
+ timestamp: "2025-03-15T15:00:00Z",
44
+ text: "Hey Alice!",
45
+ content_type: "text/plain",
46
+ formatting: null,
47
+ edited: false,
48
+ reply_to: "msg-001",
49
+ type: "user",
50
+ reactions: [],
51
+ pinned: true,
52
+ attachments: [],
53
+ },
54
+ ],
55
+ };
56
+ }
57
+ function makeServerExport() {
58
+ return {
59
+ server: {
60
+ id: "srv-001",
61
+ name: "Test Server",
62
+ description: "A test server",
63
+ icon_url: null,
64
+ created_at: "2025-03-01T00:00:00Z",
65
+ },
66
+ categories: [{ id: "cat-001", name: "Text Channels", position: 0 }],
67
+ channels: [
68
+ {
69
+ id: "ch-001",
70
+ name: "general",
71
+ type: "text",
72
+ category_id: "cat-001",
73
+ position: 0,
74
+ encrypted: true,
75
+ is_private: false,
76
+ },
77
+ ],
78
+ roles: [
79
+ {
80
+ id: "role-001",
81
+ name: "Moderator",
82
+ color: "#3498db",
83
+ permissions: 8192,
84
+ position: 1,
85
+ is_default: false,
86
+ },
87
+ ],
88
+ members: [
89
+ {
90
+ user_id: "user-001",
91
+ username: "alice",
92
+ display_name: "Alice",
93
+ nickname: null,
94
+ roles: ["role-001"],
95
+ joined_at: "2025-03-01T00:00:00Z",
96
+ },
97
+ ],
98
+ emojis: [],
99
+ permission_overwrites: [],
100
+ };
101
+ }
102
+ // ── Tests ───────────────────────────────────────────────
103
+ beforeAll(async () => {
104
+ await initSodium();
105
+ });
106
+ describe("HavenArchiveBuilder + HavenArchiveReader", () => {
107
+ const metadata = {
108
+ exportedBy: { user_id: "user-001", username: "alice", identity_key: "" },
109
+ serverId: "srv-001",
110
+ instanceUrl: "https://haven.example.com",
111
+ };
112
+ it("round-trips channel data through build and read", async () => {
113
+ const sodium = getSodium();
114
+ const kp = sodium.crypto_sign_keypair();
115
+ const meta = {
116
+ ...metadata,
117
+ exportedBy: { ...metadata.exportedBy, identity_key: toBase64(kp.publicKey) },
118
+ };
119
+ const builder = new HavenArchiveBuilder(meta);
120
+ builder.addChannel(makeChannelExport());
121
+ builder.addServerMeta(makeServerExport());
122
+ builder.addAuditLog([{ action: "test", timestamp: "2026-02-22T12:00:00Z" }]);
123
+ const attachment = new TextEncoder().encode("fake image data");
124
+ builder.addAttachment("att-001", attachment);
125
+ const zip = await builder.build(kp.privateKey);
126
+ expect(zip).toBeInstanceOf(Uint8Array);
127
+ expect(zip.byteLength).toBeGreaterThan(0);
128
+ const reader = await HavenArchiveReader.fromBlob(zip);
129
+ const manifest = reader.getManifest();
130
+ expect(manifest.version).toBe(1);
131
+ expect(manifest.format).toBe("haven-export");
132
+ expect(manifest.exported_by.username).toBe("alice");
133
+ expect(manifest.server_id).toBe("srv-001");
134
+ expect(manifest.message_count).toBe(2);
135
+ expect(manifest.user_signature).toBeDefined();
136
+ expect(Object.keys(manifest.files)).toContain("channels/general.json");
137
+ expect(Object.keys(manifest.files)).toContain("server.json");
138
+ expect(Object.keys(manifest.files)).toContain("attachments/att-001.bin");
139
+ expect(Object.keys(manifest.files)).toContain("audit-log.json");
140
+ // Channel export round-trip
141
+ const channel = reader.getChannelExport("general");
142
+ expect(channel).not.toBeNull();
143
+ expect(channel.message_count).toBe(2);
144
+ expect(channel.messages[0].text).toBe("Hello everyone!");
145
+ expect(channel.messages[1].pinned).toBe(true);
146
+ // Server meta round-trip
147
+ const server = reader.getServerMeta();
148
+ expect(server).not.toBeNull();
149
+ expect(server.server.name).toBe("Test Server");
150
+ expect(server.roles).toHaveLength(1);
151
+ // Attachment round-trip
152
+ const att = reader.getAttachment("attachments/att-001.bin");
153
+ expect(att).not.toBeNull();
154
+ expect(new TextDecoder().decode(att)).toBe("fake image data");
155
+ // Audit log round-trip
156
+ const log = reader.getAuditLog();
157
+ expect(log).not.toBeNull();
158
+ expect(log).toHaveLength(1);
159
+ expect(log[0].action).toBe("test");
160
+ });
161
+ it("verification passes for untampered archive", async () => {
162
+ const sodium = getSodium();
163
+ const kp = sodium.crypto_sign_keypair();
164
+ const meta = {
165
+ ...metadata,
166
+ exportedBy: { ...metadata.exportedBy, identity_key: toBase64(kp.publicKey) },
167
+ };
168
+ const builder = new HavenArchiveBuilder(meta);
169
+ builder.addChannel(makeChannelExport());
170
+ const zip = await builder.build(kp.privateKey);
171
+ const reader = await HavenArchiveReader.fromBlob(zip);
172
+ const result = await reader.verify();
173
+ expect(result.valid).toBe(true);
174
+ expect(result.issues).toHaveLength(0);
175
+ });
176
+ it("verification fails for tampered file", async () => {
177
+ const sodium = getSodium();
178
+ const kp = sodium.crypto_sign_keypair();
179
+ const meta = {
180
+ ...metadata,
181
+ exportedBy: { ...metadata.exportedBy, identity_key: toBase64(kp.publicKey) },
182
+ };
183
+ const builder = new HavenArchiveBuilder(meta);
184
+ builder.addChannel(makeChannelExport());
185
+ const zip = await builder.build(kp.privateKey);
186
+ // Read the archive, tamper with a file, then verify
187
+ const reader = await HavenArchiveReader.fromBlob(zip);
188
+ // Access internal files and tamper
189
+ const manifest = reader.getManifest();
190
+ // Tamper: change a hash in the manifest to simulate file corruption
191
+ const firstFile = Object.keys(manifest.files)[0];
192
+ manifest.files[firstFile].sha256 = "0000000000000000000000000000000000000000000000000000000000000000";
193
+ // Re-verify with tampered manifest — the hash won't match
194
+ const result = await reader.verify();
195
+ // The reader still has the original manifest, so we need to test differently.
196
+ // Let's instead rebuild with tampered data.
197
+ // Actually the reader's verify checks files against its own manifest,
198
+ // and since we mutated the manifest object in place, it should fail.
199
+ expect(result.valid).toBe(false);
200
+ expect(result.issues.length).toBeGreaterThan(0);
201
+ expect(result.issues[0]).toContain("Hash mismatch");
202
+ });
203
+ it("verification fails for missing file referenced in manifest", async () => {
204
+ const sodium = getSodium();
205
+ const kp = sodium.crypto_sign_keypair();
206
+ const meta = {
207
+ ...metadata,
208
+ exportedBy: { ...metadata.exportedBy, identity_key: toBase64(kp.publicKey) },
209
+ };
210
+ const builder = new HavenArchiveBuilder(meta);
211
+ builder.addChannel(makeChannelExport());
212
+ const zip = await builder.build(kp.privateKey);
213
+ const reader = await HavenArchiveReader.fromBlob(zip);
214
+ const manifest = reader.getManifest();
215
+ // Add a fake file entry to manifest
216
+ manifest.files["channels/nonexistent.json"] = { sha256: "abc", size: 100 };
217
+ const result = await reader.verify();
218
+ expect(result.valid).toBe(false);
219
+ expect(result.issues.some((i) => i.includes("Missing file"))).toBe(true);
220
+ });
221
+ });
222
+ describe("signManifest / verifyManifest", () => {
223
+ it("round-trips signing and verification", async () => {
224
+ const sodium = getSodium();
225
+ const kp = sodium.crypto_sign_keypair();
226
+ const manifest = {
227
+ version: 1,
228
+ format: "haven-export",
229
+ exported_by: {
230
+ user_id: "user-001",
231
+ username: "alice",
232
+ identity_key: toBase64(kp.publicKey),
233
+ },
234
+ exported_at: "2026-02-22T12:00:00Z",
235
+ instance_url: "https://haven.example.com",
236
+ files: {},
237
+ message_count: 0,
238
+ date_range: { from: "2026-01-01T00:00:00Z", to: "2026-02-22T12:00:00Z" },
239
+ };
240
+ const sig = signManifest(manifest, kp.privateKey);
241
+ expect(typeof sig).toBe("string");
242
+ expect(sig.length).toBeGreaterThan(0);
243
+ expect(verifyManifest(manifest, sig, kp.publicKey)).toBe(true);
244
+ });
245
+ it("rejects invalid signature", async () => {
246
+ const sodium = getSodium();
247
+ const kp1 = sodium.crypto_sign_keypair();
248
+ const kp2 = sodium.crypto_sign_keypair();
249
+ const manifest = {
250
+ version: 1,
251
+ format: "haven-export",
252
+ exported_by: {
253
+ user_id: "user-001",
254
+ username: "alice",
255
+ identity_key: toBase64(kp1.publicKey),
256
+ },
257
+ exported_at: "2026-02-22T12:00:00Z",
258
+ instance_url: "https://haven.example.com",
259
+ files: {},
260
+ message_count: 0,
261
+ date_range: { from: "2026-01-01T00:00:00Z", to: "2026-02-22T12:00:00Z" },
262
+ };
263
+ const sig = signManifest(manifest, kp1.privateKey);
264
+ // Verify with wrong key should fail
265
+ expect(verifyManifest(manifest, sig, kp2.publicKey)).toBe(false);
266
+ });
267
+ });
268
+ describe("computeFileHash", () => {
269
+ it("produces consistent SHA-256 hex digest", async () => {
270
+ const data = new TextEncoder().encode("hello world");
271
+ const hash = await computeFileHash(data);
272
+ // SHA-256 of "hello world"
273
+ expect(hash).toBe("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9");
274
+ });
275
+ });
276
+ //# sourceMappingURL=archive.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.test.js","sourceRoot":"","sources":["../../../src/export/__tests__/archive.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { HavenChannelExport, HavenServerExport } from "./types.js";
2
+ interface ArchiveMetadata {
3
+ exportedBy: {
4
+ user_id: string;
5
+ username: string;
6
+ identity_key: string;
7
+ };
8
+ scope?: "server" | "channel" | "dm";
9
+ serverId?: string;
10
+ channelId?: string;
11
+ instanceUrl: string;
12
+ }
13
+ /**
14
+ * Builds a .haven archive (ZIP) containing channel exports,
15
+ * server metadata, attachments, and an optional audit log.
16
+ */
17
+ export declare class HavenArchiveBuilder {
18
+ private metadata;
19
+ private channels;
20
+ private attachments;
21
+ private serverMeta;
22
+ private auditLog;
23
+ private messageCount;
24
+ private earliestDate;
25
+ private latestDate;
26
+ constructor(metadata: ArchiveMetadata);
27
+ addChannel(channelExport: HavenChannelExport): void;
28
+ addAttachment(id: string, data: Uint8Array): void;
29
+ addServerMeta(serverExport: HavenServerExport): void;
30
+ addAuditLog(entries: any[]): void;
31
+ /**
32
+ * Build the .haven archive as a ZIP Uint8Array.
33
+ * If signingKey is provided, the manifest is signed with Ed25519.
34
+ */
35
+ build(signingKey?: Uint8Array): Promise<Uint8Array>;
36
+ private updateDateRange;
37
+ }
38
+ export {};
@@ -0,0 +1,107 @@
1
+ import { zipSync } from "fflate";
2
+ import { signManifest, computeFileHash } from "./signing.js";
3
+ /**
4
+ * Builds a .haven archive (ZIP) containing channel exports,
5
+ * server metadata, attachments, and an optional audit log.
6
+ */
7
+ export class HavenArchiveBuilder {
8
+ metadata;
9
+ channels = new Map();
10
+ attachments = new Map();
11
+ serverMeta = null;
12
+ auditLog = null;
13
+ messageCount = 0;
14
+ earliestDate = null;
15
+ latestDate = null;
16
+ constructor(metadata) {
17
+ this.metadata = metadata;
18
+ }
19
+ addChannel(channelExport) {
20
+ const name = channelExport.channel.name.replace(/[^a-zA-Z0-9_-]/g, "_");
21
+ const dir = channelExport.channel.type === "dm" || channelExport.channel.type === "group_dm"
22
+ ? "dms"
23
+ : "channels";
24
+ const key = `${dir}/${name}.json`;
25
+ const data = new TextEncoder().encode(JSON.stringify(channelExport, null, 2));
26
+ this.channels.set(key, data);
27
+ this.messageCount += channelExport.message_count;
28
+ this.updateDateRange(channelExport.date_range.from, channelExport.date_range.to);
29
+ }
30
+ addAttachment(id, data) {
31
+ this.attachments.set(`attachments/${id}.bin`, data);
32
+ }
33
+ addServerMeta(serverExport) {
34
+ this.serverMeta = new TextEncoder().encode(JSON.stringify(serverExport, null, 2));
35
+ }
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ addAuditLog(entries) {
38
+ this.auditLog = new TextEncoder().encode(JSON.stringify(entries, null, 2));
39
+ }
40
+ /**
41
+ * Build the .haven archive as a ZIP Uint8Array.
42
+ * If signingKey is provided, the manifest is signed with Ed25519.
43
+ */
44
+ async build(signingKey) {
45
+ const files = {};
46
+ const fileHashes = {};
47
+ // Collect all files
48
+ for (const [path, data] of this.channels) {
49
+ files[path] = data;
50
+ }
51
+ for (const [path, data] of this.attachments) {
52
+ files[path] = data;
53
+ }
54
+ if (this.serverMeta) {
55
+ files["server.json"] = this.serverMeta;
56
+ }
57
+ if (this.auditLog) {
58
+ files["audit-log.json"] = this.auditLog;
59
+ }
60
+ // Compute hashes for all files
61
+ for (const [path, data] of Object.entries(files)) {
62
+ fileHashes[path] = {
63
+ sha256: await computeFileHash(data),
64
+ size: data.byteLength,
65
+ };
66
+ }
67
+ // Build manifest
68
+ const manifest = {
69
+ version: 1,
70
+ format: "haven-export",
71
+ exported_by: this.metadata.exportedBy,
72
+ exported_at: new Date().toISOString(),
73
+ instance_url: this.metadata.instanceUrl,
74
+ files: fileHashes,
75
+ message_count: this.messageCount,
76
+ date_range: {
77
+ from: this.earliestDate ?? new Date().toISOString(),
78
+ to: this.latestDate ?? new Date().toISOString(),
79
+ },
80
+ };
81
+ if (this.metadata.serverId)
82
+ manifest.server_id = this.metadata.serverId;
83
+ if (this.metadata.channelId)
84
+ manifest.channel_id = this.metadata.channelId;
85
+ if (this.metadata.scope)
86
+ manifest.scope = this.metadata.scope;
87
+ // Sign if key provided
88
+ if (signingKey) {
89
+ manifest.user_signature = signManifest(manifest, signingKey);
90
+ }
91
+ // Add manifest to archive
92
+ files["manifest.json"] = new TextEncoder().encode(JSON.stringify(manifest, null, 2));
93
+ // Build ZIP
94
+ const zippable = {};
95
+ for (const [path, data] of Object.entries(files)) {
96
+ zippable[path] = data;
97
+ }
98
+ return zipSync(zippable);
99
+ }
100
+ updateDateRange(from, to) {
101
+ if (!this.earliestDate || from < this.earliestDate)
102
+ this.earliestDate = from;
103
+ if (!this.latestDate || to > this.latestDate)
104
+ this.latestDate = to;
105
+ }
106
+ }
107
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/export/archive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export type { HavenManifest, HavenChannelExport, HavenExportMessage, HavenAttachmentRef, HavenServerExport, } from "./types.js";
2
+ export { signManifest, verifyManifest, computeFileHash } from "./signing.js";
3
+ export { HavenArchiveBuilder } from "./archive.js";
4
+ export { HavenArchiveReader } from "./reader.js";
@@ -0,0 +1,4 @@
1
+ export { signManifest, verifyManifest, computeFileHash } from "./signing.js";
2
+ export { HavenArchiveBuilder } from "./archive.js";
3
+ export { HavenArchiveReader } from "./reader.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/export/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { HavenManifest, HavenChannelExport, HavenServerExport } from "./types.js";
2
+ /**
3
+ * Reads and verifies a .haven archive (ZIP).
4
+ */
5
+ export declare class HavenArchiveReader {
6
+ private files;
7
+ private manifest;
8
+ private constructor();
9
+ /**
10
+ * Parse a .haven archive from raw ZIP bytes.
11
+ */
12
+ static fromBlob(data: Uint8Array): Promise<HavenArchiveReader>;
13
+ getManifest(): HavenManifest;
14
+ getChannelExport(channelName: string): HavenChannelExport | null;
15
+ /** Return all channel exports (channels/ and dms/ directories). */
16
+ getChannelExports(): HavenChannelExport[];
17
+ getServerMeta(): HavenServerExport | null;
18
+ getAttachment(fileRef: string): Uint8Array | null;
19
+ getAuditLog(): any[] | null;
20
+ /**
21
+ * Verify archive integrity: file hashes and optional Ed25519 signature.
22
+ */
23
+ verify(): Promise<{
24
+ valid: boolean;
25
+ issues: string[];
26
+ }>;
27
+ }