@character-foundry/character-foundry 0.1.3 → 0.1.5

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 (100) hide show
  1. package/README.md +70 -0
  2. package/dist/app-framework.cjs +1742 -0
  3. package/dist/app-framework.cjs.map +1 -0
  4. package/dist/app-framework.d.cts +881 -0
  5. package/dist/app-framework.d.ts +881 -2
  6. package/dist/app-framework.js +1718 -1
  7. package/dist/app-framework.js.map +1 -1
  8. package/dist/charx.cjs +917 -0
  9. package/dist/charx.cjs.map +1 -0
  10. package/dist/charx.d.cts +640 -0
  11. package/dist/charx.d.ts +640 -2
  12. package/dist/charx.js +893 -1
  13. package/dist/charx.js.map +1 -1
  14. package/dist/core.cjs +668 -0
  15. package/dist/core.cjs.map +1 -0
  16. package/dist/core.d.cts +363 -0
  17. package/dist/core.d.ts +363 -2
  18. package/dist/core.js +644 -1
  19. package/dist/core.js.map +1 -1
  20. package/dist/exporter.cjs +7539 -0
  21. package/dist/exporter.cjs.map +1 -0
  22. package/dist/exporter.d.cts +681 -0
  23. package/dist/exporter.d.ts +681 -2
  24. package/dist/exporter.js +7522 -1
  25. package/dist/exporter.js.map +1 -1
  26. package/dist/federation.cjs +3915 -0
  27. package/dist/federation.cjs.map +1 -0
  28. package/dist/federation.d.cts +2951 -0
  29. package/dist/federation.d.ts +2951 -2
  30. package/dist/federation.js +3891 -1
  31. package/dist/federation.js.map +1 -1
  32. package/dist/index.cjs +9109 -0
  33. package/dist/index.cjs.map +1 -0
  34. package/dist/index.d.cts +1119 -0
  35. package/dist/index.d.ts +1113 -20
  36. package/dist/index.js +9092 -26
  37. package/dist/index.js.map +1 -1
  38. package/dist/loader.cjs +8923 -0
  39. package/dist/loader.cjs.map +1 -0
  40. package/dist/loader.d.cts +1037 -0
  41. package/dist/loader.d.ts +1037 -2
  42. package/dist/loader.js +8906 -1
  43. package/dist/loader.js.map +1 -1
  44. package/dist/lorebook.cjs +865 -0
  45. package/dist/lorebook.cjs.map +1 -0
  46. package/dist/lorebook.d.cts +1008 -0
  47. package/dist/lorebook.d.ts +1008 -2
  48. package/dist/lorebook.js +841 -1
  49. package/dist/lorebook.js.map +1 -1
  50. package/dist/media.cjs +6660 -0
  51. package/dist/media.cjs.map +1 -0
  52. package/dist/media.d.cts +87 -0
  53. package/dist/media.d.ts +87 -2
  54. package/dist/media.js +6643 -1
  55. package/dist/media.js.map +1 -1
  56. package/dist/normalizer.cjs +502 -0
  57. package/dist/normalizer.cjs.map +1 -0
  58. package/dist/normalizer.d.cts +1216 -0
  59. package/dist/normalizer.d.ts +1216 -2
  60. package/dist/normalizer.js +478 -1
  61. package/dist/normalizer.js.map +1 -1
  62. package/dist/png.cjs +778 -0
  63. package/dist/png.cjs.map +1 -0
  64. package/dist/png.d.cts +786 -0
  65. package/dist/png.d.ts +786 -2
  66. package/dist/png.js +754 -1
  67. package/dist/png.js.map +1 -1
  68. package/dist/schemas.cjs +799 -0
  69. package/dist/schemas.cjs.map +1 -0
  70. package/dist/schemas.d.cts +2178 -0
  71. package/dist/schemas.d.ts +2178 -2
  72. package/dist/schemas.js +775 -1
  73. package/dist/schemas.js.map +1 -1
  74. package/dist/tokenizers.cjs +153 -0
  75. package/dist/tokenizers.cjs.map +1 -0
  76. package/dist/tokenizers.d.cts +155 -0
  77. package/dist/tokenizers.d.ts +155 -2
  78. package/dist/tokenizers.js +129 -1
  79. package/dist/tokenizers.js.map +1 -1
  80. package/dist/voxta.cjs +7995 -0
  81. package/dist/voxta.cjs.map +1 -0
  82. package/dist/voxta.d.cts +1349 -0
  83. package/dist/voxta.d.ts +1349 -2
  84. package/dist/voxta.js +7978 -1
  85. package/dist/voxta.js.map +1 -1
  86. package/package.json +177 -45
  87. package/dist/app-framework.d.ts.map +0 -1
  88. package/dist/charx.d.ts.map +0 -1
  89. package/dist/core.d.ts.map +0 -1
  90. package/dist/exporter.d.ts.map +0 -1
  91. package/dist/federation.d.ts.map +0 -1
  92. package/dist/index.d.ts.map +0 -1
  93. package/dist/loader.d.ts.map +0 -1
  94. package/dist/lorebook.d.ts.map +0 -1
  95. package/dist/media.d.ts.map +0 -1
  96. package/dist/normalizer.d.ts.map +0 -1
  97. package/dist/png.d.ts.map +0 -1
  98. package/dist/schemas.d.ts.map +0 -1
  99. package/dist/tokenizers.d.ts.map +0 -1
  100. package/dist/voxta.d.ts.map +0 -1
@@ -1,2 +1,2951 @@
1
- export * from '@character-foundry/federation';
2
- //# sourceMappingURL=federation.d.ts.map
1
+ import { z } from 'zod';
2
+
3
+ declare const CCv3DataSchema: z.ZodObject<{
4
+ spec: z.ZodLiteral<"chara_card_v3">;
5
+ spec_version: z.ZodLiteral<"3.0">;
6
+ data: z.ZodObject<{
7
+ name: z.ZodDefault<z.ZodString>;
8
+ description: z.ZodDefault<z.ZodString>;
9
+ personality: z.ZodDefault<z.ZodNullable<z.ZodString>>;
10
+ scenario: z.ZodDefault<z.ZodString>;
11
+ first_mes: z.ZodDefault<z.ZodString>;
12
+ mes_example: z.ZodDefault<z.ZodNullable<z.ZodString>>;
13
+ creator: z.ZodDefault<z.ZodString>;
14
+ character_version: z.ZodDefault<z.ZodString>;
15
+ tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
16
+ group_only_greetings: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
17
+ creator_notes: z.ZodOptional<z.ZodString>;
18
+ system_prompt: z.ZodOptional<z.ZodString>;
19
+ post_history_instructions: z.ZodOptional<z.ZodString>;
20
+ alternate_greetings: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
21
+ character_book: z.ZodNullable<z.ZodOptional<z.ZodObject<{
22
+ name: z.ZodOptional<z.ZodString>;
23
+ description: z.ZodOptional<z.ZodString>;
24
+ scan_depth: z.ZodOptional<z.ZodNumber>;
25
+ token_budget: z.ZodOptional<z.ZodNumber>;
26
+ recursive_scanning: z.ZodOptional<z.ZodBoolean>;
27
+ extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
28
+ entries: z.ZodArray<z.ZodObject<{
29
+ keys: z.ZodArray<z.ZodString, "many">;
30
+ content: z.ZodString;
31
+ enabled: z.ZodBoolean;
32
+ insertion_order: z.ZodNumber;
33
+ case_sensitive: z.ZodOptional<z.ZodBoolean>;
34
+ name: z.ZodOptional<z.ZodString>;
35
+ priority: z.ZodOptional<z.ZodNumber>;
36
+ id: z.ZodOptional<z.ZodNumber>;
37
+ comment: z.ZodOptional<z.ZodString>;
38
+ selective: z.ZodOptional<z.ZodBoolean>;
39
+ secondary_keys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
40
+ constant: z.ZodOptional<z.ZodBoolean>;
41
+ position: z.ZodOptional<z.ZodEnum<[
42
+ "before_char",
43
+ "after_char"
44
+ ]>>;
45
+ extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
46
+ automation_id: z.ZodOptional<z.ZodString>;
47
+ role: z.ZodOptional<z.ZodEnum<[
48
+ "system",
49
+ "user",
50
+ "assistant"
51
+ ]>>;
52
+ group: z.ZodOptional<z.ZodString>;
53
+ scan_frequency: z.ZodOptional<z.ZodNumber>;
54
+ probability: z.ZodOptional<z.ZodNumber>;
55
+ use_regex: z.ZodOptional<z.ZodBoolean>;
56
+ depth: z.ZodOptional<z.ZodNumber>;
57
+ selective_logic: z.ZodOptional<z.ZodEnum<[
58
+ "AND",
59
+ "NOT"
60
+ ]>>;
61
+ }, "strip", z.ZodTypeAny, {
62
+ keys: string[];
63
+ content: string;
64
+ enabled: boolean;
65
+ insertion_order: number;
66
+ name?: string | undefined;
67
+ extensions?: Record<string, unknown> | undefined;
68
+ case_sensitive?: boolean | undefined;
69
+ priority?: number | undefined;
70
+ id?: number | undefined;
71
+ comment?: string | undefined;
72
+ selective?: boolean | undefined;
73
+ secondary_keys?: string[] | undefined;
74
+ constant?: boolean | undefined;
75
+ position?: "before_char" | "after_char" | undefined;
76
+ automation_id?: string | undefined;
77
+ role?: "system" | "user" | "assistant" | undefined;
78
+ group?: string | undefined;
79
+ scan_frequency?: number | undefined;
80
+ probability?: number | undefined;
81
+ use_regex?: boolean | undefined;
82
+ depth?: number | undefined;
83
+ selective_logic?: "AND" | "NOT" | undefined;
84
+ }, {
85
+ keys: string[];
86
+ content: string;
87
+ enabled: boolean;
88
+ insertion_order: number;
89
+ name?: string | undefined;
90
+ extensions?: Record<string, unknown> | undefined;
91
+ case_sensitive?: boolean | undefined;
92
+ priority?: number | undefined;
93
+ id?: number | undefined;
94
+ comment?: string | undefined;
95
+ selective?: boolean | undefined;
96
+ secondary_keys?: string[] | undefined;
97
+ constant?: boolean | undefined;
98
+ position?: "before_char" | "after_char" | undefined;
99
+ automation_id?: string | undefined;
100
+ role?: "system" | "user" | "assistant" | undefined;
101
+ group?: string | undefined;
102
+ scan_frequency?: number | undefined;
103
+ probability?: number | undefined;
104
+ use_regex?: boolean | undefined;
105
+ depth?: number | undefined;
106
+ selective_logic?: "AND" | "NOT" | undefined;
107
+ }>, "many">;
108
+ }, "strip", z.ZodTypeAny, {
109
+ entries: {
110
+ keys: string[];
111
+ content: string;
112
+ enabled: boolean;
113
+ insertion_order: number;
114
+ name?: string | undefined;
115
+ extensions?: Record<string, unknown> | undefined;
116
+ case_sensitive?: boolean | undefined;
117
+ priority?: number | undefined;
118
+ id?: number | undefined;
119
+ comment?: string | undefined;
120
+ selective?: boolean | undefined;
121
+ secondary_keys?: string[] | undefined;
122
+ constant?: boolean | undefined;
123
+ position?: "before_char" | "after_char" | undefined;
124
+ automation_id?: string | undefined;
125
+ role?: "system" | "user" | "assistant" | undefined;
126
+ group?: string | undefined;
127
+ scan_frequency?: number | undefined;
128
+ probability?: number | undefined;
129
+ use_regex?: boolean | undefined;
130
+ depth?: number | undefined;
131
+ selective_logic?: "AND" | "NOT" | undefined;
132
+ }[];
133
+ name?: string | undefined;
134
+ description?: string | undefined;
135
+ scan_depth?: number | undefined;
136
+ token_budget?: number | undefined;
137
+ recursive_scanning?: boolean | undefined;
138
+ extensions?: Record<string, unknown> | undefined;
139
+ }, {
140
+ entries: {
141
+ keys: string[];
142
+ content: string;
143
+ enabled: boolean;
144
+ insertion_order: number;
145
+ name?: string | undefined;
146
+ extensions?: Record<string, unknown> | undefined;
147
+ case_sensitive?: boolean | undefined;
148
+ priority?: number | undefined;
149
+ id?: number | undefined;
150
+ comment?: string | undefined;
151
+ selective?: boolean | undefined;
152
+ secondary_keys?: string[] | undefined;
153
+ constant?: boolean | undefined;
154
+ position?: "before_char" | "after_char" | undefined;
155
+ automation_id?: string | undefined;
156
+ role?: "system" | "user" | "assistant" | undefined;
157
+ group?: string | undefined;
158
+ scan_frequency?: number | undefined;
159
+ probability?: number | undefined;
160
+ use_regex?: boolean | undefined;
161
+ depth?: number | undefined;
162
+ selective_logic?: "AND" | "NOT" | undefined;
163
+ }[];
164
+ name?: string | undefined;
165
+ description?: string | undefined;
166
+ scan_depth?: number | undefined;
167
+ token_budget?: number | undefined;
168
+ recursive_scanning?: boolean | undefined;
169
+ extensions?: Record<string, unknown> | undefined;
170
+ }>>>;
171
+ extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
172
+ assets: z.ZodOptional<z.ZodArray<z.ZodObject<{
173
+ type: z.ZodEnum<[
174
+ "icon",
175
+ "background",
176
+ "emotion",
177
+ "user_icon",
178
+ "sound",
179
+ "video",
180
+ "custom",
181
+ "x-risu-asset"
182
+ ]>;
183
+ uri: z.ZodString;
184
+ name: z.ZodString;
185
+ ext: z.ZodString;
186
+ }, "strip", z.ZodTypeAny, {
187
+ name: string;
188
+ type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
189
+ uri: string;
190
+ ext: string;
191
+ }, {
192
+ name: string;
193
+ type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
194
+ uri: string;
195
+ ext: string;
196
+ }>, "many">>;
197
+ nickname: z.ZodOptional<z.ZodString>;
198
+ creator_notes_multilingual: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
199
+ source: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
200
+ creation_date: z.ZodOptional<z.ZodNumber>;
201
+ modification_date: z.ZodOptional<z.ZodNumber>;
202
+ }, "strip", z.ZodTypeAny, {
203
+ name: string;
204
+ description: string;
205
+ personality: string | null;
206
+ scenario: string;
207
+ first_mes: string;
208
+ mes_example: string | null;
209
+ tags: string[];
210
+ creator: string;
211
+ character_version: string;
212
+ group_only_greetings: string[];
213
+ extensions?: Record<string, unknown> | undefined;
214
+ creator_notes?: string | undefined;
215
+ system_prompt?: string | undefined;
216
+ post_history_instructions?: string | undefined;
217
+ alternate_greetings?: string[] | undefined;
218
+ character_book?: {
219
+ entries: {
220
+ keys: string[];
221
+ content: string;
222
+ enabled: boolean;
223
+ insertion_order: number;
224
+ name?: string | undefined;
225
+ extensions?: Record<string, unknown> | undefined;
226
+ case_sensitive?: boolean | undefined;
227
+ priority?: number | undefined;
228
+ id?: number | undefined;
229
+ comment?: string | undefined;
230
+ selective?: boolean | undefined;
231
+ secondary_keys?: string[] | undefined;
232
+ constant?: boolean | undefined;
233
+ position?: "before_char" | "after_char" | undefined;
234
+ automation_id?: string | undefined;
235
+ role?: "system" | "user" | "assistant" | undefined;
236
+ group?: string | undefined;
237
+ scan_frequency?: number | undefined;
238
+ probability?: number | undefined;
239
+ use_regex?: boolean | undefined;
240
+ depth?: number | undefined;
241
+ selective_logic?: "AND" | "NOT" | undefined;
242
+ }[];
243
+ name?: string | undefined;
244
+ description?: string | undefined;
245
+ scan_depth?: number | undefined;
246
+ token_budget?: number | undefined;
247
+ recursive_scanning?: boolean | undefined;
248
+ extensions?: Record<string, unknown> | undefined;
249
+ } | null | undefined;
250
+ assets?: {
251
+ name: string;
252
+ type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
253
+ uri: string;
254
+ ext: string;
255
+ }[] | undefined;
256
+ nickname?: string | undefined;
257
+ creator_notes_multilingual?: Record<string, string> | undefined;
258
+ source?: string[] | undefined;
259
+ creation_date?: number | undefined;
260
+ modification_date?: number | undefined;
261
+ }, {
262
+ name?: string | undefined;
263
+ description?: string | undefined;
264
+ extensions?: Record<string, unknown> | undefined;
265
+ personality?: string | null | undefined;
266
+ scenario?: string | undefined;
267
+ first_mes?: string | undefined;
268
+ mes_example?: string | null | undefined;
269
+ creator_notes?: string | undefined;
270
+ system_prompt?: string | undefined;
271
+ post_history_instructions?: string | undefined;
272
+ alternate_greetings?: string[] | undefined;
273
+ character_book?: {
274
+ entries: {
275
+ keys: string[];
276
+ content: string;
277
+ enabled: boolean;
278
+ insertion_order: number;
279
+ name?: string | undefined;
280
+ extensions?: Record<string, unknown> | undefined;
281
+ case_sensitive?: boolean | undefined;
282
+ priority?: number | undefined;
283
+ id?: number | undefined;
284
+ comment?: string | undefined;
285
+ selective?: boolean | undefined;
286
+ secondary_keys?: string[] | undefined;
287
+ constant?: boolean | undefined;
288
+ position?: "before_char" | "after_char" | undefined;
289
+ automation_id?: string | undefined;
290
+ role?: "system" | "user" | "assistant" | undefined;
291
+ group?: string | undefined;
292
+ scan_frequency?: number | undefined;
293
+ probability?: number | undefined;
294
+ use_regex?: boolean | undefined;
295
+ depth?: number | undefined;
296
+ selective_logic?: "AND" | "NOT" | undefined;
297
+ }[];
298
+ name?: string | undefined;
299
+ description?: string | undefined;
300
+ scan_depth?: number | undefined;
301
+ token_budget?: number | undefined;
302
+ recursive_scanning?: boolean | undefined;
303
+ extensions?: Record<string, unknown> | undefined;
304
+ } | null | undefined;
305
+ tags?: string[] | undefined;
306
+ creator?: string | undefined;
307
+ character_version?: string | undefined;
308
+ group_only_greetings?: string[] | undefined;
309
+ assets?: {
310
+ name: string;
311
+ type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
312
+ uri: string;
313
+ ext: string;
314
+ }[] | undefined;
315
+ nickname?: string | undefined;
316
+ creator_notes_multilingual?: Record<string, string> | undefined;
317
+ source?: string[] | undefined;
318
+ creation_date?: number | undefined;
319
+ modification_date?: number | undefined;
320
+ }>;
321
+ }, "strip", z.ZodTypeAny, {
322
+ data: {
323
+ name: string;
324
+ description: string;
325
+ personality: string | null;
326
+ scenario: string;
327
+ first_mes: string;
328
+ mes_example: string | null;
329
+ tags: string[];
330
+ creator: string;
331
+ character_version: string;
332
+ group_only_greetings: string[];
333
+ extensions?: Record<string, unknown> | undefined;
334
+ creator_notes?: string | undefined;
335
+ system_prompt?: string | undefined;
336
+ post_history_instructions?: string | undefined;
337
+ alternate_greetings?: string[] | undefined;
338
+ character_book?: {
339
+ entries: {
340
+ keys: string[];
341
+ content: string;
342
+ enabled: boolean;
343
+ insertion_order: number;
344
+ name?: string | undefined;
345
+ extensions?: Record<string, unknown> | undefined;
346
+ case_sensitive?: boolean | undefined;
347
+ priority?: number | undefined;
348
+ id?: number | undefined;
349
+ comment?: string | undefined;
350
+ selective?: boolean | undefined;
351
+ secondary_keys?: string[] | undefined;
352
+ constant?: boolean | undefined;
353
+ position?: "before_char" | "after_char" | undefined;
354
+ automation_id?: string | undefined;
355
+ role?: "system" | "user" | "assistant" | undefined;
356
+ group?: string | undefined;
357
+ scan_frequency?: number | undefined;
358
+ probability?: number | undefined;
359
+ use_regex?: boolean | undefined;
360
+ depth?: number | undefined;
361
+ selective_logic?: "AND" | "NOT" | undefined;
362
+ }[];
363
+ name?: string | undefined;
364
+ description?: string | undefined;
365
+ scan_depth?: number | undefined;
366
+ token_budget?: number | undefined;
367
+ recursive_scanning?: boolean | undefined;
368
+ extensions?: Record<string, unknown> | undefined;
369
+ } | null | undefined;
370
+ assets?: {
371
+ name: string;
372
+ type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
373
+ uri: string;
374
+ ext: string;
375
+ }[] | undefined;
376
+ nickname?: string | undefined;
377
+ creator_notes_multilingual?: Record<string, string> | undefined;
378
+ source?: string[] | undefined;
379
+ creation_date?: number | undefined;
380
+ modification_date?: number | undefined;
381
+ };
382
+ spec: "chara_card_v3";
383
+ spec_version: "3.0";
384
+ }, {
385
+ data: {
386
+ name?: string | undefined;
387
+ description?: string | undefined;
388
+ extensions?: Record<string, unknown> | undefined;
389
+ personality?: string | null | undefined;
390
+ scenario?: string | undefined;
391
+ first_mes?: string | undefined;
392
+ mes_example?: string | null | undefined;
393
+ creator_notes?: string | undefined;
394
+ system_prompt?: string | undefined;
395
+ post_history_instructions?: string | undefined;
396
+ alternate_greetings?: string[] | undefined;
397
+ character_book?: {
398
+ entries: {
399
+ keys: string[];
400
+ content: string;
401
+ enabled: boolean;
402
+ insertion_order: number;
403
+ name?: string | undefined;
404
+ extensions?: Record<string, unknown> | undefined;
405
+ case_sensitive?: boolean | undefined;
406
+ priority?: number | undefined;
407
+ id?: number | undefined;
408
+ comment?: string | undefined;
409
+ selective?: boolean | undefined;
410
+ secondary_keys?: string[] | undefined;
411
+ constant?: boolean | undefined;
412
+ position?: "before_char" | "after_char" | undefined;
413
+ automation_id?: string | undefined;
414
+ role?: "system" | "user" | "assistant" | undefined;
415
+ group?: string | undefined;
416
+ scan_frequency?: number | undefined;
417
+ probability?: number | undefined;
418
+ use_regex?: boolean | undefined;
419
+ depth?: number | undefined;
420
+ selective_logic?: "AND" | "NOT" | undefined;
421
+ }[];
422
+ name?: string | undefined;
423
+ description?: string | undefined;
424
+ scan_depth?: number | undefined;
425
+ token_budget?: number | undefined;
426
+ recursive_scanning?: boolean | undefined;
427
+ extensions?: Record<string, unknown> | undefined;
428
+ } | null | undefined;
429
+ tags?: string[] | undefined;
430
+ creator?: string | undefined;
431
+ character_version?: string | undefined;
432
+ group_only_greetings?: string[] | undefined;
433
+ assets?: {
434
+ name: string;
435
+ type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
436
+ uri: string;
437
+ ext: string;
438
+ }[] | undefined;
439
+ nickname?: string | undefined;
440
+ creator_notes_multilingual?: Record<string, string> | undefined;
441
+ source?: string[] | undefined;
442
+ creation_date?: number | undefined;
443
+ modification_date?: number | undefined;
444
+ };
445
+ spec: "chara_card_v3";
446
+ spec_version: "3.0";
447
+ }>;
448
+ /**
449
+ * Character Card v3 full structure
450
+ */
451
+ export type CCv3Data = z.infer<typeof CCv3DataSchema>;
452
+ /**
453
+ * Federation Types
454
+ *
455
+ * Core types for ActivityPub-based character card federation.
456
+ */
457
+ /**
458
+ * Unique identifier for a federated card
459
+ * Format: {platform}:{id} or full URI
460
+ */
461
+ export type FederatedCardId = string;
462
+ /**
463
+ * Platform identifier
464
+ */
465
+ export type PlatformId = "archive" | "hub" | "editor" | "sillytavern" | "risu" | "chub" | "custom";
466
+ /**
467
+ * ActivityPub Actor representing a creator/user
468
+ */
469
+ export interface FederatedActor {
470
+ /** ActivityPub context */
471
+ "@context"?: ActivityPubContext;
472
+ /** ActivityPub ID (URI) */
473
+ id: string;
474
+ /** Actor type */
475
+ type: "Person" | "Service" | "Application";
476
+ /** Display name */
477
+ name: string;
478
+ /** Username/handle */
479
+ preferredUsername: string;
480
+ /** Profile summary */
481
+ summary?: string;
482
+ /** Avatar URL */
483
+ icon?: string;
484
+ /** Inbox URL for receiving activities */
485
+ inbox: string;
486
+ /** Outbox URL for published activities */
487
+ outbox: string;
488
+ /** Followers collection URL */
489
+ followers?: string;
490
+ /** Following collection URL */
491
+ following?: string;
492
+ /** Public key for HTTP signatures */
493
+ publicKey?: {
494
+ id: string;
495
+ owner: string;
496
+ publicKeyPem: string;
497
+ };
498
+ }
499
+ /**
500
+ * ActivityPub context type - can be a string, or an array of strings and objects
501
+ */
502
+ export type ActivityPubContext = string | (string | Record<string, unknown>)[];
503
+ /**
504
+ * ActivityPub Object representing a character card
505
+ */
506
+ export interface FederatedCard {
507
+ /** ActivityPub context */
508
+ "@context": ActivityPubContext;
509
+ /** Unique ID (URI) */
510
+ id: string;
511
+ /** Object type */
512
+ type: "Note" | "Article" | "Document";
513
+ /** Card name/title */
514
+ name: string;
515
+ /** Card description/summary */
516
+ summary?: string;
517
+ /** Full card content (JSON stringified CCv3Data) */
518
+ content: string;
519
+ /** Content media type */
520
+ mediaType: "application/json";
521
+ /** Creator actor */
522
+ attributedTo: string;
523
+ /** Publication timestamp */
524
+ published: string;
525
+ /** Last update timestamp */
526
+ updated?: string;
527
+ /** Tags/hashtags */
528
+ tag?: Array<{
529
+ type: "Hashtag";
530
+ name: string;
531
+ href?: string;
532
+ }>;
533
+ /** Attached assets */
534
+ attachment?: Array<{
535
+ type: "Document" | "Image" | "Audio";
536
+ mediaType: string;
537
+ url: string;
538
+ name?: string;
539
+ }>;
540
+ /** Source platform */
541
+ source?: {
542
+ platform: PlatformId;
543
+ id: string;
544
+ url?: string;
545
+ };
546
+ /** Card version */
547
+ "character:version"?: string;
548
+ /** Card spec version */
549
+ "character:spec"?: string;
550
+ }
551
+ /**
552
+ * ActivityPub Activity types for cards
553
+ */
554
+ export type ActivityType = "Create" | "Update" | "Delete" | "Announce" | "Like" | "Follow" | "Undo" | "Fork" | "Install" | "Flag" | "Block";
555
+ /**
556
+ * Platform role in federation topology
557
+ */
558
+ export type PlatformRole = "publisher" | "hub" | "architect" | "consumer";
559
+ /**
560
+ * Fork reference stored in card extensions
561
+ * Path: card.data.extensions['character-foundry'].forkedFrom
562
+ */
563
+ export interface ForkReference {
564
+ /** Federated URI of the source card */
565
+ federatedId: string;
566
+ /** Source platform identifier */
567
+ platform: PlatformId;
568
+ /** Timestamp when forked */
569
+ forkedAt: string;
570
+ /** Source version hash at fork time */
571
+ sourceVersionHash?: string;
572
+ }
573
+ /**
574
+ * Fork notification received by source instance
575
+ */
576
+ export interface ForkNotification {
577
+ /** Federated URI of the fork */
578
+ forkId: string;
579
+ /** Actor who created the fork */
580
+ actorId: string;
581
+ /** Platform where fork was created */
582
+ platform: PlatformId;
583
+ /** Timestamp of fork */
584
+ timestamp: string;
585
+ }
586
+ /**
587
+ * Install notification received by source instance
588
+ * Consumers (SillyTavern, Voxta) send these when a card is installed
589
+ */
590
+ export interface InstallNotification {
591
+ /** Platform where card was installed */
592
+ platform: PlatformId;
593
+ /** Actor/user who installed (if known) */
594
+ actorId?: string;
595
+ /** Timestamp of installation */
596
+ timestamp: string;
597
+ }
598
+ /**
599
+ * Aggregated stats for a card
600
+ */
601
+ export interface CardStats {
602
+ /** Total known installs across all platforms */
603
+ installCount: number;
604
+ /** Install counts per platform */
605
+ installsByPlatform: Partial<Record<PlatformId, number>>;
606
+ /** Fork count (separate from forkNotifications for efficiency) */
607
+ forkCount: number;
608
+ /** Like/favorite count */
609
+ likeCount: number;
610
+ /** Last time any stat was updated */
611
+ lastUpdated: string;
612
+ }
613
+ /**
614
+ * Install activity - consumer notifies hub about card installation
615
+ */
616
+ export interface InstallActivity extends Omit<FederatedActivity, "type" | "object"> {
617
+ type: "Install";
618
+ /** Card URI that was installed */
619
+ object: string;
620
+ /** Platform where installed */
621
+ target?: {
622
+ type: "Application";
623
+ name: PlatformId;
624
+ };
625
+ }
626
+ /**
627
+ * ActivityPub Activity
628
+ */
629
+ export interface FederatedActivity {
630
+ "@context": ActivityPubContext;
631
+ id: string;
632
+ type: ActivityType;
633
+ actor: string;
634
+ object: string | FederatedCard;
635
+ published: string;
636
+ to?: string[];
637
+ cc?: string[];
638
+ }
639
+ /**
640
+ * Sync state for a card across platforms
641
+ */
642
+ export interface CardSyncState {
643
+ /** Local card ID */
644
+ localId: string;
645
+ /** Federated card ID (ActivityPub URI) */
646
+ federatedId: string;
647
+ /** Platform-specific IDs (only set for synced platforms) */
648
+ platformIds: Partial<Record<PlatformId, string>>;
649
+ /** Last sync timestamp per platform (only set for synced platforms) */
650
+ lastSync: Partial<Record<PlatformId, string>>;
651
+ /** Current version hash */
652
+ versionHash: string;
653
+ /** Sync status */
654
+ status: "synced" | "pending" | "conflict" | "error";
655
+ /** Conflict details if any */
656
+ conflict?: {
657
+ localVersion: string;
658
+ remoteVersion: string;
659
+ remotePlatform: PlatformId;
660
+ };
661
+ /** Fork reference if this card is a fork */
662
+ forkedFrom?: ForkReference;
663
+ /** Count of known forks of this card */
664
+ forksCount?: number;
665
+ /** References to known forks (capped at 100 to prevent unbounded growth) */
666
+ forkNotifications?: ForkNotification[];
667
+ /** Aggregated stats for this card */
668
+ stats?: CardStats;
669
+ }
670
+ /**
671
+ * Sync operation
672
+ */
673
+ export interface SyncOperation {
674
+ type: "push" | "pull" | "delete" | "resolve" | "fork";
675
+ cardId: FederatedCardId;
676
+ sourcePlatform: PlatformId;
677
+ targetPlatform: PlatformId;
678
+ timestamp: string;
679
+ }
680
+ /**
681
+ * Sync result
682
+ */
683
+ export interface SyncResult {
684
+ success: boolean;
685
+ operation: SyncOperation;
686
+ newState?: CardSyncState;
687
+ error?: string;
688
+ }
689
+ /**
690
+ * Platform adapter interface
691
+ * Each platform implements this to enable federation
692
+ */
693
+ export interface PlatformAdapter {
694
+ /** Platform identifier */
695
+ readonly platform: PlatformId;
696
+ /** Platform display name */
697
+ readonly displayName: string;
698
+ /** Whether the platform is currently available */
699
+ isAvailable(): Promise<boolean>;
700
+ /** Get a card by local ID */
701
+ getCard(localId: string): Promise<CCv3Data | null>;
702
+ /** List all cards (with optional pagination) */
703
+ listCards(options?: {
704
+ limit?: number;
705
+ offset?: number;
706
+ since?: string;
707
+ }): Promise<Array<{
708
+ id: string;
709
+ card: CCv3Data;
710
+ updatedAt: string;
711
+ }>>;
712
+ /** Save/update a card */
713
+ saveCard(card: CCv3Data, localId?: string): Promise<string>;
714
+ /** Delete a card */
715
+ deleteCard(localId: string): Promise<boolean>;
716
+ /** Get card assets */
717
+ getAssets(localId: string): Promise<Array<{
718
+ name: string;
719
+ type: string;
720
+ data: Uint8Array;
721
+ }>>;
722
+ /** Get the last modified timestamp for a card */
723
+ getLastModified(localId: string): Promise<string | null>;
724
+ }
725
+ /**
726
+ * Federation hub configuration
727
+ */
728
+ export interface FederationConfig {
729
+ /** This instance's ActivityPub actor */
730
+ actor: FederatedActor;
731
+ /** Registered platform adapters */
732
+ platforms: Map<PlatformId, PlatformAdapter>;
733
+ /** Sync state storage */
734
+ stateStore: SyncStateStore;
735
+ /** HTTP signature keys */
736
+ keys?: {
737
+ privateKey: string;
738
+ publicKey: string;
739
+ };
740
+ }
741
+ /**
742
+ * Sync state storage interface
743
+ */
744
+ export interface SyncStateStore {
745
+ /** Get sync state for a card */
746
+ get(federatedId: string): Promise<CardSyncState | null>;
747
+ /** Save sync state */
748
+ set(state: CardSyncState): Promise<void>;
749
+ /** Delete sync state */
750
+ delete(federatedId: string): Promise<void>;
751
+ /** List all sync states */
752
+ list(): Promise<CardSyncState[]>;
753
+ /** Find by platform ID */
754
+ findByPlatformId(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
755
+ }
756
+ /**
757
+ * Federation event types
758
+ */
759
+ export type FederationEventType = "card:created" | "card:updated" | "card:deleted" | "card:synced" | "card:conflict" | "card:forked" | "card:fork-received" | "card:installed" | "card:install-received" | "sync:started" | "sync:completed" | "sync:failed" | "sync:skipped" | "actor:followed" | "actor:unfollowed" | "moderation:report-received" | "moderation:action-taken" | "moderation:instance-blocked" | "moderation:instance-unblocked" | "moderation:policy-matched" | "moderation:policy-rejected";
760
+ /**
761
+ * Federation event
762
+ */
763
+ export interface FederationEvent {
764
+ type: FederationEventType;
765
+ timestamp: string;
766
+ data: unknown;
767
+ }
768
+ /**
769
+ * Event listener
770
+ */
771
+ export type FederationEventListener = (event: FederationEvent) => void | Promise<void>;
772
+ /**
773
+ * Fork activity - custom ActivityPub extension
774
+ */
775
+ export interface ForkActivity extends Omit<FederatedActivity, "type" | "object"> {
776
+ type: "Fork";
777
+ /** Source card URI being forked */
778
+ object: string;
779
+ /** The newly created fork */
780
+ result: FederatedCard;
781
+ }
782
+ /**
783
+ * Fork operation result
784
+ */
785
+ export interface ForkResult {
786
+ success: boolean;
787
+ operation: SyncOperation;
788
+ forkState?: CardSyncState;
789
+ sourceFederatedId?: string;
790
+ forkFederatedId?: string;
791
+ error?: string;
792
+ }
793
+ /**
794
+ * Inbox handler result
795
+ */
796
+ export interface InboxResult {
797
+ accepted: boolean;
798
+ error?: string;
799
+ activityType?: ActivityType;
800
+ }
801
+ /**
802
+ * Inbox handler options
803
+ */
804
+ export interface InboxHandlerOptions {
805
+ /** Fetch actor by ID for signature verification */
806
+ fetchActor: (actorId: string) => Promise<FederatedActor | null>;
807
+ /** Strict mode for signature validation */
808
+ strictMode?: boolean;
809
+ /** Maximum age for signatures in seconds (default 300) */
810
+ maxAge?: number;
811
+ }
812
+ /**
813
+ * ActivityPub Utilities
814
+ *
815
+ * Convert character cards to/from ActivityPub format.
816
+ */
817
+ /**
818
+ * ActivityPub context for character cards
819
+ */
820
+ export declare const ACTIVITY_CONTEXT: (string | {
821
+ character: string;
822
+ "character:version": {
823
+ "@id": string;
824
+ };
825
+ "character:spec": {
826
+ "@id": string;
827
+ };
828
+ })[];
829
+ /**
830
+ * Extended ActivityPub context for Fork activities
831
+ * Includes custom character-foundry namespace for fork semantics
832
+ */
833
+ export declare const FORK_ACTIVITY_CONTEXT: (string | {
834
+ character: string;
835
+ "character:version": {
836
+ "@id": string;
837
+ };
838
+ "character:spec": {
839
+ "@id": string;
840
+ };
841
+ Fork: string;
842
+ forkedFrom: {
843
+ "@id": string;
844
+ "@type": string;
845
+ };
846
+ })[];
847
+ /**
848
+ * Extended ActivityPub context for Install activities
849
+ * Used by consumers (SillyTavern, Voxta) to notify hub about installations
850
+ */
851
+ export declare const INSTALL_ACTIVITY_CONTEXT: (string | {
852
+ character: string;
853
+ Install: string;
854
+ })[];
855
+ /**
856
+ * Generate a unique ID for a federated card
857
+ *
858
+ * @security localId is URI-encoded to prevent path traversal and query injection.
859
+ * Characters like '/', '?', '#', and spaces are safely escaped.
860
+ */
861
+ export declare function generateCardId(baseUrl: string, localId: string): string;
862
+ /**
863
+ * Generate a unique ID for an activity using crypto-grade randomness
864
+ */
865
+ export declare function generateActivityId(baseUrl: string): string;
866
+ /**
867
+ * Convert a CCv3 card to ActivityPub FederatedCard format
868
+ */
869
+ export declare function cardToActivityPub(card: CCv3Data, options: {
870
+ id: string;
871
+ actorId: string;
872
+ published?: string;
873
+ updated?: string;
874
+ sourcePlatform?: PlatformId;
875
+ sourceId?: string;
876
+ sourceUrl?: string;
877
+ attachments?: Array<{
878
+ type: "Image" | "Audio" | "Document";
879
+ mediaType: string;
880
+ url: string;
881
+ name?: string;
882
+ }>;
883
+ }): FederatedCard;
884
+ /**
885
+ * Extract CCv3 card from ActivityPub FederatedCard
886
+ */
887
+ export declare function cardFromActivityPub(federatedCard: FederatedCard): CCv3Data;
888
+ /**
889
+ * Create a Create activity for a new card
890
+ */
891
+ export declare function createCreateActivity(card: FederatedCard, actorId: string, baseUrl: string, recipients?: {
892
+ to?: string[];
893
+ cc?: string[];
894
+ }): FederatedActivity;
895
+ /**
896
+ * Create an Update activity for a modified card
897
+ */
898
+ export declare function createUpdateActivity(card: FederatedCard, actorId: string, baseUrl: string, recipients?: {
899
+ to?: string[];
900
+ cc?: string[];
901
+ }): FederatedActivity;
902
+ /**
903
+ * Create a Delete activity for a removed card
904
+ */
905
+ export declare function createDeleteActivity(cardId: string, actorId: string, baseUrl: string, recipients?: {
906
+ to?: string[];
907
+ cc?: string[];
908
+ }): FederatedActivity;
909
+ /**
910
+ * Create an Announce (reshare) activity
911
+ */
912
+ export declare function createAnnounceActivity(cardId: string, actorId: string, baseUrl: string, recipients?: {
913
+ to?: string[];
914
+ cc?: string[];
915
+ }): FederatedActivity;
916
+ /**
917
+ * Create a Like activity
918
+ */
919
+ export declare function createLikeActivity(cardId: string, actorId: string, baseUrl: string): FederatedActivity;
920
+ /**
921
+ * Create an Undo activity
922
+ */
923
+ export declare function createUndoActivity(originalActivityId: string, actorId: string, baseUrl: string): FederatedActivity;
924
+ /**
925
+ * Create a Fork activity for card derivation
926
+ *
927
+ * Fork activities notify the source instance that a card was forked.
928
+ * The forked card contains a reference to the source in its extensions.
929
+ */
930
+ export declare function createForkActivity(sourceCardId: string, forkedCard: FederatedCard, actorId: string, baseUrl: string, recipients?: {
931
+ to?: string[];
932
+ cc?: string[];
933
+ }): ForkActivity;
934
+ /**
935
+ * Parse an incoming Fork activity
936
+ *
937
+ * @returns Parsed fork data or null if invalid
938
+ */
939
+ export declare function parseForkActivity(activity: unknown): {
940
+ sourceCardId: string;
941
+ forkedCard: FederatedCard;
942
+ actor: string;
943
+ activityId: string;
944
+ } | null;
945
+ /**
946
+ * Create an Install activity for notifying hub about card installation
947
+ *
948
+ * Consumers (SillyTavern, Voxta) send this when a card is installed/saved.
949
+ */
950
+ export declare function createInstallActivity(cardId: string, actorId: string, baseUrl: string, platform: PlatformId, recipients?: {
951
+ to?: string[];
952
+ cc?: string[];
953
+ }): InstallActivity;
954
+ /**
955
+ * Parse an incoming Install activity
956
+ *
957
+ * @returns Parsed install data or null if invalid
958
+ */
959
+ export declare function parseInstallActivity(activity: unknown): {
960
+ cardId: string;
961
+ actor: string;
962
+ platform: PlatformId | null;
963
+ activityId: string;
964
+ } | null;
965
+ /**
966
+ * Create a minimal actor object
967
+ */
968
+ export declare function createActor(options: {
969
+ id: string;
970
+ username: string;
971
+ displayName: string;
972
+ summary?: string;
973
+ icon?: string;
974
+ baseUrl: string;
975
+ publicKeyPem?: string;
976
+ }): FederatedActor;
977
+ /**
978
+ * Parse an incoming activity
979
+ */
980
+ export declare function parseActivity(data: unknown): FederatedActivity;
981
+ /**
982
+ * Sync Engine
983
+ *
984
+ * Handles synchronization of character cards across platforms.
985
+ */
986
+ /**
987
+ * Sync Engine Options
988
+ */
989
+ export interface SyncEngineOptions {
990
+ /** Base URL for this federation instance */
991
+ baseUrl: string;
992
+ /** Actor ID for this instance */
993
+ actorId: string;
994
+ /** State storage */
995
+ stateStore: SyncStateStore;
996
+ /** Auto-sync interval in ms (0 to disable) */
997
+ autoSyncInterval?: number;
998
+ /**
999
+ * Use SHA-256 for change detection instead of fast 32-bit hash.
1000
+ *
1001
+ * Default: false (fast hash for backwards compatibility)
1002
+ * Recommended: true for federation sync across untrusted systems
1003
+ *
1004
+ * @security Fast hash has collision potential (~2^16 birthday bound).
1005
+ * SHA-256 is cryptographically secure but slightly slower.
1006
+ */
1007
+ secureHashing?: boolean;
1008
+ }
1009
+ /**
1010
+ * Sync Engine
1011
+ * Coordinates synchronization between registered platforms
1012
+ */
1013
+ export declare class SyncEngine {
1014
+ private platforms;
1015
+ private stateStore;
1016
+ private baseUrl;
1017
+ private actorId;
1018
+ private listeners;
1019
+ private autoSyncTimer?;
1020
+ private secureHashing;
1021
+ /**
1022
+ * Mutex flag to prevent concurrent syncAll() executions.
1023
+ * When autoSyncInterval triggers while a sync is in progress,
1024
+ * the new sync is skipped to prevent race conditions.
1025
+ */
1026
+ private syncInProgress;
1027
+ constructor(options: SyncEngineOptions);
1028
+ /**
1029
+ * Generate a hash for change detection.
1030
+ * Uses SHA-256 if secureHashing is enabled, otherwise fast 32-bit hash.
1031
+ */
1032
+ private hashCard;
1033
+ /**
1034
+ * Register a platform adapter
1035
+ */
1036
+ registerPlatform(adapter: PlatformAdapter): void;
1037
+ /**
1038
+ * Unregister a platform adapter
1039
+ */
1040
+ unregisterPlatform(platform: PlatformId): void;
1041
+ /**
1042
+ * Get registered platforms
1043
+ */
1044
+ getPlatforms(): PlatformId[];
1045
+ /**
1046
+ * Add event listener
1047
+ */
1048
+ on(event: FederationEventType, listener: FederationEventListener): void;
1049
+ /**
1050
+ * Remove event listener
1051
+ */
1052
+ off(event: FederationEventType, listener: FederationEventListener): void;
1053
+ /**
1054
+ * Emit an event
1055
+ */
1056
+ private emit;
1057
+ /**
1058
+ * Push a card from one platform to another
1059
+ */
1060
+ pushCard(sourcePlatform: PlatformId, sourceId: string, targetPlatform: PlatformId): Promise<SyncResult>;
1061
+ /**
1062
+ * Pull a card from a remote platform to local
1063
+ */
1064
+ pullCard(remotePlatform: PlatformId, remoteId: string, localPlatform: PlatformId): Promise<SyncResult>;
1065
+ /**
1066
+ * Sync a card across all registered platforms
1067
+ */
1068
+ syncCardToAll(sourcePlatform: PlatformId, sourceId: string): Promise<SyncResult[]>;
1069
+ /**
1070
+ * Sync all cards from one platform to another
1071
+ */
1072
+ syncPlatform(sourcePlatform: PlatformId, targetPlatform: PlatformId): Promise<SyncResult[]>;
1073
+ /**
1074
+ * Sync all platforms with each other.
1075
+ *
1076
+ * Includes mutex protection to prevent concurrent executions when
1077
+ * triggered by autoSyncInterval. If a sync is already in progress,
1078
+ * subsequent calls are skipped and emit a 'sync:skipped' event.
1079
+ */
1080
+ syncAll(): Promise<Map<string, SyncResult[]>>;
1081
+ /**
1082
+ * Get sync state for a card
1083
+ */
1084
+ getSyncState(federatedId: string): Promise<CardSyncState | null>;
1085
+ /**
1086
+ * Find sync state by platform ID
1087
+ */
1088
+ findSyncState(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
1089
+ /**
1090
+ * Resolve a sync conflict by choosing a version
1091
+ */
1092
+ resolveConflict(federatedId: string, resolution: "local" | "remote" | "merge", mergedCard?: CCv3Data): Promise<SyncResult>;
1093
+ /**
1094
+ * Fork a card from a remote source
1095
+ *
1096
+ * Creates a local copy of a remote card with fork metadata stored in
1097
+ * the card's extensions and sync state. Optionally notifies the source
1098
+ * instance about the fork.
1099
+ *
1100
+ * @param sourceFederatedId - Federated URI of the source card
1101
+ * @param sourcePlatform - Platform where the source card resides
1102
+ * @param targetPlatform - Platform to save the fork to
1103
+ * @param options - Fork options
1104
+ */
1105
+ forkCard(sourceFederatedId: string, sourcePlatform: PlatformId, targetPlatform: PlatformId, options?: {
1106
+ modifications?: Partial<CCv3Data["data"]>;
1107
+ notifySource?: boolean;
1108
+ sourceInbox?: string;
1109
+ }): Promise<ForkResult>;
1110
+ /**
1111
+ * Create a forked card with fork metadata in extensions
1112
+ */
1113
+ private createForkedCard;
1114
+ /**
1115
+ * Handle incoming fork notification
1116
+ *
1117
+ * Called when another instance notifies us that they forked one of our cards.
1118
+ * Increments the fork count and stores the notification.
1119
+ */
1120
+ handleForkNotification(activity: ForkActivity): Promise<void>;
1121
+ /**
1122
+ * Handle incoming install notification
1123
+ *
1124
+ * Called when a consumer (SillyTavern, Voxta) notifies us that they installed one of our cards.
1125
+ * Increments the install count and stores the notification.
1126
+ */
1127
+ handleInstallNotification(activity: InstallActivity): Promise<void>;
1128
+ /**
1129
+ * Get stats for a card
1130
+ */
1131
+ getCardStats(federatedId: string): Promise<CardStats | null>;
1132
+ /**
1133
+ * Get fork count for a card
1134
+ */
1135
+ getForkCount(federatedId: string): Promise<number>;
1136
+ /**
1137
+ * Get install count for a card
1138
+ */
1139
+ getInstallCount(federatedId: string): Promise<number>;
1140
+ /**
1141
+ * Find all local forks of a source card
1142
+ */
1143
+ findForks(sourceFederatedId: string): Promise<CardSyncState[]>;
1144
+ /**
1145
+ * Stop the sync engine
1146
+ */
1147
+ dispose(): void;
1148
+ }
1149
+ /**
1150
+ * Sync State Stores
1151
+ *
1152
+ * Implementations for storing sync state.
1153
+ */
1154
+ /**
1155
+ * In-memory sync state store
1156
+ * Useful for testing and single-session sync
1157
+ */
1158
+ export declare class MemorySyncStateStore implements SyncStateStore {
1159
+ private states;
1160
+ get(federatedId: string): Promise<CardSyncState | null>;
1161
+ set(state: CardSyncState): Promise<void>;
1162
+ delete(federatedId: string): Promise<void>;
1163
+ list(): Promise<CardSyncState[]>;
1164
+ findByPlatformId(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
1165
+ /**
1166
+ * Clear all states (for testing)
1167
+ */
1168
+ clear(): void;
1169
+ /**
1170
+ * Increment fork count and add notification
1171
+ */
1172
+ incrementForkCount(federatedId: string, notification: ForkNotification): Promise<void>;
1173
+ /**
1174
+ * Get fork count for a card
1175
+ */
1176
+ getForkCount(federatedId: string): Promise<number>;
1177
+ /**
1178
+ * Find all cards that are forks of a given source card
1179
+ */
1180
+ findForks(sourceFederatedId: string): Promise<CardSyncState[]>;
1181
+ }
1182
+ /**
1183
+ * JSON file-based sync state store
1184
+ * Persists state to a JSON file
1185
+ */
1186
+ export declare class FileSyncStateStore implements SyncStateStore {
1187
+ private states;
1188
+ private filePath;
1189
+ private saveDebounce?;
1190
+ private fs;
1191
+ constructor(filePath: string, fs: {
1192
+ readFile: (path: string, encoding: string) => Promise<string>;
1193
+ writeFile: (path: string, data: string) => Promise<void>;
1194
+ mkdir: (path: string, options: {
1195
+ recursive: boolean;
1196
+ }) => Promise<void>;
1197
+ });
1198
+ /**
1199
+ * Load state from file
1200
+ */
1201
+ load(): Promise<void>;
1202
+ /**
1203
+ * Save state to file (debounced)
1204
+ */
1205
+ private scheduleSave;
1206
+ /**
1207
+ * Save state to file immediately
1208
+ */
1209
+ saveNow(): Promise<void>;
1210
+ get(federatedId: string): Promise<CardSyncState | null>;
1211
+ set(state: CardSyncState): Promise<void>;
1212
+ delete(federatedId: string): Promise<void>;
1213
+ list(): Promise<CardSyncState[]>;
1214
+ findByPlatformId(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
1215
+ /**
1216
+ * Increment fork count and add notification
1217
+ */
1218
+ incrementForkCount(federatedId: string, notification: ForkNotification): Promise<void>;
1219
+ /**
1220
+ * Get fork count for a card
1221
+ */
1222
+ getForkCount(federatedId: string): Promise<number>;
1223
+ /**
1224
+ * Find all cards that are forks of a given source card
1225
+ */
1226
+ findForks(sourceFederatedId: string): Promise<CardSyncState[]>;
1227
+ }
1228
+ /**
1229
+ * Storage interface (compatible with Web Storage API)
1230
+ */
1231
+ export interface StorageInterface {
1232
+ getItem(key: string): string | null;
1233
+ setItem(key: string, value: string): void;
1234
+ removeItem(key: string): void;
1235
+ }
1236
+ /**
1237
+ * Create a state store backed by a Storage interface (e.g., localStorage in browser)
1238
+ *
1239
+ * @param key - Prefix key for storage entries
1240
+ * @param storage - Storage interface (pass localStorage in browser, or a polyfill in Node.js)
1241
+ */
1242
+ export declare function createLocalStorageStore(key: string, storage: StorageInterface): SyncStateStore;
1243
+ /**
1244
+ * D1 Sync State Store
1245
+ *
1246
+ * Cloudflare D1-compatible implementation of SyncStateStore for production
1247
+ * federation support on Cloudflare Workers.
1248
+ */
1249
+ /**
1250
+ * Minimal D1Database interface
1251
+ * Compatible with Cloudflare Workers D1 API
1252
+ */
1253
+ export interface D1Database {
1254
+ prepare(query: string): D1PreparedStatement;
1255
+ exec(query: string): Promise<D1ExecResult>;
1256
+ batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;
1257
+ }
1258
+ export interface D1PreparedStatement {
1259
+ bind(...values: unknown[]): D1PreparedStatement;
1260
+ first<T = unknown>(column?: string): Promise<T | null>;
1261
+ run(): Promise<D1Result>;
1262
+ all<T = unknown>(): Promise<D1Result<T>>;
1263
+ }
1264
+ export interface D1Result<T = unknown> {
1265
+ results: T[];
1266
+ success: boolean;
1267
+ meta: {
1268
+ duration: number;
1269
+ changes: number;
1270
+ last_row_id: number;
1271
+ served_by?: string;
1272
+ };
1273
+ }
1274
+ export interface D1ExecResult {
1275
+ count: number;
1276
+ duration: number;
1277
+ }
1278
+ /**
1279
+ * D1-compatible implementation of SyncStateStore
1280
+ *
1281
+ * Stores federation sync state in Cloudflare D1 (SQLite).
1282
+ *
1283
+ * @example
1284
+ * ```typescript
1285
+ * const store = new D1SyncStateStore(env.DB);
1286
+ * await store.init();
1287
+ *
1288
+ * // Use with SyncEngine
1289
+ * const engine = new SyncEngine({
1290
+ * stateStore: store,
1291
+ * // ...
1292
+ * });
1293
+ * ```
1294
+ */
1295
+ export declare class D1SyncStateStore implements SyncStateStore {
1296
+ private db;
1297
+ private tableName;
1298
+ /**
1299
+ * Create a new D1SyncStateStore
1300
+ *
1301
+ * @param db - D1Database instance (from env.DB in Workers)
1302
+ * @param tableName - Table name for storing sync state (default: 'federation_sync_state')
1303
+ * @throws If tableName contains invalid characters (SQL injection prevention)
1304
+ */
1305
+ constructor(db: D1Database, tableName?: string);
1306
+ /**
1307
+ * Initialize the database table
1308
+ *
1309
+ * Creates the sync state table if it doesn't exist.
1310
+ * Safe to call multiple times (idempotent).
1311
+ */
1312
+ init(): Promise<void>;
1313
+ /**
1314
+ * Get sync state for a federated card ID
1315
+ */
1316
+ get(federatedId: string): Promise<CardSyncState | null>;
1317
+ /**
1318
+ * Save or update sync state
1319
+ */
1320
+ set(state: CardSyncState): Promise<void>;
1321
+ /**
1322
+ * Delete sync state for a federated card ID
1323
+ */
1324
+ delete(federatedId: string): Promise<void>;
1325
+ /**
1326
+ * List all sync states
1327
+ */
1328
+ list(): Promise<CardSyncState[]>;
1329
+ /**
1330
+ * Find sync state by platform-specific ID
1331
+ *
1332
+ * @param platform - Platform identifier
1333
+ * @param platformId - Platform-specific card ID
1334
+ * @returns Sync state if found, null otherwise
1335
+ */
1336
+ findByPlatformId(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
1337
+ /**
1338
+ * Find sync state by local ID
1339
+ *
1340
+ * @param localId - Local card ID
1341
+ * @returns Sync state if found, null otherwise
1342
+ */
1343
+ findByLocalId(localId: string): Promise<CardSyncState | null>;
1344
+ /**
1345
+ * Get count of all sync states
1346
+ */
1347
+ count(): Promise<number>;
1348
+ /**
1349
+ * List sync states by status
1350
+ *
1351
+ * @param status - Status to filter by
1352
+ * @returns Array of sync states with matching status
1353
+ */
1354
+ listByStatus(status: CardSyncState["status"]): Promise<CardSyncState[]>;
1355
+ /**
1356
+ * Clear all sync states (for testing)
1357
+ *
1358
+ * ⚠️ Use with caution - this deletes all data
1359
+ */
1360
+ clear(): Promise<void>;
1361
+ /**
1362
+ * Increment fork count and add notification for a source card
1363
+ *
1364
+ * Used when receiving a Fork activity to track that someone forked this card.
1365
+ * Notifications are capped at 100 to prevent unbounded growth.
1366
+ */
1367
+ incrementForkCount(federatedId: string, notification: ForkNotification): Promise<void>;
1368
+ /**
1369
+ * Get fork count for a card
1370
+ */
1371
+ getForkCount(federatedId: string): Promise<number>;
1372
+ /**
1373
+ * Find all cards that are forks of a given source card
1374
+ */
1375
+ findForks(sourceFederatedId: string): Promise<CardSyncState[]>;
1376
+ /**
1377
+ * Convert database row to CardSyncState
1378
+ */
1379
+ private rowToState;
1380
+ }
1381
+ /**
1382
+ * Base Platform Adapter
1383
+ *
1384
+ * Abstract base class for platform adapters.
1385
+ */
1386
+ /**
1387
+ * Card with metadata from adapter
1388
+ */
1389
+ export interface AdapterCard {
1390
+ id: string;
1391
+ card: CCv3Data;
1392
+ updatedAt: string;
1393
+ createdAt?: string;
1394
+ }
1395
+ /**
1396
+ * Asset from adapter
1397
+ */
1398
+ export interface AdapterAsset {
1399
+ name: string;
1400
+ type: string;
1401
+ data: Uint8Array;
1402
+ mimeType?: string;
1403
+ }
1404
+ /**
1405
+ * Abstract base adapter with common functionality
1406
+ */
1407
+ export declare abstract class BasePlatformAdapter implements PlatformAdapter {
1408
+ abstract readonly platform: PlatformId;
1409
+ abstract readonly displayName: string;
1410
+ /**
1411
+ * Check if platform is available
1412
+ */
1413
+ abstract isAvailable(): Promise<boolean>;
1414
+ /**
1415
+ * Get a card by local ID
1416
+ */
1417
+ abstract getCard(localId: string): Promise<CCv3Data | null>;
1418
+ /**
1419
+ * List all cards
1420
+ */
1421
+ abstract listCards(options?: {
1422
+ limit?: number;
1423
+ offset?: number;
1424
+ since?: string;
1425
+ }): Promise<AdapterCard[]>;
1426
+ /**
1427
+ * Save/update a card
1428
+ */
1429
+ abstract saveCard(card: CCv3Data, localId?: string): Promise<string>;
1430
+ /**
1431
+ * Delete a card
1432
+ */
1433
+ abstract deleteCard(localId: string): Promise<boolean>;
1434
+ /**
1435
+ * Get card assets
1436
+ */
1437
+ abstract getAssets(localId: string): Promise<AdapterAsset[]>;
1438
+ /**
1439
+ * Get last modified timestamp
1440
+ */
1441
+ abstract getLastModified(localId: string): Promise<string | null>;
1442
+ /**
1443
+ * Generate a new local ID using crypto-grade randomness
1444
+ */
1445
+ protected generateId(): string;
1446
+ }
1447
+ /**
1448
+ * In-memory adapter for testing
1449
+ */
1450
+ export declare class MemoryPlatformAdapter extends BasePlatformAdapter {
1451
+ readonly platform: PlatformId;
1452
+ readonly displayName: string;
1453
+ private cards;
1454
+ private assets;
1455
+ constructor(platform?: PlatformId, displayName?: string);
1456
+ isAvailable(): Promise<boolean>;
1457
+ getCard(localId: string): Promise<CCv3Data | null>;
1458
+ listCards(options?: {
1459
+ limit?: number;
1460
+ offset?: number;
1461
+ since?: string;
1462
+ }): Promise<AdapterCard[]>;
1463
+ saveCard(card: CCv3Data, localId?: string): Promise<string>;
1464
+ deleteCard(localId: string): Promise<boolean>;
1465
+ getAssets(localId: string): Promise<AdapterAsset[]>;
1466
+ getLastModified(localId: string): Promise<string | null>;
1467
+ /**
1468
+ * Set assets for a card (for testing)
1469
+ */
1470
+ setAssets(localId: string, assets: AdapterAsset[]): void;
1471
+ /**
1472
+ * Clear all data (for testing)
1473
+ */
1474
+ clear(): void;
1475
+ /**
1476
+ * Get card count
1477
+ */
1478
+ count(): number;
1479
+ }
1480
+ /**
1481
+ * HTTP Platform Adapter
1482
+ *
1483
+ * Adapter for platforms accessible via HTTP API.
1484
+ */
1485
+ /**
1486
+ * HTTP fetch function type
1487
+ */
1488
+ export type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;
1489
+ /**
1490
+ * HTTP adapter configuration
1491
+ */
1492
+ export interface HttpAdapterConfig {
1493
+ /** Platform ID */
1494
+ platform: PlatformId;
1495
+ /** Display name */
1496
+ displayName: string;
1497
+ /** Base URL of the API */
1498
+ baseUrl: string;
1499
+ /** API endpoints */
1500
+ endpoints: {
1501
+ /** List cards: GET */
1502
+ list: string;
1503
+ /** Get card: GET (append /:id) */
1504
+ get: string;
1505
+ /** Create card: POST */
1506
+ create: string;
1507
+ /** Update card: PUT/PATCH (append /:id) */
1508
+ update: string;
1509
+ /** Delete card: DELETE (append /:id) */
1510
+ delete: string;
1511
+ /** Get assets: GET (append /:id) */
1512
+ assets?: string;
1513
+ /** Health check: GET */
1514
+ health?: string;
1515
+ };
1516
+ /** Authentication header */
1517
+ auth?: {
1518
+ type: "bearer" | "api-key" | "basic";
1519
+ token: string;
1520
+ header?: string;
1521
+ };
1522
+ /** Custom fetch function (for Node.js or testing) */
1523
+ fetch?: FetchFn;
1524
+ /**
1525
+ * Request timeout in milliseconds.
1526
+ * Default: 30000 (30 seconds)
1527
+ *
1528
+ * @security Prevents hanging connections that could cause resource exhaustion.
1529
+ */
1530
+ timeout?: number;
1531
+ /** Response transformers */
1532
+ transformers?: {
1533
+ /** Transform list response to AdapterCard[] */
1534
+ list?: (response: unknown) => AdapterCard[];
1535
+ /** Transform get response to CCv3Data */
1536
+ get?: (response: unknown) => CCv3Data;
1537
+ /** Transform card to create request body */
1538
+ create?: (card: CCv3Data) => unknown;
1539
+ /** Transform card to update request body */
1540
+ update?: (card: CCv3Data) => unknown;
1541
+ /** Extract ID from create response */
1542
+ extractId?: (response: unknown) => string;
1543
+ };
1544
+ }
1545
+ /**
1546
+ * Error thrown when resource ID validation fails
1547
+ */
1548
+ export declare class InvalidResourceIdError extends Error {
1549
+ readonly id: string;
1550
+ readonly reason: string;
1551
+ constructor(id: string, reason: string);
1552
+ }
1553
+ /**
1554
+ * HTTP-based platform adapter
1555
+ */
1556
+ export declare class HttpPlatformAdapter extends BasePlatformAdapter {
1557
+ readonly platform: PlatformId;
1558
+ readonly displayName: string;
1559
+ private config;
1560
+ private fetchFn;
1561
+ private timeoutMs;
1562
+ constructor(config: HttpAdapterConfig);
1563
+ /**
1564
+ * Execute fetch with timeout using AbortController
1565
+ *
1566
+ * @security Prevents hanging connections that could cause resource exhaustion
1567
+ */
1568
+ private fetchWithTimeout;
1569
+ /**
1570
+ * Build headers for requests
1571
+ */
1572
+ private buildHeaders;
1573
+ /**
1574
+ * Build full URL with optional resource ID.
1575
+ *
1576
+ * @security ID is validated and encoded to prevent SSRF/path traversal.
1577
+ * @throws InvalidResourceIdError if ID contains malicious patterns
1578
+ */
1579
+ private buildUrl;
1580
+ isAvailable(): Promise<boolean>;
1581
+ getCard(localId: string): Promise<CCv3Data | null>;
1582
+ listCards(options?: {
1583
+ limit?: number;
1584
+ offset?: number;
1585
+ since?: string;
1586
+ }): Promise<AdapterCard[]>;
1587
+ saveCard(card: CCv3Data, localId?: string): Promise<string>;
1588
+ deleteCard(localId: string): Promise<boolean>;
1589
+ getAssets(localId: string): Promise<AdapterAsset[]>;
1590
+ getLastModified(localId: string): Promise<string | null>;
1591
+ }
1592
+ /**
1593
+ * Create an HTTP adapter for Character Archive API
1594
+ */
1595
+ export declare function createArchiveAdapter(baseUrl: string, apiKey?: string): HttpPlatformAdapter;
1596
+ /**
1597
+ * Create an HTTP adapter for CardsHub API
1598
+ */
1599
+ export declare function createHubAdapter(baseUrl: string, apiKey?: string): HttpPlatformAdapter;
1600
+ /**
1601
+ * SillyTavern Platform Adapter
1602
+ *
1603
+ * Adapter for SillyTavern integration via plugin.
1604
+ * This provides the interface that a SillyTavern plugin would implement.
1605
+ */
1606
+ /**
1607
+ * SillyTavern character format (simplified)
1608
+ */
1609
+ export interface STCharacter {
1610
+ /** Character filename/ID */
1611
+ name: string;
1612
+ /** Avatar filename */
1613
+ avatar: string;
1614
+ /** Character data (TavernCard format) */
1615
+ data: {
1616
+ name: string;
1617
+ description: string;
1618
+ personality: string;
1619
+ scenario: string;
1620
+ first_mes: string;
1621
+ mes_example: string;
1622
+ creator_notes?: string;
1623
+ system_prompt?: string;
1624
+ post_history_instructions?: string;
1625
+ alternate_greetings?: string[];
1626
+ character_book?: {
1627
+ entries: Array<{
1628
+ keys: string[];
1629
+ content: string;
1630
+ enabled: boolean;
1631
+ [key: string]: unknown;
1632
+ }>;
1633
+ [key: string]: unknown;
1634
+ };
1635
+ tags?: string[];
1636
+ creator?: string;
1637
+ character_version?: string;
1638
+ extensions?: Record<string, unknown>;
1639
+ };
1640
+ }
1641
+ /**
1642
+ * Usage stats for a character in SillyTavern
1643
+ */
1644
+ export interface STCharacterStats {
1645
+ /** Number of chats with this character */
1646
+ chatCount?: number;
1647
+ /** Total messages exchanged */
1648
+ messageCount?: number;
1649
+ /** Last time this character was used */
1650
+ lastUsed?: string;
1651
+ /** When the character was first added */
1652
+ installedAt?: string;
1653
+ }
1654
+ /**
1655
+ * Interface that SillyTavern plugin must implement
1656
+ */
1657
+ export interface SillyTavernBridge {
1658
+ /** Get all characters */
1659
+ getCharacters(): Promise<STCharacter[]>;
1660
+ /** Get a character by name/ID */
1661
+ getCharacter(name: string): Promise<STCharacter | null>;
1662
+ /** Save a character */
1663
+ saveCharacter(character: STCharacter): Promise<string>;
1664
+ /** Delete a character */
1665
+ deleteCharacter(name: string): Promise<boolean>;
1666
+ /** Get character avatar */
1667
+ getAvatar(name: string): Promise<Uint8Array | null>;
1668
+ /** Check if SillyTavern is available */
1669
+ isConnected(): Promise<boolean>;
1670
+ /**
1671
+ * Get usage stats for a character (optional)
1672
+ * SillyTavern plugins can implement this to report usage data back to hub
1673
+ */
1674
+ getCharacterStats?(name: string): Promise<STCharacterStats | null>;
1675
+ /**
1676
+ * Get stats for all characters (optional)
1677
+ * Returns a map of character name to stats
1678
+ */
1679
+ getAllStats?(): Promise<Map<string, STCharacterStats>>;
1680
+ /**
1681
+ * Notify hub that a card was installed (optional)
1682
+ * Called when a new character is added from federation
1683
+ * @param federatedId - The federated URI of the installed card
1684
+ * @param hubInbox - The hub's inbox URL to notify
1685
+ */
1686
+ notifyInstall?(federatedId: string, hubInbox: string): Promise<void>;
1687
+ }
1688
+ /**
1689
+ * Convert STCharacter to CCv3Data
1690
+ */
1691
+ export declare function stCharacterToCCv3(st: STCharacter): CCv3Data;
1692
+ /**
1693
+ * Convert CCv3Data to STCharacter
1694
+ */
1695
+ export declare function ccv3ToSTCharacter(card: CCv3Data, filename?: string): STCharacter;
1696
+ /**
1697
+ * SillyTavern adapter
1698
+ * Requires a bridge implementation from the SillyTavern plugin
1699
+ */
1700
+ export declare class SillyTavernAdapter extends BasePlatformAdapter {
1701
+ readonly platform: PlatformId;
1702
+ readonly displayName = "SillyTavern";
1703
+ private bridge;
1704
+ constructor(bridge: SillyTavernBridge);
1705
+ isAvailable(): Promise<boolean>;
1706
+ getCard(localId: string): Promise<CCv3Data | null>;
1707
+ listCards(options?: {
1708
+ limit?: number;
1709
+ offset?: number;
1710
+ since?: string;
1711
+ }): Promise<AdapterCard[]>;
1712
+ saveCard(card: CCv3Data, localId?: string): Promise<string>;
1713
+ deleteCard(localId: string): Promise<boolean>;
1714
+ getAssets(localId: string): Promise<AdapterAsset[]>;
1715
+ getLastModified(localId: string): Promise<string | null>;
1716
+ /**
1717
+ * Get stats for a character (if bridge supports it)
1718
+ */
1719
+ getStats(localId: string): Promise<STCharacterStats | null>;
1720
+ /**
1721
+ * Get stats for all characters (if bridge supports it)
1722
+ */
1723
+ getAllStats(): Promise<Map<string, STCharacterStats> | null>;
1724
+ /**
1725
+ * Notify hub about installation (if bridge supports it)
1726
+ */
1727
+ notifyInstall(federatedId: string, hubInbox: string): Promise<boolean>;
1728
+ }
1729
+ /**
1730
+ * Create a mock SillyTavern bridge for testing
1731
+ */
1732
+ export declare function createMockSTBridge(): SillyTavernBridge & {
1733
+ characters: Map<string, STCharacter>;
1734
+ avatars: Map<string, Uint8Array>;
1735
+ stats: Map<string, STCharacterStats>;
1736
+ installNotifications: Array<{
1737
+ federatedId: string;
1738
+ hubInbox: string;
1739
+ }>;
1740
+ };
1741
+ export interface WebFingerLink {
1742
+ rel: string;
1743
+ type?: string;
1744
+ href: string;
1745
+ template?: string;
1746
+ }
1747
+ export interface WebFingerResponse {
1748
+ subject: string;
1749
+ aliases?: string[];
1750
+ properties?: Record<string, string>;
1751
+ links: WebFingerLink[];
1752
+ }
1753
+ /**
1754
+ * Handle WebFinger discovery request
1755
+ *
1756
+ * @param resource - The resource URI being requested (e.g. acct:user@domain)
1757
+ * @param config - Federation configuration containing actor details
1758
+ * @returns The WebFinger response object or null if not found
1759
+ */
1760
+ export declare function handleWebFinger(resource: string, config: FederationConfig): WebFingerResponse | null;
1761
+ export interface NodeInfoLink {
1762
+ rel: string;
1763
+ href: string;
1764
+ }
1765
+ export interface NodeInfoDiscoveryResponse {
1766
+ links: NodeInfoLink[];
1767
+ }
1768
+ export interface NodeInfoUsage {
1769
+ users: {
1770
+ total: number;
1771
+ activeMonth?: number;
1772
+ activeHalfyear?: number;
1773
+ };
1774
+ localPosts?: number;
1775
+ localComments?: number;
1776
+ }
1777
+ export interface NodeInfoResponse {
1778
+ version: string;
1779
+ software: {
1780
+ name: string;
1781
+ version: string;
1782
+ };
1783
+ protocols: string[];
1784
+ services: {
1785
+ inbound: string[];
1786
+ outbound: string[];
1787
+ };
1788
+ openRegistrations: boolean;
1789
+ usage: NodeInfoUsage;
1790
+ metadata: Record<string, unknown>;
1791
+ }
1792
+ /**
1793
+ * Handle NodeInfo discovery request (/.well-known/nodeinfo)
1794
+ *
1795
+ * @param baseUrl - The base URL of the server (e.g. https://hub.example.com)
1796
+ * @returns The NodeInfo discovery response
1797
+ */
1798
+ export declare function handleNodeInfoDiscovery(baseUrl: string): NodeInfoDiscoveryResponse;
1799
+ /**
1800
+ * Handle NodeInfo data request
1801
+ *
1802
+ * @param config - Federation configuration
1803
+ * @param version - NodeInfo version ('2.0' or '2.1')
1804
+ * @returns The NodeInfo response
1805
+ */
1806
+ export declare function handleNodeInfo(config: FederationConfig, version?: "2.0" | "2.1"): NodeInfoResponse;
1807
+ /**
1808
+ * Handle Actor profile request
1809
+ *
1810
+ * @param config - Federation configuration
1811
+ * @returns The Actor JSON-LD object
1812
+ */
1813
+ export declare function handleActor(config: FederationConfig): FederatedActor;
1814
+ /**
1815
+ * Moderation Types
1816
+ *
1817
+ * Core types for federated moderation infrastructure.
1818
+ * Provides tools for communities to self-moderate without imposing specific morality.
1819
+ */
1820
+ /**
1821
+ * Flag activity for reporting content/actors
1822
+ * Per ActivityPub spec, Flag is delivered directly to target's inbox
1823
+ */
1824
+ export interface FlagActivity {
1825
+ "@context": ActivityPubContext;
1826
+ id: string;
1827
+ type: "Flag";
1828
+ /** Reporter actor URI */
1829
+ actor: string;
1830
+ /** Reported content/actor URI(s) */
1831
+ object: string | string[];
1832
+ /** Report reason/description */
1833
+ content?: string;
1834
+ /** Report category (flexible string - communities define their own) */
1835
+ category?: string;
1836
+ published: string;
1837
+ /** Target instance inbox */
1838
+ to?: string[];
1839
+ }
1840
+ /**
1841
+ * Block activity for instance-level blocking
1842
+ */
1843
+ export interface BlockActivity {
1844
+ "@context": ActivityPubContext;
1845
+ id: string;
1846
+ type: "Block";
1847
+ /** Admin actor URI */
1848
+ actor: string;
1849
+ /** Blocked instance domain or actor URI */
1850
+ object: string;
1851
+ /** Block reason */
1852
+ summary?: string;
1853
+ published: string;
1854
+ }
1855
+ /**
1856
+ * Report status - flexible string, communities define their own workflow
1857
+ * Common values: 'pending', 'reviewing', 'resolved', 'dismissed', 'escalated'
1858
+ */
1859
+ export type ReportStatus = string;
1860
+ /**
1861
+ * Report category - flexible string, communities define their own categories
1862
+ * Examples: 'spam', 'harassment', 'nsfw_unmarked', 'copyright', 'illegal_content', etc.
1863
+ */
1864
+ export type ReportCategory = string;
1865
+ /**
1866
+ * Moderation report record
1867
+ */
1868
+ export interface ModerationReport {
1869
+ /** Unique report ID */
1870
+ id: string;
1871
+ /** Reporter actor URI */
1872
+ reporterActorId: string;
1873
+ /** Reporter's instance domain */
1874
+ reporterInstance: string;
1875
+ /** Reported content/actor URI(s) */
1876
+ targetIds: string[];
1877
+ /** Report category */
1878
+ category: ReportCategory;
1879
+ /** Detailed description */
1880
+ description: string;
1881
+ /** Report status */
1882
+ status: ReportStatus;
1883
+ /** Associated Flag activity ID */
1884
+ activityId: string;
1885
+ /** Timestamp when created */
1886
+ createdAt: string;
1887
+ /** Timestamp of last update */
1888
+ updatedAt: string;
1889
+ /** Instance where report was received */
1890
+ receivingInstance: string;
1891
+ /** Whether the report was federated to target instance */
1892
+ federatedToTarget: boolean;
1893
+ /** Any additional context/metadata */
1894
+ metadata?: Record<string, unknown>;
1895
+ }
1896
+ /**
1897
+ * Action type - flexible string, communities define their own actions
1898
+ * Examples: 'warn', 'delete', 'suspend', 'silence', 'ban', 'reject_report', 'escalate', 'restore'
1899
+ */
1900
+ export type ActionType = string;
1901
+ /**
1902
+ * Moderation action record - audit trail for moderator decisions
1903
+ */
1904
+ export interface ModerationAction {
1905
+ /** Unique action ID */
1906
+ id: string;
1907
+ /** Report this action addresses (optional - some actions are proactive) */
1908
+ reportId?: string;
1909
+ /** Moderator actor URI */
1910
+ moderatorActorId: string;
1911
+ /** Target of the action (actor or content URI) */
1912
+ targetId: string;
1913
+ /** Type of action taken */
1914
+ actionType: ActionType;
1915
+ /** Reason/justification for action */
1916
+ reason: string;
1917
+ /** When action was taken */
1918
+ timestamp: string;
1919
+ /** When action expires (for temporary actions like suspensions) */
1920
+ expiresAt?: string;
1921
+ /** Whether action is currently active */
1922
+ active: boolean;
1923
+ /** Previous action this reverses (for undo/restore) */
1924
+ reversesActionId?: string;
1925
+ /** Audit trail - who approved (for multi-mod workflows) */
1926
+ approvedBy?: string[];
1927
+ /** Additional metadata */
1928
+ metadata?: Record<string, unknown>;
1929
+ }
1930
+ /**
1931
+ * Instance block level
1932
+ * - suspend: Full defederation, reject all activities
1933
+ * - silence: Don't show in public feeds, but allow existing follows
1934
+ * - reject_media: Accept text but reject media/assets
1935
+ */
1936
+ export type InstanceBlockLevel = "suspend" | "silence" | "reject_media";
1937
+ /**
1938
+ * Instance block record for defederation
1939
+ */
1940
+ export interface InstanceBlock {
1941
+ /** Unique block ID */
1942
+ id: string;
1943
+ /** Blocked instance domain (e.g., "evil.example.com") */
1944
+ blockedDomain: string;
1945
+ /** Block level */
1946
+ level: InstanceBlockLevel;
1947
+ /** Reason for block */
1948
+ reason: string;
1949
+ /** Admin who created block */
1950
+ createdBy: string;
1951
+ /** When block was created */
1952
+ createdAt: string;
1953
+ /** Whether block is active */
1954
+ active: boolean;
1955
+ /** Optional: public comment for transparency reports */
1956
+ publicComment?: string;
1957
+ /** Whether to announce block to federation peers */
1958
+ federate: boolean;
1959
+ }
1960
+ /**
1961
+ * Policy rule types
1962
+ * - keyword: Text contains keyword (case-insensitive)
1963
+ * - regex: Text matches regex pattern
1964
+ * - tag: Card has specific tag
1965
+ * - creator: Card from specific creator
1966
+ * - instance: Card from specific instance domain
1967
+ */
1968
+ export type PolicyRuleType = "keyword" | "regex" | "tag" | "creator" | "instance";
1969
+ /**
1970
+ * Policy action on rule match
1971
+ * - allow: Explicitly allow (bypass other checks, whitelist)
1972
+ * - warn: Log warning, allow through
1973
+ * - review: Queue for manual review
1974
+ * - reject: Block entirely
1975
+ * - quarantine: Accept but limit visibility
1976
+ */
1977
+ export type PolicyAction = "allow" | "warn" | "review" | "reject" | "quarantine";
1978
+ /**
1979
+ * Content policy rule
1980
+ */
1981
+ export interface ContentPolicyRule {
1982
+ /** Unique rule ID */
1983
+ id: string;
1984
+ /** Rule name for admin reference */
1985
+ name: string;
1986
+ /** Rule type */
1987
+ type: PolicyRuleType;
1988
+ /** Pattern/value to match (interpretation depends on type) */
1989
+ pattern: string;
1990
+ /**
1991
+ * Fields to check (for keyword/regex)
1992
+ * Default: ['name', 'description', 'personality', 'scenario']
1993
+ */
1994
+ targetFields?: string[];
1995
+ /** Action to take on match */
1996
+ action: PolicyAction;
1997
+ /** Priority (lower = checked first, allows for whitelist rules) */
1998
+ priority: number;
1999
+ /** Whether rule is enabled */
2000
+ enabled: boolean;
2001
+ /** Optional explanation for transparency */
2002
+ publicReason?: string;
2003
+ /** When rule was created */
2004
+ createdAt: string;
2005
+ /** Who created the rule */
2006
+ createdBy: string;
2007
+ }
2008
+ /**
2009
+ * Content policy (collection of rules)
2010
+ */
2011
+ export interface ContentPolicy {
2012
+ /** Policy ID */
2013
+ id: string;
2014
+ /** Policy name */
2015
+ name: string;
2016
+ /** Description */
2017
+ description: string;
2018
+ /** Rules in this policy */
2019
+ rules: ContentPolicyRule[];
2020
+ /** Whether policy is active */
2021
+ enabled: boolean;
2022
+ /** Default action if no rules match */
2023
+ defaultAction: PolicyAction;
2024
+ /** When policy was last updated */
2025
+ updatedAt: string;
2026
+ }
2027
+ /**
2028
+ * Result of policy evaluation
2029
+ */
2030
+ export interface PolicyEvaluationResult {
2031
+ /** Final action to take */
2032
+ action: PolicyAction;
2033
+ /** Rules that matched (for audit) */
2034
+ matchedRules: Array<{
2035
+ ruleId: string;
2036
+ ruleName: string;
2037
+ matchedField?: string;
2038
+ matchedValue?: string;
2039
+ }>;
2040
+ /** Whether any rules matched */
2041
+ hasMatch: boolean;
2042
+ }
2043
+ /**
2044
+ * Rate limit bucket for an actor
2045
+ */
2046
+ export interface RateLimitBucket {
2047
+ /** Actor ID */
2048
+ actorId: string;
2049
+ /** Current token count */
2050
+ tokens: number;
2051
+ /** Max tokens (bucket capacity) */
2052
+ maxTokens: number;
2053
+ /** Last time bucket was refilled (ISO timestamp) */
2054
+ lastRefill: string;
2055
+ /** Refill rate (tokens per hour) */
2056
+ refillRate: number;
2057
+ }
2058
+ /**
2059
+ * Rate limit check result
2060
+ */
2061
+ export interface RateLimitResult {
2062
+ /** Whether action is allowed */
2063
+ allowed: boolean;
2064
+ /** Remaining tokens */
2065
+ remaining: number;
2066
+ /** When bucket resets (ISO timestamp) */
2067
+ resetAt: string;
2068
+ /** Retry-After header value in seconds (if not allowed) */
2069
+ retryAfter?: number;
2070
+ }
2071
+ /**
2072
+ * Moderation store interface
2073
+ * Follows same pattern as SyncStateStore for consistency
2074
+ */
2075
+ export interface ModerationStore {
2076
+ /** Create a new report */
2077
+ createReport(report: Omit<ModerationReport, "id">): Promise<ModerationReport>;
2078
+ /** Get report by ID */
2079
+ getReport(id: string): Promise<ModerationReport | null>;
2080
+ /** Update report */
2081
+ updateReport(id: string, updates: Partial<ModerationReport>): Promise<void>;
2082
+ /** List reports with optional filters */
2083
+ listReports(filters?: {
2084
+ status?: ReportStatus;
2085
+ category?: ReportCategory;
2086
+ targetId?: string;
2087
+ reporterActorId?: string;
2088
+ since?: string;
2089
+ limit?: number;
2090
+ offset?: number;
2091
+ }): Promise<ModerationReport[]>;
2092
+ /** Count reports */
2093
+ countReports(filters?: {
2094
+ status?: ReportStatus;
2095
+ }): Promise<number>;
2096
+ /** Create a moderation action */
2097
+ createAction(action: Omit<ModerationAction, "id">): Promise<ModerationAction>;
2098
+ /** Get action by ID */
2099
+ getAction(id: string): Promise<ModerationAction | null>;
2100
+ /** List actions with optional filters */
2101
+ listActions(filters?: {
2102
+ targetId?: string;
2103
+ moderatorActorId?: string;
2104
+ actionType?: ActionType;
2105
+ active?: boolean;
2106
+ since?: string;
2107
+ limit?: number;
2108
+ }): Promise<ModerationAction[]>;
2109
+ /** Deactivate an action (for reversal) */
2110
+ deactivateAction(id: string): Promise<void>;
2111
+ /** Create an instance block */
2112
+ createBlock(block: Omit<InstanceBlock, "id">): Promise<InstanceBlock>;
2113
+ /** Get block by ID */
2114
+ getBlock(id: string): Promise<InstanceBlock | null>;
2115
+ /** Get block by domain */
2116
+ getBlockByDomain(domain: string): Promise<InstanceBlock | null>;
2117
+ /** List blocks */
2118
+ listBlocks(filters?: {
2119
+ active?: boolean;
2120
+ }): Promise<InstanceBlock[]>;
2121
+ /** Update block */
2122
+ updateBlock(id: string, updates: Partial<InstanceBlock>): Promise<void>;
2123
+ /** Check if instance is blocked */
2124
+ isInstanceBlocked(domain: string): Promise<boolean>;
2125
+ /** Create a content policy */
2126
+ createPolicy(policy: Omit<ContentPolicy, "id">): Promise<ContentPolicy>;
2127
+ /** Get policy by ID */
2128
+ getPolicy(id: string): Promise<ContentPolicy | null>;
2129
+ /** List policies */
2130
+ listPolicies(filters?: {
2131
+ enabled?: boolean;
2132
+ }): Promise<ContentPolicy[]>;
2133
+ /** Update policy */
2134
+ updatePolicy(id: string, updates: Partial<ContentPolicy>): Promise<void>;
2135
+ /** Delete policy */
2136
+ deletePolicy(id: string): Promise<void>;
2137
+ /** Get rate limit bucket for actor */
2138
+ getRateLimitBucket(actorId: string): Promise<RateLimitBucket | null>;
2139
+ /** Update rate limit bucket */
2140
+ updateRateLimitBucket(bucket: RateLimitBucket): Promise<void>;
2141
+ /** Get audit log (alias for listActions with specific filters) */
2142
+ getAuditLog(filters?: {
2143
+ targetId?: string;
2144
+ actorId?: string;
2145
+ since?: string;
2146
+ until?: string;
2147
+ limit?: number;
2148
+ }): Promise<ModerationAction[]>;
2149
+ }
2150
+ /**
2151
+ * Moderation event types
2152
+ */
2153
+ export type ModerationEventType = "report:created" | "report:updated" | "report:resolved" | "action:created" | "action:reverted" | "block:created" | "block:removed" | "policy:matched" | "policy:rejected";
2154
+ /**
2155
+ * Moderation event
2156
+ */
2157
+ export interface ModerationEvent {
2158
+ type: ModerationEventType;
2159
+ timestamp: string;
2160
+ data: unknown;
2161
+ }
2162
+ /**
2163
+ * Moderation event listener
2164
+ */
2165
+ export type ModerationEventListener = (event: ModerationEvent) => void | Promise<void>;
2166
+ /**
2167
+ * Moderation ActivityPub Activities
2168
+ *
2169
+ * Create and parse Flag/Block activities per ActivityPub spec.
2170
+ */
2171
+ /**
2172
+ * ActivityPub context for moderation activities
2173
+ */
2174
+ export declare const MODERATION_ACTIVITY_CONTEXT: readonly [
2175
+ "https://www.w3.org/ns/activitystreams",
2176
+ {
2177
+ readonly moderation: "https://character-foundry.dev/ns/moderation#";
2178
+ readonly category: {
2179
+ readonly "@id": "moderation:category";
2180
+ };
2181
+ }
2182
+ ];
2183
+ /**
2184
+ * Create a Flag activity for reporting content/actors
2185
+ *
2186
+ * Per ActivityPub spec, Flag is delivered directly to the target's instance inbox,
2187
+ * NOT wrapped in a Create activity.
2188
+ *
2189
+ * @example
2190
+ * ```typescript
2191
+ * const flag = createFlagActivity(
2192
+ * 'https://myinstance.com/users/reporter',
2193
+ * 'https://other.com/cards/problematic-card',
2194
+ * 'https://myinstance.com',
2195
+ * {
2196
+ * content: 'This card contains spam',
2197
+ * category: 'spam',
2198
+ * to: ['https://other.com/inbox'],
2199
+ * }
2200
+ * );
2201
+ * ```
2202
+ */
2203
+ export declare function createFlagActivity(reporterActorId: string, targetIds: string | string[], baseUrl: string, options?: {
2204
+ /** Report description/reason */
2205
+ content?: string;
2206
+ /** Report category (flexible string) */
2207
+ category?: ReportCategory;
2208
+ /** Target inbox(es) */
2209
+ to?: string[];
2210
+ }): FlagActivity;
2211
+ /**
2212
+ * Parse incoming Flag activity
2213
+ *
2214
+ * @returns Parsed flag data or null if invalid
2215
+ */
2216
+ export declare function parseFlagActivity(activity: unknown): {
2217
+ actorId: string;
2218
+ targetIds: string[];
2219
+ content?: string;
2220
+ category?: string;
2221
+ activityId: string;
2222
+ } | null;
2223
+ /**
2224
+ * Validate a Flag activity has all required fields
2225
+ */
2226
+ declare function validateFlagActivity(activity: unknown): {
2227
+ valid: boolean;
2228
+ error?: string;
2229
+ };
2230
+ /**
2231
+ * Create a Block activity for instance-level blocking
2232
+ *
2233
+ * Block activities are typically:
2234
+ * 1. Stored locally for enforcement
2235
+ * 2. Optionally announced to trusted federation peers
2236
+ *
2237
+ * @example
2238
+ * ```typescript
2239
+ * const block = createBlockActivity(
2240
+ * 'https://myinstance.com/actor',
2241
+ * 'evil.example.com',
2242
+ * 'https://myinstance.com',
2243
+ * { summary: 'Spam and harassment' }
2244
+ * );
2245
+ * ```
2246
+ */
2247
+ export declare function createBlockActivity(adminActorId: string, blockedTarget: string, baseUrl: string, options?: {
2248
+ /** Block reason */
2249
+ summary?: string;
2250
+ /** Recipients for federation announcement */
2251
+ to?: string[];
2252
+ }): BlockActivity;
2253
+ /**
2254
+ * Parse incoming Block activity
2255
+ *
2256
+ * @returns Parsed block data or null if invalid
2257
+ */
2258
+ export declare function parseBlockActivity(activity: unknown): {
2259
+ actorId: string;
2260
+ targetId: string;
2261
+ summary?: string;
2262
+ activityId: string;
2263
+ } | null;
2264
+ /**
2265
+ * Validate a Block activity has all required fields
2266
+ */
2267
+ declare function validateBlockActivity(activity: unknown): {
2268
+ valid: boolean;
2269
+ error?: string;
2270
+ };
2271
+ /**
2272
+ * Inbox Handler
2273
+ *
2274
+ * Process incoming ActivityPub activities.
2275
+ */
2276
+ /**
2277
+ * Handle incoming ActivityPub activities
2278
+ *
2279
+ * Parses and routes incoming activities to the appropriate handlers.
2280
+ * Currently supports Fork activities for fork notification handling.
2281
+ *
2282
+ * @example
2283
+ * ```typescript
2284
+ * // In your web framework (e.g., Hono, Express)
2285
+ * app.post('/inbox', async (req) => {
2286
+ * // IMPORTANT: Capture raw body BEFORE parsing for Digest verification
2287
+ * const rawBody = await req.text();
2288
+ * const body = JSON.parse(rawBody);
2289
+ *
2290
+ * const result = await handleInbox(body, req.headers, {
2291
+ * rawBody, // Required for body integrity verification
2292
+ * method: 'POST',
2293
+ * path: '/inbox', // Must match your actual route path
2294
+ * strictMode: true,
2295
+ * fetchActor: async (id) => fetchActorFromNetwork(id),
2296
+ * onFork: async (activity) => {
2297
+ * await syncEngine.handleForkNotification(activity);
2298
+ * },
2299
+ * });
2300
+ *
2301
+ * if (result.accepted) {
2302
+ * return new Response(null, { status: 202 });
2303
+ * } else {
2304
+ * return new Response(result.error, { status: 400 });
2305
+ * }
2306
+ * });
2307
+ * ```
2308
+ */
2309
+ export declare function handleInbox(body: unknown, headers: Headers | Record<string, string>, options: InboxHandlerOptions & {
2310
+ /** HTTP method (required for signature verification in strictMode) */
2311
+ method?: string;
2312
+ /** Request path (required for signature verification in strictMode) */
2313
+ path?: string;
2314
+ /**
2315
+ * Raw request body for Digest header verification.
2316
+ *
2317
+ * @security REQUIRED in strictMode when Digest header is present.
2318
+ * Without this, an attacker could modify the JSON body while keeping a valid signature
2319
+ * (since signatures only cover headers, not the body).
2320
+ *
2321
+ * Pass the raw bytes before JSON parsing:
2322
+ * ```typescript
2323
+ * const rawBody = await req.text();
2324
+ * const body = JSON.parse(rawBody);
2325
+ * handleInbox(body, headers, { rawBody, ... });
2326
+ * ```
2327
+ */
2328
+ rawBody?: string | ArrayBuffer;
2329
+ onFork?: (activity: ForkActivity) => Promise<void>;
2330
+ onInstall?: (activity: InstallActivity) => Promise<void>;
2331
+ onCreate?: (activity: FederatedActivity) => Promise<void>;
2332
+ onUpdate?: (activity: FederatedActivity) => Promise<void>;
2333
+ onDelete?: (activity: FederatedActivity) => Promise<void>;
2334
+ onLike?: (activity: FederatedActivity) => Promise<void>;
2335
+ onAnnounce?: (activity: FederatedActivity) => Promise<void>;
2336
+ onUndo?: (activity: FederatedActivity) => Promise<void>;
2337
+ onFlag?: (activity: FlagActivity, parsedReport: ReturnType<typeof parseFlagActivity>) => Promise<void>;
2338
+ onBlock?: (activity: BlockActivity, parsedBlock: ReturnType<typeof parseBlockActivity>) => Promise<void>;
2339
+ /**
2340
+ * Moderation store for instance blocking.
2341
+ * If provided, blocked instances are rejected BEFORE signature verification (saves CPU).
2342
+ */
2343
+ moderationStore?: ModerationStore;
2344
+ }): Promise<InboxResult>;
2345
+ /**
2346
+ * Validate that a Fork activity has all required fields
2347
+ */
2348
+ export declare function validateForkActivity(activity: unknown): {
2349
+ valid: boolean;
2350
+ error?: string;
2351
+ };
2352
+ /**
2353
+ * Validate that an Install activity has all required fields
2354
+ */
2355
+ export declare function validateInstallActivity(activity: unknown): {
2356
+ valid: boolean;
2357
+ error?: string;
2358
+ };
2359
+ /**
2360
+ * HTTP Signatures
2361
+ *
2362
+ * Implements HTTP message signing per draft-cavage-http-signatures
2363
+ * for ActivityPub federation.
2364
+ *
2365
+ * Works in both Node.js and browser/Workers environments using
2366
+ * the Web Crypto API.
2367
+ */
2368
+ /**
2369
+ * Parsed HTTP signature header
2370
+ */
2371
+ export interface ParsedSignature {
2372
+ keyId: string;
2373
+ algorithm: string;
2374
+ headers: string[];
2375
+ signature: string;
2376
+ }
2377
+ /**
2378
+ * Required headers for strict signature validation.
2379
+ * These headers MUST be included in the signed header list when strictMode is enabled.
2380
+ */
2381
+ export declare const REQUIRED_SIGNED_HEADERS: readonly [
2382
+ "(request-target)",
2383
+ "host",
2384
+ "date"
2385
+ ];
2386
+ /**
2387
+ * Signature validation options
2388
+ */
2389
+ export interface SignatureValidationOptions {
2390
+ /** HTTP method (GET, POST, etc.) */
2391
+ method: string;
2392
+ /** Request path (e.g., /inbox) */
2393
+ path: string;
2394
+ /** Function to fetch actor by ID */
2395
+ fetchActor: (actorId: string) => Promise<FederatedActor | null>;
2396
+ /** Maximum age in seconds for Date header. Default: 300 (5 minutes) */
2397
+ maxAge?: number;
2398
+ /**
2399
+ * Enable strict signature validation mode.
2400
+ *
2401
+ * When enabled, signatures MUST include these headers: (request-target), host, date.
2402
+ * This prevents replay attacks and cross-host request reuse.
2403
+ *
2404
+ * Default: false (for backwards compatibility with existing federation peers)
2405
+ * Recommended: true for new deployments
2406
+ *
2407
+ * @security Without strict mode, attackers can:
2408
+ * - Replay captured requests if Date is not signed
2409
+ * - Reuse signatures across different hosts if host is not signed
2410
+ */
2411
+ strictMode?: boolean;
2412
+ }
2413
+ /**
2414
+ * Signature validation result
2415
+ */
2416
+ export interface SignatureValidationResult {
2417
+ valid: boolean;
2418
+ error?: string;
2419
+ actor?: FederatedActor;
2420
+ keyId?: string;
2421
+ }
2422
+ /**
2423
+ * Request signing options
2424
+ */
2425
+ export interface SigningOptions {
2426
+ /** Private key in PEM format */
2427
+ privateKeyPem: string;
2428
+ /** Key ID (usually actor#main-key) */
2429
+ keyId: string;
2430
+ /** HTTP method */
2431
+ method: string;
2432
+ /** Request path */
2433
+ path: string;
2434
+ /** Optional: target host header */
2435
+ host?: string;
2436
+ /** Optional: digest header */
2437
+ digest?: string;
2438
+ /** Optional: content-type header */
2439
+ contentType?: string;
2440
+ }
2441
+ /**
2442
+ * Parse an HTTP Signature header
2443
+ *
2444
+ * Format: keyId="...",algorithm="...",headers="...",signature="..."
2445
+ */
2446
+ export declare function parseSignatureHeader(header: string): ParsedSignature | null;
2447
+ /**
2448
+ * Successful signing string result
2449
+ */
2450
+ export interface SigningStringSuccess {
2451
+ success: true;
2452
+ signingString: string;
2453
+ }
2454
+ /**
2455
+ * Failed signing string result (missing headers)
2456
+ */
2457
+ export interface SigningStringFailure {
2458
+ success: false;
2459
+ error: string;
2460
+ missingHeaders: string[];
2461
+ }
2462
+ /**
2463
+ * Result of building a signing string
2464
+ */
2465
+ export type SigningStringResult = SigningStringSuccess | SigningStringFailure;
2466
+ /**
2467
+ * Build the signing string from headers
2468
+ *
2469
+ * @security If a header is listed in headerNames but missing from the request,
2470
+ * this function returns an error. This prevents downgrade attacks where an
2471
+ * attacker claims to sign headers that aren't actually present.
2472
+ *
2473
+ * Synthetic headers ((request-target), (created), (expires)) are always allowed.
2474
+ */
2475
+ export declare function buildSigningString(method: string, path: string, headers: Headers, headerNames: string[]): string;
2476
+ /**
2477
+ * Build signing string with strict header validation
2478
+ *
2479
+ * Returns an error if any non-synthetic header in headerNames is missing.
2480
+ * Use this for new code that wants strict validation.
2481
+ */
2482
+ export declare function buildSigningStringStrict(method: string, path: string, headers: Headers, headerNames: string[]): SigningStringResult;
2483
+ /**
2484
+ * Options for HTTP signature verification
2485
+ */
2486
+ export interface VerifyHttpSignatureOptions {
2487
+ /**
2488
+ * If true, verification fails when any claimed signed header is missing.
2489
+ * This prevents downgrade attacks where signatures claim to cover headers
2490
+ * that aren't actually present in the request.
2491
+ *
2492
+ * @security Enable this for production deployments to ensure signatures
2493
+ * actually cover all claimed headers.
2494
+ */
2495
+ strictHeaders?: boolean;
2496
+ }
2497
+ /**
2498
+ * Verify an HTTP signature using Web Crypto API
2499
+ *
2500
+ * @param options.strictHeaders - If true, fails when claimed headers are missing
2501
+ */
2502
+ export declare function verifyHttpSignature(parsed: ParsedSignature, publicKeyPem: string, method: string, path: string, headers: Headers, options?: VerifyHttpSignatureOptions): Promise<boolean>;
2503
+ /**
2504
+ * Validate an incoming activity's HTTP signature
2505
+ *
2506
+ * @security Enable `strictMode` for production deployments to prevent:
2507
+ * - Replay attacks (requires Date header in signature)
2508
+ * - Cross-host request reuse (requires host header in signature)
2509
+ */
2510
+ declare function validateActivitySignature(activity: FederatedActivity, headers: Headers, options: SignatureValidationOptions): Promise<SignatureValidationResult>;
2511
+ /**
2512
+ * Sign an outgoing HTTP request
2513
+ *
2514
+ * @param options.host - Required. The target host for the request.
2515
+ * @throws If host is not provided (required for valid signatures)
2516
+ */
2517
+ export declare function signRequest(options: SigningOptions): Promise<{
2518
+ signature: string;
2519
+ date: string;
2520
+ host: string;
2521
+ }>;
2522
+ /**
2523
+ * Calculate SHA-256 digest for request body
2524
+ */
2525
+ export declare function calculateDigest(body: string | ArrayBuffer): Promise<string>;
2526
+ /**
2527
+ * In-Memory Moderation Store
2528
+ *
2529
+ * Memory implementation for testing and single-session use.
2530
+ * Follows the same pattern as MemorySyncStateStore.
2531
+ */
2532
+ /**
2533
+ * In-memory implementation of ModerationStore
2534
+ *
2535
+ * Suitable for testing, development, and single-session use cases.
2536
+ * Data is lost when the process ends.
2537
+ */
2538
+ export declare class MemoryModerationStore implements ModerationStore {
2539
+ private reports;
2540
+ private actions;
2541
+ private blocks;
2542
+ private policies;
2543
+ private rateLimits;
2544
+ createReport(report: Omit<ModerationReport, "id">): Promise<ModerationReport>;
2545
+ getReport(id: string): Promise<ModerationReport | null>;
2546
+ updateReport(id: string, updates: Partial<ModerationReport>): Promise<void>;
2547
+ listReports(filters?: {
2548
+ status?: ReportStatus;
2549
+ category?: ReportCategory;
2550
+ targetId?: string;
2551
+ reporterActorId?: string;
2552
+ since?: string;
2553
+ limit?: number;
2554
+ offset?: number;
2555
+ }): Promise<ModerationReport[]>;
2556
+ countReports(filters?: {
2557
+ status?: ReportStatus;
2558
+ }): Promise<number>;
2559
+ createAction(action: Omit<ModerationAction, "id">): Promise<ModerationAction>;
2560
+ getAction(id: string): Promise<ModerationAction | null>;
2561
+ listActions(filters?: {
2562
+ targetId?: string;
2563
+ moderatorActorId?: string;
2564
+ actionType?: ActionType;
2565
+ active?: boolean;
2566
+ since?: string;
2567
+ limit?: number;
2568
+ }): Promise<ModerationAction[]>;
2569
+ deactivateAction(id: string): Promise<void>;
2570
+ createBlock(block: Omit<InstanceBlock, "id">): Promise<InstanceBlock>;
2571
+ getBlock(id: string): Promise<InstanceBlock | null>;
2572
+ getBlockByDomain(domain: string): Promise<InstanceBlock | null>;
2573
+ listBlocks(filters?: {
2574
+ active?: boolean;
2575
+ }): Promise<InstanceBlock[]>;
2576
+ updateBlock(id: string, updates: Partial<InstanceBlock>): Promise<void>;
2577
+ isInstanceBlocked(domain: string): Promise<boolean>;
2578
+ createPolicy(policy: Omit<ContentPolicy, "id">): Promise<ContentPolicy>;
2579
+ getPolicy(id: string): Promise<ContentPolicy | null>;
2580
+ listPolicies(filters?: {
2581
+ enabled?: boolean;
2582
+ }): Promise<ContentPolicy[]>;
2583
+ updatePolicy(id: string, updates: Partial<ContentPolicy>): Promise<void>;
2584
+ deletePolicy(id: string): Promise<void>;
2585
+ getRateLimitBucket(actorId: string): Promise<RateLimitBucket | null>;
2586
+ updateRateLimitBucket(bucket: RateLimitBucket): Promise<void>;
2587
+ getAuditLog(filters?: {
2588
+ targetId?: string;
2589
+ actorId?: string;
2590
+ since?: string;
2591
+ until?: string;
2592
+ limit?: number;
2593
+ }): Promise<ModerationAction[]>;
2594
+ /**
2595
+ * Clear all data (useful for testing)
2596
+ */
2597
+ clear(): void;
2598
+ /**
2599
+ * Get counts for dashboard/stats
2600
+ */
2601
+ getStats(): Promise<{
2602
+ reports: {
2603
+ total: number;
2604
+ pending: number;
2605
+ };
2606
+ actions: {
2607
+ total: number;
2608
+ active: number;
2609
+ };
2610
+ blocks: {
2611
+ total: number;
2612
+ active: number;
2613
+ };
2614
+ policies: {
2615
+ total: number;
2616
+ enabled: number;
2617
+ };
2618
+ }>;
2619
+ }
2620
+ /**
2621
+ * D1 Moderation Store
2622
+ *
2623
+ * Cloudflare D1-compatible implementation of ModerationStore for production
2624
+ * moderation support on Cloudflare Workers.
2625
+ */
2626
+ /**
2627
+ * D1-compatible implementation of ModerationStore
2628
+ *
2629
+ * @example
2630
+ * ```typescript
2631
+ * const store = new D1ModerationStore(env.DB);
2632
+ * await store.init();
2633
+ *
2634
+ * // Create a report
2635
+ * const report = await store.createReport({
2636
+ * reporterActorId: 'https://example.com/users/alice',
2637
+ * reporterInstance: 'example.com',
2638
+ * targetIds: ['https://bad.com/cards/spam'],
2639
+ * category: 'spam',
2640
+ * description: 'This card is spam',
2641
+ * status: 'pending',
2642
+ * activityId: 'https://example.com/activities/123',
2643
+ * createdAt: new Date().toISOString(),
2644
+ * updatedAt: new Date().toISOString(),
2645
+ * receivingInstance: 'bad.com',
2646
+ * federatedToTarget: true,
2647
+ * });
2648
+ * ```
2649
+ */
2650
+ export declare class D1ModerationStore implements ModerationStore {
2651
+ private db;
2652
+ private prefix;
2653
+ constructor(db: D1Database, tablePrefix?: string);
2654
+ /**
2655
+ * Initialize all moderation tables
2656
+ */
2657
+ init(): Promise<void>;
2658
+ createReport(report: Omit<ModerationReport, "id">): Promise<ModerationReport>;
2659
+ getReport(id: string): Promise<ModerationReport | null>;
2660
+ updateReport(id: string, updates: Partial<ModerationReport>): Promise<void>;
2661
+ listReports(filters?: {
2662
+ status?: ReportStatus;
2663
+ category?: ReportCategory;
2664
+ targetId?: string;
2665
+ reporterActorId?: string;
2666
+ since?: string;
2667
+ limit?: number;
2668
+ offset?: number;
2669
+ }): Promise<ModerationReport[]>;
2670
+ countReports(filters?: {
2671
+ status?: ReportStatus;
2672
+ }): Promise<number>;
2673
+ createAction(action: Omit<ModerationAction, "id">): Promise<ModerationAction>;
2674
+ getAction(id: string): Promise<ModerationAction | null>;
2675
+ listActions(filters?: {
2676
+ targetId?: string;
2677
+ moderatorActorId?: string;
2678
+ actionType?: ActionType;
2679
+ active?: boolean;
2680
+ since?: string;
2681
+ limit?: number;
2682
+ }): Promise<ModerationAction[]>;
2683
+ deactivateAction(id: string): Promise<void>;
2684
+ createBlock(block: Omit<InstanceBlock, "id">): Promise<InstanceBlock>;
2685
+ getBlock(id: string): Promise<InstanceBlock | null>;
2686
+ getBlockByDomain(domain: string): Promise<InstanceBlock | null>;
2687
+ listBlocks(filters?: {
2688
+ active?: boolean;
2689
+ }): Promise<InstanceBlock[]>;
2690
+ updateBlock(id: string, updates: Partial<InstanceBlock>): Promise<void>;
2691
+ isInstanceBlocked(domain: string): Promise<boolean>;
2692
+ createPolicy(policy: Omit<ContentPolicy, "id">): Promise<ContentPolicy>;
2693
+ getPolicy(id: string): Promise<ContentPolicy | null>;
2694
+ listPolicies(filters?: {
2695
+ enabled?: boolean;
2696
+ }): Promise<ContentPolicy[]>;
2697
+ updatePolicy(id: string, updates: Partial<ContentPolicy>): Promise<void>;
2698
+ deletePolicy(id: string): Promise<void>;
2699
+ getRateLimitBucket(actorId: string): Promise<RateLimitBucket | null>;
2700
+ updateRateLimitBucket(bucket: RateLimitBucket): Promise<void>;
2701
+ getAuditLog(filters?: {
2702
+ targetId?: string;
2703
+ actorId?: string;
2704
+ since?: string;
2705
+ until?: string;
2706
+ limit?: number;
2707
+ }): Promise<ModerationAction[]>;
2708
+ /**
2709
+ * Clear all moderation data (for testing)
2710
+ */
2711
+ clear(): Promise<void>;
2712
+ /**
2713
+ * Get stats for dashboard
2714
+ */
2715
+ getStats(): Promise<{
2716
+ reports: {
2717
+ total: number;
2718
+ pending: number;
2719
+ };
2720
+ actions: {
2721
+ total: number;
2722
+ active: number;
2723
+ };
2724
+ blocks: {
2725
+ total: number;
2726
+ active: number;
2727
+ };
2728
+ policies: {
2729
+ total: number;
2730
+ enabled: number;
2731
+ };
2732
+ }>;
2733
+ private rowToReport;
2734
+ private rowToAction;
2735
+ private rowToBlock;
2736
+ private rowToPolicy;
2737
+ private rowToRateLimit;
2738
+ }
2739
+ /**
2740
+ * Content Policy Engine
2741
+ *
2742
+ * Evaluates incoming cards against content policies.
2743
+ * Provides tools for community-defined moderation rules.
2744
+ */
2745
+ /**
2746
+ * Check if a regex pattern might be vulnerable to ReDoS
2747
+ * Returns a warning message if the pattern looks dangerous, null if safe
2748
+ *
2749
+ * @security This is a heuristic check, not a guarantee.
2750
+ * Admin-only patterns should still be audited manually.
2751
+ */
2752
+ export declare function checkRegexSafety(pattern: string): string | null;
2753
+ /**
2754
+ * Content Policy Engine
2755
+ *
2756
+ * Evaluates cards against configured policies and returns the appropriate action.
2757
+ *
2758
+ * @example
2759
+ * ```typescript
2760
+ * const engine = new PolicyEngine(moderationStore);
2761
+ *
2762
+ * const result = await engine.evaluateCard(card, 'source.example.com');
2763
+ * if (result.action === 'reject') {
2764
+ * return new Response('Content policy violation', { status: 403 });
2765
+ * }
2766
+ * if (result.action === 'review') {
2767
+ * await queueForReview(card, result.matchedRules);
2768
+ * }
2769
+ * ```
2770
+ */
2771
+ export declare class PolicyEngine {
2772
+ private store;
2773
+ private compiledRegexCache;
2774
+ constructor(store: ModerationStore);
2775
+ /**
2776
+ * Evaluate a card against all active policies
2777
+ *
2778
+ * @param card - The CCv3 card to evaluate
2779
+ * @param sourceInstance - Optional source instance domain for instance rules
2780
+ * @returns Evaluation result with action and matched rules
2781
+ */
2782
+ evaluateCard(card: CCv3Data, sourceInstance?: string): Promise<PolicyEvaluationResult>;
2783
+ /**
2784
+ * Evaluate card against a single policy
2785
+ */
2786
+ private evaluateAgainstPolicy;
2787
+ /**
2788
+ * Evaluate a single rule against a card
2789
+ */
2790
+ private evaluateRule;
2791
+ /**
2792
+ * Keyword rule - case-insensitive text search
2793
+ */
2794
+ private evaluateKeywordRule;
2795
+ /**
2796
+ * Regex rule - pattern matching with compiled cache
2797
+ *
2798
+ * @security Input is truncated to MAX_REGEX_INPUT_LENGTH to prevent ReDoS.
2799
+ * Patterns should be admin-audited. Use checkRegexSafety() when creating rules.
2800
+ */
2801
+ private evaluateRegexRule;
2802
+ /**
2803
+ * Tag rule - check card tags array
2804
+ */
2805
+ private evaluateTagRule;
2806
+ /**
2807
+ * Creator rule - match card creator
2808
+ */
2809
+ private evaluateCreatorRule;
2810
+ /**
2811
+ * Instance rule - match source domain
2812
+ * Supports wildcard patterns like "*.evil.com"
2813
+ */
2814
+ private evaluateInstanceRule;
2815
+ /**
2816
+ * Get a field value from card data
2817
+ */
2818
+ private getCardField;
2819
+ /**
2820
+ * Clear regex cache (call when rules change)
2821
+ */
2822
+ clearCache(): void;
2823
+ /**
2824
+ * Pre-compile regexes for a policy (optimization)
2825
+ *
2826
+ * @returns errors for invalid patterns, warnings for potentially unsafe patterns
2827
+ */
2828
+ precompilePolicy(policy: ContentPolicy): {
2829
+ errors: string[];
2830
+ warnings: string[];
2831
+ };
2832
+ }
2833
+ /**
2834
+ * Rate Limiter
2835
+ *
2836
+ * Token bucket rate limiting for moderation actions.
2837
+ * Prevents abuse of report/flag functionality.
2838
+ */
2839
+ /**
2840
+ * Rate limiter configuration
2841
+ */
2842
+ export interface RateLimiterConfig {
2843
+ /** Max tokens per bucket (default: 5) */
2844
+ maxTokens: number;
2845
+ /** Tokens per hour refill rate (default: 1) */
2846
+ refillRate: number;
2847
+ }
2848
+ /**
2849
+ * Token bucket rate limiter
2850
+ *
2851
+ * @example
2852
+ * ```typescript
2853
+ * const limiter = new RateLimiter(moderationStore, {
2854
+ * maxTokens: 10,
2855
+ * refillRate: 2, // 2 tokens per hour
2856
+ * });
2857
+ *
2858
+ * const result = await limiter.checkAndConsume('https://example.com/users/alice');
2859
+ * if (!result.allowed) {
2860
+ * return new Response('Too many reports', {
2861
+ * status: 429,
2862
+ * headers: { 'Retry-After': String(result.retryAfter) },
2863
+ * });
2864
+ * }
2865
+ * ```
2866
+ */
2867
+ export declare class RateLimiter {
2868
+ private store;
2869
+ private config;
2870
+ constructor(store: ModerationStore, config?: Partial<RateLimiterConfig>);
2871
+ /**
2872
+ * Check if action is allowed and consume a token if so
2873
+ *
2874
+ * @param actorId - Actor URI to check
2875
+ * @returns Rate limit result with remaining tokens and reset time
2876
+ */
2877
+ checkAndConsume(actorId: string): Promise<RateLimitResult>;
2878
+ /**
2879
+ * Check remaining tokens without consuming
2880
+ *
2881
+ * @param actorId - Actor URI to check
2882
+ * @returns Current rate limit status
2883
+ */
2884
+ check(actorId: string): Promise<RateLimitResult>;
2885
+ /**
2886
+ * Reset rate limit for an actor (admin function)
2887
+ *
2888
+ * @param actorId - Actor URI to reset
2889
+ */
2890
+ reset(actorId: string): Promise<void>;
2891
+ /**
2892
+ * Refill tokens based on time elapsed since last refill
2893
+ */
2894
+ private refillBucket;
2895
+ /**
2896
+ * Calculate when bucket will have at least 1 token
2897
+ */
2898
+ private calculateResetTime;
2899
+ /**
2900
+ * Get current configuration
2901
+ */
2902
+ getConfig(): RateLimiterConfig;
2903
+ }
2904
+ /**
2905
+ * @character-foundry/federation
2906
+ *
2907
+ * Federation layer for syncing character cards across platforms via ActivityPub.
2908
+ *
2909
+ * ⚠️ WARNING: This package is experimental and incomplete.
2910
+ *
2911
+ * Security-critical features (signature validation, inbox handling) are stubbed.
2912
+ * Do NOT use in production without explicit opt-in.
2913
+ *
2914
+ * To enable federation features, you MUST:
2915
+ * 1. Set FEDERATION_ENABLED=true in your environment (Node.js) or call enableFederation({ skipEnvCheck: true }) in browser/Workers
2916
+ * 2. Call enableFederation() before using SyncEngine or route handlers
2917
+ *
2918
+ * Both steps are required as a dual opt-in safety mechanism (except in environments without process.env).
2919
+ */
2920
+ /**
2921
+ * Enable federation features. Must be called before using SyncEngine or route handlers.
2922
+ * This is a safety gate - federation is experimental and has incomplete security.
2923
+ *
2924
+ * @param options.skipEnvCheck - Skip FEDERATION_ENABLED env var check (required for browser/Workers)
2925
+ *
2926
+ * Note: In Node.js, both this call AND FEDERATION_ENABLED=true are required (dual opt-in).
2927
+ * In browser/Workers, use skipEnvCheck: true since env vars aren't available.
2928
+ */
2929
+ export declare function enableFederation(options?: {
2930
+ skipEnvCheck?: boolean;
2931
+ }): void;
2932
+ /**
2933
+ * Check if federation is enabled
2934
+ *
2935
+ * In Node.js: requires BOTH env var AND enableFederation() call
2936
+ * In browser/Workers with skipEnvCheck: requires only enableFederation() call
2937
+ */
2938
+ export declare function isFederationEnabled(): boolean;
2939
+ /**
2940
+ * Assert federation is enabled, throw if not
2941
+ * @internal
2942
+ */
2943
+ export declare function assertFederationEnabled(feature: string): void;
2944
+
2945
+ export {
2946
+ validateActivitySignature as validateHttpSignature,
2947
+ validateBlockActivity as validateBlockActivityFields,
2948
+ validateFlagActivity as validateFlagActivityFields,
2949
+ };
2950
+
2951
+ export {};