@character-foundry/character-foundry 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/app-framework.cjs +12 -3
  2. package/dist/app-framework.cjs.map +1 -1
  3. package/dist/app-framework.d.cts +9 -1
  4. package/dist/app-framework.d.ts +9 -1
  5. package/dist/app-framework.js +12 -3
  6. package/dist/app-framework.js.map +1 -1
  7. package/dist/charx.cjs +85 -52
  8. package/dist/charx.cjs.map +1 -1
  9. package/dist/charx.d.cts +22 -22
  10. package/dist/charx.d.ts +22 -22
  11. package/dist/charx.js +85 -52
  12. package/dist/charx.js.map +1 -1
  13. package/dist/exporter.cjs +104 -54
  14. package/dist/exporter.cjs.map +1 -1
  15. package/dist/exporter.d.cts +19 -19
  16. package/dist/exporter.d.ts +19 -19
  17. package/dist/exporter.js +104 -54
  18. package/dist/exporter.js.map +1 -1
  19. package/dist/federation.cjs +104 -36
  20. package/dist/federation.cjs.map +1 -1
  21. package/dist/federation.d.cts +54 -19
  22. package/dist/federation.d.ts +54 -19
  23. package/dist/federation.js +104 -36
  24. package/dist/federation.js.map +1 -1
  25. package/dist/index.cjs +104 -54
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +29 -29
  28. package/dist/index.d.ts +29 -29
  29. package/dist/index.js +104 -54
  30. package/dist/index.js.map +1 -1
  31. package/dist/loader.cjs +171 -31
  32. package/dist/loader.cjs.map +1 -1
  33. package/dist/loader.d.cts +37 -23
  34. package/dist/loader.d.ts +37 -23
  35. package/dist/loader.js +171 -31
  36. package/dist/loader.js.map +1 -1
  37. package/dist/lorebook.d.cts +23 -23
  38. package/dist/lorebook.d.ts +23 -23
  39. package/dist/normalizer.cjs +72 -18
  40. package/dist/normalizer.cjs.map +1 -1
  41. package/dist/normalizer.d.cts +37 -37
  42. package/dist/normalizer.d.ts +37 -37
  43. package/dist/normalizer.js +72 -18
  44. package/dist/normalizer.js.map +1 -1
  45. package/dist/png.cjs +72 -18
  46. package/dist/png.cjs.map +1 -1
  47. package/dist/png.d.cts +25 -25
  48. package/dist/png.d.ts +25 -25
  49. package/dist/png.js +72 -18
  50. package/dist/png.js.map +1 -1
  51. package/dist/schemas.cjs +80 -23
  52. package/dist/schemas.cjs.map +1 -1
  53. package/dist/schemas.d.cts +85 -67
  54. package/dist/schemas.d.ts +85 -67
  55. package/dist/schemas.js +80 -23
  56. package/dist/schemas.js.map +1 -1
  57. package/dist/voxta.cjs +91 -20
  58. package/dist/voxta.cjs.map +1 -1
  59. package/dist/voxta.d.cts +23 -23
  60. package/dist/voxta.d.ts +23 -23
  61. package/dist/voxta.js +91 -20
  62. package/dist/voxta.js.map +1 -1
  63. package/package.json +24 -24
@@ -21,8 +21,8 @@ declare const CCv3DataSchema: z.ZodObject<{
21
21
  character_book: z.ZodNullable<z.ZodOptional<z.ZodObject<{
22
22
  name: z.ZodOptional<z.ZodString>;
23
23
  description: z.ZodOptional<z.ZodString>;
24
- scan_depth: z.ZodOptional<z.ZodNumber>;
25
- token_budget: z.ZodOptional<z.ZodNumber>;
24
+ scan_depth: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
25
+ token_budget: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
26
26
  recursive_scanning: z.ZodOptional<z.ZodBoolean>;
27
27
  extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
28
28
  entries: z.ZodArray<z.ZodObject<{
@@ -258,14 +258,14 @@ declare const CCv3DataSchema: z.ZodObject<{
258
258
  }, z.ZodTypeAny, "passthrough">[];
259
259
  name?: string | undefined;
260
260
  description?: string | undefined;
261
- scan_depth?: number | undefined;
262
- token_budget?: number | undefined;
261
+ scan_depth?: unknown;
262
+ token_budget?: unknown;
263
263
  recursive_scanning?: boolean | undefined;
264
264
  extensions?: Record<string, unknown> | undefined;
265
265
  }>>>;
266
266
  extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
267
267
  assets: z.ZodOptional<z.ZodArray<z.ZodObject<{
268
- type: z.ZodEnum<[
268
+ type: z.ZodEffects<z.ZodEnum<[
269
269
  "icon",
270
270
  "background",
271
271
  "emotion",
@@ -274,7 +274,7 @@ declare const CCv3DataSchema: z.ZodObject<{
274
274
  "video",
275
275
  "custom",
276
276
  "x-risu-asset"
277
- ]>;
277
+ ]>, "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset", unknown>;
278
278
  uri: z.ZodString;
279
279
  name: z.ZodString;
280
280
  ext: z.ZodString;
@@ -285,15 +285,15 @@ declare const CCv3DataSchema: z.ZodObject<{
285
285
  ext: string;
286
286
  }, {
287
287
  name: string;
288
- type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
289
288
  uri: string;
290
289
  ext: string;
290
+ type?: unknown;
291
291
  }>, "many">>;
292
292
  nickname: z.ZodOptional<z.ZodString>;
293
293
  creator_notes_multilingual: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
294
294
  source: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
295
- creation_date: z.ZodOptional<z.ZodNumber>;
296
- modification_date: z.ZodOptional<z.ZodNumber>;
295
+ creation_date: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
296
+ modification_date: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
297
297
  }, "strip", z.ZodTypeAny, {
298
298
  name: string;
299
299
  description: string;
@@ -434,8 +434,8 @@ declare const CCv3DataSchema: z.ZodObject<{
434
434
  }, z.ZodTypeAny, "passthrough">[];
435
435
  name?: string | undefined;
436
436
  description?: string | undefined;
437
- scan_depth?: number | undefined;
438
- token_budget?: number | undefined;
437
+ scan_depth?: unknown;
438
+ token_budget?: unknown;
439
439
  recursive_scanning?: boolean | undefined;
440
440
  extensions?: Record<string, unknown> | undefined;
441
441
  } | null | undefined;
@@ -445,15 +445,15 @@ declare const CCv3DataSchema: z.ZodObject<{
445
445
  group_only_greetings?: string[] | undefined;
446
446
  assets?: {
447
447
  name: string;
448
- type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
449
448
  uri: string;
450
449
  ext: string;
450
+ type?: unknown;
451
451
  }[] | undefined;
452
452
  nickname?: string | undefined;
453
453
  creator_notes_multilingual?: Record<string, string> | undefined;
454
454
  source?: string[] | undefined;
455
- creation_date?: number | undefined;
456
- modification_date?: number | undefined;
455
+ creation_date?: unknown;
456
+ modification_date?: unknown;
457
457
  }>;
458
458
  }, "strip", z.ZodTypeAny, {
459
459
  data: {
@@ -600,8 +600,8 @@ declare const CCv3DataSchema: z.ZodObject<{
600
600
  }, z.ZodTypeAny, "passthrough">[];
601
601
  name?: string | undefined;
602
602
  description?: string | undefined;
603
- scan_depth?: number | undefined;
604
- token_budget?: number | undefined;
603
+ scan_depth?: unknown;
604
+ token_budget?: unknown;
605
605
  recursive_scanning?: boolean | undefined;
606
606
  extensions?: Record<string, unknown> | undefined;
607
607
  } | null | undefined;
@@ -611,15 +611,15 @@ declare const CCv3DataSchema: z.ZodObject<{
611
611
  group_only_greetings?: string[] | undefined;
612
612
  assets?: {
613
613
  name: string;
614
- type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
615
614
  uri: string;
616
615
  ext: string;
616
+ type?: unknown;
617
617
  }[] | undefined;
618
618
  nickname?: string | undefined;
619
619
  creator_notes_multilingual?: Record<string, string> | undefined;
620
620
  source?: string[] | undefined;
621
- creation_date?: number | undefined;
622
- modification_date?: number | undefined;
621
+ creation_date?: unknown;
622
+ modification_date?: unknown;
623
623
  };
624
624
  spec: "chara_card_v3";
625
625
  spec_version: "3.0";
@@ -628,6 +628,23 @@ declare const CCv3DataSchema: z.ZodObject<{
628
628
  * Character Card v3 full structure
629
629
  */
630
630
  export type CCv3Data = z.infer<typeof CCv3DataSchema>;
631
+ /**
632
+ * Federation Logger
633
+ *
634
+ * Lightweight logger with configurable verbosity and a safe default (warn).
635
+ * No external dependencies.
636
+ */
637
+ export type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
638
+ export interface Logger {
639
+ debug: (...args: unknown[]) => void;
640
+ info: (...args: unknown[]) => void;
641
+ warn: (...args: unknown[]) => void;
642
+ error: (...args: unknown[]) => void;
643
+ }
644
+ export declare function createConsoleLogger(level?: LogLevel): Logger;
645
+ declare function getLogger(): Logger;
646
+ declare function setLogger(logger: Logger): void;
647
+ declare function setLogLevel(level: LogLevel): void;
631
648
  /**
632
649
  * Federation Types
633
650
  *
@@ -987,6 +1004,17 @@ export interface InboxHandlerOptions {
987
1004
  strictMode?: boolean;
988
1005
  /** Maximum age for signatures in seconds (default 300) */
989
1006
  maxAge?: number;
1007
+ /**
1008
+ * Optional shared network key for internal-only federation.
1009
+ *
1010
+ * When set, incoming requests must include a matching network key header.
1011
+ * In strictMode, the header must also be included in the signed header list.
1012
+ */
1013
+ networkKey?: string;
1014
+ /**
1015
+ * Header name for the shared network key (default: X-Foundry-Network-Key)
1016
+ */
1017
+ networkKeyHeader?: string;
990
1018
  }
991
1019
  /**
992
1020
  * ActivityPub Utilities
@@ -3107,6 +3135,10 @@ export declare class RateLimiter {
3107
3135
  */
3108
3136
  export declare function enableFederation(options?: {
3109
3137
  skipEnvCheck?: boolean;
3138
+ /** Optional logger override */
3139
+ logger?: Logger;
3140
+ /** Optional log level (used when logger is not provided). Default: warn */
3141
+ logLevel?: LogLevel;
3110
3142
  }): void;
3111
3143
  /**
3112
3144
  * Check if federation is enabled
@@ -3122,6 +3154,9 @@ export declare function isFederationEnabled(): boolean;
3122
3154
  export declare function assertFederationEnabled(feature: string): void;
3123
3155
 
3124
3156
  export {
3157
+ getLogger as getFederationLogger,
3158
+ setLogLevel as setFederationLogLevel,
3159
+ setLogger as setFederationLogger,
3125
3160
  validateActivitySignature as validateHttpSignature,
3126
3161
  validateBlockActivity as validateBlockActivityFields,
3127
3162
  validateFlagActivity as validateFlagActivityFields,
@@ -21,8 +21,8 @@ declare const CCv3DataSchema: z.ZodObject<{
21
21
  character_book: z.ZodNullable<z.ZodOptional<z.ZodObject<{
22
22
  name: z.ZodOptional<z.ZodString>;
23
23
  description: z.ZodOptional<z.ZodString>;
24
- scan_depth: z.ZodOptional<z.ZodNumber>;
25
- token_budget: z.ZodOptional<z.ZodNumber>;
24
+ scan_depth: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
25
+ token_budget: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
26
26
  recursive_scanning: z.ZodOptional<z.ZodBoolean>;
27
27
  extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
28
28
  entries: z.ZodArray<z.ZodObject<{
@@ -258,14 +258,14 @@ declare const CCv3DataSchema: z.ZodObject<{
258
258
  }, z.ZodTypeAny, "passthrough">[];
259
259
  name?: string | undefined;
260
260
  description?: string | undefined;
261
- scan_depth?: number | undefined;
262
- token_budget?: number | undefined;
261
+ scan_depth?: unknown;
262
+ token_budget?: unknown;
263
263
  recursive_scanning?: boolean | undefined;
264
264
  extensions?: Record<string, unknown> | undefined;
265
265
  }>>>;
266
266
  extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
267
267
  assets: z.ZodOptional<z.ZodArray<z.ZodObject<{
268
- type: z.ZodEnum<[
268
+ type: z.ZodEffects<z.ZodEnum<[
269
269
  "icon",
270
270
  "background",
271
271
  "emotion",
@@ -274,7 +274,7 @@ declare const CCv3DataSchema: z.ZodObject<{
274
274
  "video",
275
275
  "custom",
276
276
  "x-risu-asset"
277
- ]>;
277
+ ]>, "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset", unknown>;
278
278
  uri: z.ZodString;
279
279
  name: z.ZodString;
280
280
  ext: z.ZodString;
@@ -285,15 +285,15 @@ declare const CCv3DataSchema: z.ZodObject<{
285
285
  ext: string;
286
286
  }, {
287
287
  name: string;
288
- type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
289
288
  uri: string;
290
289
  ext: string;
290
+ type?: unknown;
291
291
  }>, "many">>;
292
292
  nickname: z.ZodOptional<z.ZodString>;
293
293
  creator_notes_multilingual: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
294
294
  source: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
295
- creation_date: z.ZodOptional<z.ZodNumber>;
296
- modification_date: z.ZodOptional<z.ZodNumber>;
295
+ creation_date: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
296
+ modification_date: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
297
297
  }, "strip", z.ZodTypeAny, {
298
298
  name: string;
299
299
  description: string;
@@ -434,8 +434,8 @@ declare const CCv3DataSchema: z.ZodObject<{
434
434
  }, z.ZodTypeAny, "passthrough">[];
435
435
  name?: string | undefined;
436
436
  description?: string | undefined;
437
- scan_depth?: number | undefined;
438
- token_budget?: number | undefined;
437
+ scan_depth?: unknown;
438
+ token_budget?: unknown;
439
439
  recursive_scanning?: boolean | undefined;
440
440
  extensions?: Record<string, unknown> | undefined;
441
441
  } | null | undefined;
@@ -445,15 +445,15 @@ declare const CCv3DataSchema: z.ZodObject<{
445
445
  group_only_greetings?: string[] | undefined;
446
446
  assets?: {
447
447
  name: string;
448
- type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
449
448
  uri: string;
450
449
  ext: string;
450
+ type?: unknown;
451
451
  }[] | undefined;
452
452
  nickname?: string | undefined;
453
453
  creator_notes_multilingual?: Record<string, string> | undefined;
454
454
  source?: string[] | undefined;
455
- creation_date?: number | undefined;
456
- modification_date?: number | undefined;
455
+ creation_date?: unknown;
456
+ modification_date?: unknown;
457
457
  }>;
458
458
  }, "strip", z.ZodTypeAny, {
459
459
  data: {
@@ -600,8 +600,8 @@ declare const CCv3DataSchema: z.ZodObject<{
600
600
  }, z.ZodTypeAny, "passthrough">[];
601
601
  name?: string | undefined;
602
602
  description?: string | undefined;
603
- scan_depth?: number | undefined;
604
- token_budget?: number | undefined;
603
+ scan_depth?: unknown;
604
+ token_budget?: unknown;
605
605
  recursive_scanning?: boolean | undefined;
606
606
  extensions?: Record<string, unknown> | undefined;
607
607
  } | null | undefined;
@@ -611,15 +611,15 @@ declare const CCv3DataSchema: z.ZodObject<{
611
611
  group_only_greetings?: string[] | undefined;
612
612
  assets?: {
613
613
  name: string;
614
- type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
615
614
  uri: string;
616
615
  ext: string;
616
+ type?: unknown;
617
617
  }[] | undefined;
618
618
  nickname?: string | undefined;
619
619
  creator_notes_multilingual?: Record<string, string> | undefined;
620
620
  source?: string[] | undefined;
621
- creation_date?: number | undefined;
622
- modification_date?: number | undefined;
621
+ creation_date?: unknown;
622
+ modification_date?: unknown;
623
623
  };
624
624
  spec: "chara_card_v3";
625
625
  spec_version: "3.0";
@@ -628,6 +628,23 @@ declare const CCv3DataSchema: z.ZodObject<{
628
628
  * Character Card v3 full structure
629
629
  */
630
630
  export type CCv3Data = z.infer<typeof CCv3DataSchema>;
631
+ /**
632
+ * Federation Logger
633
+ *
634
+ * Lightweight logger with configurable verbosity and a safe default (warn).
635
+ * No external dependencies.
636
+ */
637
+ export type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
638
+ export interface Logger {
639
+ debug: (...args: unknown[]) => void;
640
+ info: (...args: unknown[]) => void;
641
+ warn: (...args: unknown[]) => void;
642
+ error: (...args: unknown[]) => void;
643
+ }
644
+ export declare function createConsoleLogger(level?: LogLevel): Logger;
645
+ declare function getLogger(): Logger;
646
+ declare function setLogger(logger: Logger): void;
647
+ declare function setLogLevel(level: LogLevel): void;
631
648
  /**
632
649
  * Federation Types
633
650
  *
@@ -987,6 +1004,17 @@ export interface InboxHandlerOptions {
987
1004
  strictMode?: boolean;
988
1005
  /** Maximum age for signatures in seconds (default 300) */
989
1006
  maxAge?: number;
1007
+ /**
1008
+ * Optional shared network key for internal-only federation.
1009
+ *
1010
+ * When set, incoming requests must include a matching network key header.
1011
+ * In strictMode, the header must also be included in the signed header list.
1012
+ */
1013
+ networkKey?: string;
1014
+ /**
1015
+ * Header name for the shared network key (default: X-Foundry-Network-Key)
1016
+ */
1017
+ networkKeyHeader?: string;
990
1018
  }
991
1019
  /**
992
1020
  * ActivityPub Utilities
@@ -3107,6 +3135,10 @@ export declare class RateLimiter {
3107
3135
  */
3108
3136
  export declare function enableFederation(options?: {
3109
3137
  skipEnvCheck?: boolean;
3138
+ /** Optional logger override */
3139
+ logger?: Logger;
3140
+ /** Optional log level (used when logger is not provided). Default: warn */
3141
+ logLevel?: LogLevel;
3110
3142
  }): void;
3111
3143
  /**
3112
3144
  * Check if federation is enabled
@@ -3122,6 +3154,9 @@ export declare function isFederationEnabled(): boolean;
3122
3154
  export declare function assertFederationEnabled(feature: string): void;
3123
3155
 
3124
3156
  export {
3157
+ getLogger as getFederationLogger,
3158
+ setLogLevel as setFederationLogLevel,
3159
+ setLogger as setFederationLogger,
3125
3160
  validateActivitySignature as validateHttpSignature,
3126
3161
  validateBlockActivity as validateBlockActivityFields,
3127
3162
  validateFlagActivity as validateFlagActivityFields,
@@ -44,6 +44,50 @@ function generateUUID() {
44
44
  }
45
45
 
46
46
  // ../federation/dist/index.js
47
+ var LEVELS = {
48
+ silent: 0,
49
+ error: 1,
50
+ warn: 2,
51
+ info: 3,
52
+ debug: 4
53
+ };
54
+ var noop = () => {
55
+ };
56
+ function createConsoleLogger(level = "warn") {
57
+ const severity = LEVELS[level] ?? LEVELS.warn;
58
+ const hasConsole = typeof console !== "undefined";
59
+ const c = hasConsole ? console : void 0;
60
+ const debugImpl = c?.debug ? c.debug.bind(c) : c?.log ? c.log.bind(c) : noop;
61
+ const infoImpl = c?.info ? c.info.bind(c) : c?.log ? c.log.bind(c) : noop;
62
+ const warnImpl = c?.warn ? c.warn.bind(c) : c?.log ? c.log.bind(c) : noop;
63
+ const errorImpl = c?.error ? c.error.bind(c) : c?.log ? c.log.bind(c) : noop;
64
+ return {
65
+ debug: severity >= LEVELS.debug ? debugImpl : noop,
66
+ info: severity >= LEVELS.info ? infoImpl : noop,
67
+ warn: severity >= LEVELS.warn ? warnImpl : noop,
68
+ error: severity >= LEVELS.error ? errorImpl : noop
69
+ };
70
+ }
71
+ var federationLogger = createConsoleLogger("warn");
72
+ function getLogger() {
73
+ return federationLogger;
74
+ }
75
+ function setLogger(logger) {
76
+ federationLogger = logger;
77
+ }
78
+ function setLogLevel(level) {
79
+ federationLogger = createConsoleLogger(level);
80
+ }
81
+ function configureLogger(options) {
82
+ if (!options) return;
83
+ if (options.logger) {
84
+ setLogger(options.logger);
85
+ return;
86
+ }
87
+ if (options.logLevel) {
88
+ setLogLevel(options.logLevel);
89
+ }
90
+ }
47
91
  var ACTIVITY_CONTEXT = [
48
92
  "https://www.w3.org/ns/activitystreams",
49
93
  {
@@ -402,7 +446,7 @@ var SyncEngine = class {
402
446
  try {
403
447
  listener(event);
404
448
  } catch (err) {
405
- console.error(`Event listener error:`, err);
449
+ getLogger().error("[federation] Event listener error:", err);
406
450
  }
407
451
  }
408
452
  }
@@ -1531,7 +1575,7 @@ var HttpPlatformAdapter = class extends BasePlatformAdapter {
1531
1575
  const data = await response.json();
1532
1576
  return this.config.transformers?.get ? this.config.transformers.get(data) : data;
1533
1577
  } catch (err) {
1534
- console.error(`Failed to get card ${localId}:`, err);
1578
+ getLogger().error(`[federation] Failed to get card ${localId}:`, err);
1535
1579
  return null;
1536
1580
  }
1537
1581
  }
@@ -2083,27 +2127,14 @@ function parseSignatureHeader(header) {
2083
2127
  return {
2084
2128
  keyId: params.keyId,
2085
2129
  algorithm: params.algorithm || "rsa-sha256",
2086
- headers: (params.headers || "(request-target) host date").split(" "),
2130
+ headers: (params.headers || "(request-target) host date").trim().split(/\s+/).filter(Boolean).map((h) => h.toLowerCase()),
2087
2131
  signature: params.signature
2088
2132
  };
2089
2133
  }
2090
2134
  function buildSigningString(method, path, headers, headerNames) {
2091
2135
  const result = buildSigningStringStrict(method, path, headers, headerNames);
2092
2136
  if (!result.success) {
2093
- console.warn(`[federation] Signature verification may fail: ${result.error}`);
2094
- const lines = [];
2095
- for (const name of headerNames) {
2096
- if (name === "(request-target)") {
2097
- lines.push(`(request-target): ${method.toLowerCase()} ${path}`);
2098
- } else if (name === "(created)" || name === "(expires)") {
2099
- } else {
2100
- const value = headers.get(name);
2101
- if (value !== null) {
2102
- lines.push(`${name.toLowerCase()}: ${value}`);
2103
- }
2104
- }
2105
- }
2106
- return lines.join("\n");
2137
+ throw new Error(result.error);
2107
2138
  }
2108
2139
  return result.signingString;
2109
2140
  }
@@ -2112,16 +2143,17 @@ function buildSigningStringStrict(method, path, headers, headerNames) {
2112
2143
  const missingHeaders = [];
2113
2144
  const syntheticHeaders = /* @__PURE__ */ new Set(["(request-target)", "(created)", "(expires)"]);
2114
2145
  for (const name of headerNames) {
2115
- if (name === "(request-target)") {
2146
+ const normalizedName = name.toLowerCase();
2147
+ if (normalizedName === "(request-target)") {
2116
2148
  lines.push(`(request-target): ${method.toLowerCase()} ${path}`);
2117
- } else if (name === "(created)") {
2118
- } else if (name === "(expires)") {
2149
+ } else if (normalizedName === "(created)") {
2150
+ } else if (normalizedName === "(expires)") {
2119
2151
  } else {
2120
- const value = headers.get(name);
2152
+ const value = headers.get(normalizedName);
2121
2153
  if (value !== null) {
2122
- lines.push(`${name.toLowerCase()}: ${value}`);
2123
- } else if (!syntheticHeaders.has(name)) {
2124
- missingHeaders.push(name);
2154
+ lines.push(`${normalizedName}: ${value}`);
2155
+ } else if (!syntheticHeaders.has(normalizedName)) {
2156
+ missingHeaders.push(normalizedName);
2125
2157
  }
2126
2158
  }
2127
2159
  }
@@ -2142,7 +2174,7 @@ async function verifyHttpSignature(parsed, publicKeyPem, method, path, headers,
2142
2174
  if (options.strictHeaders) {
2143
2175
  const strictResult = buildSigningStringStrict(method, path, headers, parsed.headers);
2144
2176
  if (!strictResult.success) {
2145
- console.warn(`[federation] Strict header verification failed: ${strictResult.error}`);
2177
+ getLogger().warn(`[federation] Strict header verification failed: ${strictResult.error}`);
2146
2178
  return false;
2147
2179
  }
2148
2180
  }
@@ -2162,7 +2194,7 @@ async function verifyHttpSignature(parsed, publicKeyPem, method, path, headers,
2162
2194
  data
2163
2195
  );
2164
2196
  } catch (error) {
2165
- console.error("Signature verification failed:", error);
2197
+ getLogger().error("[federation] Signature verification failed:", error);
2166
2198
  return false;
2167
2199
  }
2168
2200
  }
@@ -2300,7 +2332,7 @@ async function importPublicKey(pem) {
2300
2332
  ["verify"]
2301
2333
  );
2302
2334
  } catch (error) {
2303
- console.error("Failed to import public key:", error);
2335
+ getLogger().error("[federation] Failed to import public key:", error);
2304
2336
  return null;
2305
2337
  }
2306
2338
  }
@@ -2316,7 +2348,7 @@ async function importPrivateKey(pem) {
2316
2348
  ["sign"]
2317
2349
  );
2318
2350
  } catch (error) {
2319
- console.error("Failed to import private key:", error);
2351
+ getLogger().error("[federation] Failed to import private key:", error);
2320
2352
  return null;
2321
2353
  }
2322
2354
  }
@@ -2345,9 +2377,31 @@ function extractHostFromActorId(actorId) {
2345
2377
  return null;
2346
2378
  }
2347
2379
  }
2380
+ function timingSafeEqualString(a, b) {
2381
+ let mismatch = a.length === b.length ? 0 : 1;
2382
+ const maxLen = Math.max(a.length, b.length);
2383
+ for (let i = 0; i < maxLen; i++) {
2384
+ const aCode = a.charCodeAt(i) || 0;
2385
+ const bCode = b.charCodeAt(i) || 0;
2386
+ mismatch |= aCode ^ bCode;
2387
+ }
2388
+ return mismatch === 0;
2389
+ }
2348
2390
  async function handleInbox(body, headers, options) {
2349
2391
  assertFederationEnabled("handleInbox");
2350
2392
  try {
2393
+ const normalizedHeaders = headers instanceof Headers ? headers : new Headers(headers);
2394
+ const networkKey = typeof options.networkKey === "string" ? options.networkKey : void 0;
2395
+ const networkKeyHeader = options.networkKeyHeader ?? "X-Foundry-Network-Key";
2396
+ if (networkKey && networkKey.length > 0) {
2397
+ const provided = normalizedHeaders.get(networkKeyHeader);
2398
+ if (!provided || !timingSafeEqualString(provided, networkKey)) {
2399
+ return {
2400
+ accepted: false,
2401
+ error: "Unauthorized: invalid or missing network key"
2402
+ };
2403
+ }
2404
+ }
2351
2405
  if (options.moderationStore) {
2352
2406
  const actorId = typeof body === "object" && body !== null && "actor" in body ? String(body.actor) : null;
2353
2407
  if (actorId) {
@@ -2370,7 +2424,7 @@ async function handleInbox(body, headers, options) {
2370
2424
  };
2371
2425
  }
2372
2426
  if (options.strictMode) {
2373
- const signatureHeader = headers instanceof Headers ? headers.get("signature") : headers["signature"] || headers["Signature"];
2427
+ const signatureHeader = normalizedHeaders.get("signature");
2374
2428
  if (!signatureHeader) {
2375
2429
  return {
2376
2430
  accepted: false,
@@ -2384,6 +2438,15 @@ async function handleInbox(body, headers, options) {
2384
2438
  error: "Invalid Signature header format"
2385
2439
  };
2386
2440
  }
2441
+ if (networkKey && networkKey.length > 0) {
2442
+ const requiredSigned = networkKeyHeader.toLowerCase();
2443
+ if (!parsedSig.headers.includes(requiredSigned)) {
2444
+ return {
2445
+ accepted: false,
2446
+ error: `Strict mode: signature missing required header: ${requiredSigned}`
2447
+ };
2448
+ }
2449
+ }
2387
2450
  const missingSignedHeaders = REQUIRED_SIGNED_HEADERS.filter(
2388
2451
  (h) => !parsedSig.headers.includes(h)
2389
2452
  );
@@ -2393,14 +2456,14 @@ async function handleInbox(body, headers, options) {
2393
2456
  error: `Strict mode: signature missing required headers: ${missingSignedHeaders.join(", ")}`
2394
2457
  };
2395
2458
  }
2396
- const dateHeader = headers instanceof Headers ? headers.get("date") : headers["date"] || headers["Date"];
2459
+ const dateHeader = normalizedHeaders.get("date");
2397
2460
  if (!dateHeader) {
2398
2461
  return {
2399
2462
  accepted: false,
2400
2463
  error: "Strict mode: Date header required"
2401
2464
  };
2402
2465
  }
2403
- const hostHeader = headers instanceof Headers ? headers.get("host") : headers["host"] || headers["Host"];
2466
+ const hostHeader = normalizedHeaders.get("host");
2404
2467
  if (!hostHeader) {
2405
2468
  return {
2406
2469
  accepted: false,
@@ -2443,7 +2506,7 @@ async function handleInbox(body, headers, options) {
2443
2506
  error: `Invalid key ID or actor URL`
2444
2507
  };
2445
2508
  }
2446
- const digestHeader = headers instanceof Headers ? headers.get("digest") : headers["digest"] || headers["Digest"];
2509
+ const digestHeader = normalizedHeaders.get("digest");
2447
2510
  if (digestHeader) {
2448
2511
  if (!options.rawBody) {
2449
2512
  return {
@@ -2474,13 +2537,13 @@ async function handleInbox(body, headers, options) {
2474
2537
  }
2475
2538
  const method = options.method || "POST";
2476
2539
  const path = options.path || "/inbox";
2477
- const normalizedHeaders = headers instanceof Headers ? headers : new Headers(headers);
2478
2540
  const isValid = await verifyHttpSignature(
2479
2541
  parsedSig,
2480
2542
  actor.publicKey.publicKeyPem,
2481
2543
  method,
2482
2544
  path,
2483
- normalizedHeaders
2545
+ normalizedHeaders,
2546
+ { strictHeaders: true }
2484
2547
  );
2485
2548
  if (!isValid) {
2486
2549
  return {
@@ -3559,7 +3622,7 @@ var PolicyEngine = class {
3559
3622
  if (!regex) {
3560
3623
  const safetyWarning = checkRegexSafety(rule.pattern);
3561
3624
  if (safetyWarning) {
3562
- console.warn(`[moderation] Rule "${rule.name}": ${safetyWarning}`);
3625
+ getLogger().warn(`[moderation] Rule "${rule.name}": ${safetyWarning}`);
3563
3626
  }
3564
3627
  try {
3565
3628
  regex = new RegExp(rule.pattern, "i");
@@ -3799,11 +3862,12 @@ function getEnvVar(name) {
3799
3862
  return void 0;
3800
3863
  }
3801
3864
  function enableFederation(options) {
3865
+ configureLogger(options);
3802
3866
  explicitlyEnabled = true;
3803
3867
  envCheckSkipped = options?.skipEnvCheck ?? false;
3804
3868
  const nodeEnv = getEnvVar("NODE_ENV");
3805
3869
  if (nodeEnv === "development" || nodeEnv === "test") {
3806
- console.warn(
3870
+ getLogger().warn(
3807
3871
  "[character-foundry/federation] Federation enabled. WARNING: Verify HTTP signatures in production. Do NOT use in production with untrusted inputs without signature validation."
3808
3872
  );
3809
3873
  }
@@ -3867,6 +3931,7 @@ export {
3867
3931
  createAnnounceActivity,
3868
3932
  createArchiveAdapter,
3869
3933
  createBlockActivity,
3934
+ createConsoleLogger,
3870
3935
  createCreateActivity,
3871
3936
  createDeleteActivity,
3872
3937
  createFlagActivity,
@@ -3881,6 +3946,7 @@ export {
3881
3946
  enableFederation,
3882
3947
  generateActivityId,
3883
3948
  generateCardId,
3949
+ getLogger as getFederationLogger,
3884
3950
  handleActor,
3885
3951
  handleInbox,
3886
3952
  handleNodeInfo,
@@ -3893,6 +3959,8 @@ export {
3893
3959
  parseForkActivity,
3894
3960
  parseInstallActivity,
3895
3961
  parseSignatureHeader,
3962
+ setLogLevel as setFederationLogLevel,
3963
+ setLogger as setFederationLogger,
3896
3964
  signRequest,
3897
3965
  stCharacterToCCv3,
3898
3966
  validateBlockActivity as validateBlockActivityFields,