@sockethub/server 5.0.0-beta.0 → 5.0.0-beta.1

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.
@@ -30,7 +30,5 @@ export interface MessageHandlersOptions {
30
30
  * Normalize middleware errors into payload-safe error responses.
31
31
  * Removes internal-only properties that must never be sent to clients.
32
32
  */
33
- export declare function attachError<T extends ActivityStream>(err: unknown, msg?: T): Error | (T & {
34
- sessionSecret?: string;
35
- });
33
+ export declare function attachError<T extends ActivityStream>(err: unknown, msg?: T): T | Error;
36
34
  export declare function createMessageHandlers(options: MessageHandlersOptions): MessageHandlers;
@@ -1,3 +1,16 @@
1
1
  import type { ActivityStream } from "@sockethub/schemas";
2
2
  import type { MiddlewareChainInterface } from "../middleware.js";
3
+ /**
4
+ * Maximum nesting depth accepted for client-supplied messages. Validation
5
+ * error formatting and the socket.io encoder both recurse over the message, so
6
+ * an unbounded depth lets a single payload overflow the stack and crash the
7
+ * server.
8
+ */
9
+ export declare const MAX_MESSAGE_DEPTH = 64;
10
+ /**
11
+ * Iteratively check whether a value nests deeper than `max` levels. Must not
12
+ * recurse itself, since it guards the recursive code paths.
13
+ */
14
+ export declare function exceedsDepth(root: unknown, max?: number): boolean;
15
+ /** Reject malformed or over-deep input before normalizing an activity. */
3
16
  export default function normalizeActivityStreamMiddleware<T extends ActivityStream>(msg: T, done: MiddlewareChainInterface<T>): void;
@@ -0,0 +1,2 @@
1
+ /** Disable namespaces that log raw payloads or session IDs, or recurse over client input. */
2
+ export default function disablePayloadDebug(): void;
@@ -55,6 +55,7 @@ export default class PlatformInstance {
55
55
  readonly parentId: string;
56
56
  private readonly parentSecret1?;
57
57
  readonly sessions: Set<string>;
58
+ readonly ownerIps: Set<string>;
58
59
  readonly sessionIps: Map<string, string>;
59
60
  private readonly sessionSecrets;
60
61
  private processMessageListener?;
package/dist/platform.js CHANGED
@@ -20497,7 +20497,7 @@ var init_package = __esm(() => {
20497
20497
  package_default = {
20498
20498
  name: "@sockethub/schemas",
20499
20499
  description: "JSON schema files for validating Sockethub Activity Streams",
20500
- version: "3.0.0-beta.0",
20500
+ version: "3.0.0-beta.1",
20501
20501
  type: "module",
20502
20502
  private: false,
20503
20503
  author: "Nick Jennings <nick@silverbucket.net>",
@@ -20567,7 +20567,7 @@ var init_package = __esm(() => {
20567
20567
  "ajv-formats": "^2.1.1",
20568
20568
  "ajv-formats-draft2019": "^1.6.1"
20569
20569
  },
20570
- gitHead: "75002ae8f15d7679bd58a9327f5a1d7eeaffa237",
20570
+ gitHead: "06c9062f1cb5f0c0599fd979e6e91da79f08eda0",
20571
20571
  types: "./dist/index.d.ts"
20572
20572
  };
20573
20573
  });
@@ -20733,7 +20733,8 @@ var init_sockethub_config = __esm(() => {
20733
20733
  type: "object",
20734
20734
  additionalProperties: false,
20735
20735
  properties: {
20736
- connectTimeoutMs: { type: "number" }
20736
+ connectTimeoutMs: { type: "number" },
20737
+ allowPrivateAddresses: { type: "boolean" }
20737
20738
  }
20738
20739
  },
20739
20740
  "@sockethub/platform-metadata": {
@@ -20823,11 +20824,23 @@ var init_sockethub_config = __esm(() => {
20823
20824
  type: "object",
20824
20825
  additionalProperties: false,
20825
20826
  properties: {
20827
+ maxInstancesPerSession: {
20828
+ type: "integer",
20829
+ minimum: 0,
20830
+ default: 10,
20831
+ description: "Maximum persistent instances per socket session. 0 disables the cap."
20832
+ },
20833
+ maxInstancesPerIp: {
20834
+ type: "integer",
20835
+ minimum: 0,
20836
+ default: 20,
20837
+ description: "Maximum persistent instances per client IP, including HTTP actions. 0 disables the cap."
20838
+ },
20826
20839
  maxPlatformInstances: {
20827
20840
  type: "number",
20828
20841
  minimum: 0,
20829
20842
  default: 100,
20830
- description: "Upper bound on concurrently running platform instances " + "(child processes). Each persistent-platform actor forks " + "its own process, so an unbounded count is a resource " + "exhaustion risk on public instances. 0 disables the cap."
20843
+ description: "Upper bound on concurrently running persistent platform instances " + "(child processes). Each persistent-platform actor forks " + "its own process, so an unbounded count is a resource " + "exhaustion risk on public instances. 0 disables the cap."
20831
20844
  }
20832
20845
  }
20833
20846
  },
@@ -27196,15 +27209,33 @@ var require_ajv = __commonJS(function(exports, module) {
27196
27209
  } });
27197
27210
  });
27198
27211
 
27212
+ // ../schemas/src/helpers/codegen.ts
27213
+ function isCodegenAvailable() {
27214
+ if (available === undefined) {
27215
+ try {
27216
+ new Function("return true");
27217
+ available = true;
27218
+ } catch {
27219
+ available = false;
27220
+ }
27221
+ }
27222
+ return available;
27223
+ }
27224
+ function warnCodegenUnavailable() {
27225
+ if (warned) {
27226
+ return;
27227
+ }
27228
+ warned = true;
27229
+ console.warn("[@sockethub/schemas] Schema validation is disabled: this environment's Content Security Policy blocks `new Function`, which Ajv needs to compile schemas. The Sockethub server still validates every message.");
27230
+ }
27231
+ var available, warned = false;
27232
+
27199
27233
  // ../schemas/src/schemas/service-descriptor.ts
27200
27234
  var init_service_descriptor = () => {};
27201
27235
 
27202
27236
  // ../schemas/src/service-descriptor.ts
27203
- var import_ajv, ajv;
27204
27237
  var init_service_descriptor2 = __esm(() => {
27205
27238
  init_service_descriptor();
27206
- import_ajv = __toESM(require_ajv(), 1);
27207
- ajv = new import_ajv.default({ strictTypes: false, allErrors: true });
27208
27239
  });
27209
27240
 
27210
27241
  // ../../node_modules/ajv-formats/dist/formats.js
@@ -36752,15 +36783,35 @@ var init_error_parser = __esm(() => {
36752
36783
  });
36753
36784
 
36754
36785
  // ../schemas/src/validator.ts
36786
+ function getAjv() {
36787
+ if (!isCodegenAvailable()) {
36788
+ warnCodegenUnavailable();
36789
+ return;
36790
+ }
36791
+ if (!ajv) {
36792
+ const instance = new import_ajv.default({ strictTypes: false, allErrors: true });
36793
+ import_ajv_formats.default(instance);
36794
+ import_ajv_formats_draft2019.default(instance);
36795
+ for (const uri in schemas) {
36796
+ instance.addSchema(schemas[uri], uri);
36797
+ }
36798
+ ajv = instance;
36799
+ }
36800
+ return ajv;
36801
+ }
36755
36802
  function registerSystemContext(platformId, contextUrl) {
36756
36803
  registerPlatformContext(platformId, contextUrl, `${schemaURL}/context/${platformId}/messages`, `${schemaURL}/context/${platformId}/credentials`);
36804
+ const instance = getAjv();
36805
+ if (!instance) {
36806
+ return;
36807
+ }
36757
36808
  const messageSchemaRef = `${schemaURL}/context/${platformId}/messages`;
36758
- if (!ajv2.getSchema(messageSchemaRef)) {
36759
- ajv2.addSchema({}, messageSchemaRef);
36809
+ if (!instance.getSchema(messageSchemaRef)) {
36810
+ instance.addSchema({}, messageSchemaRef);
36760
36811
  }
36761
36812
  const credentialsSchemaRef = `${schemaURL}/context/${platformId}/credentials`;
36762
- if (!ajv2.getSchema(credentialsSchemaRef)) {
36763
- ajv2.addSchema({}, credentialsSchemaRef);
36813
+ if (!instance.getSchema(credentialsSchemaRef)) {
36814
+ instance.addSchema({}, credentialsSchemaRef);
36764
36815
  }
36765
36816
  }
36766
36817
  function registerSystemPlatformContexts() {
@@ -36772,17 +36823,21 @@ function registerSystemPlatformContexts() {
36772
36823
  systemContextsRegistered = true;
36773
36824
  }
36774
36825
  function validateSockethubConfig(config) {
36826
+ const instance = getAjv();
36827
+ if (!instance) {
36828
+ return "";
36829
+ }
36775
36830
  if (!sockethubConfigValidator) {
36776
- sockethubConfigValidator = ajv2.compile(SockethubConfigSchema);
36831
+ sockethubConfigValidator = instance.compile(SockethubConfigSchema);
36777
36832
  }
36778
36833
  if (sockethubConfigValidator(config)) {
36779
36834
  return "";
36780
36835
  }
36781
- return ajv2.errorsText(sockethubConfigValidator.errors, {
36836
+ return instance.errorsText(sockethubConfigValidator.errors, {
36782
36837
  dataVar: "config"
36783
36838
  });
36784
36839
  }
36785
- var import_ajv2, import_ajv_formats, import_ajv_formats_draft2019, ajv2, schemaURL = "https://sockethub.org/schemas/v0", schemas, systemContextsRegistered = false, sockethubConfigValidator;
36840
+ var import_ajv, import_ajv_formats, import_ajv_formats_draft2019, schemaURL = "https://sockethub.org/schemas/v0", schemas, systemContextsRegistered = false, ajv, sockethubConfigValidator;
36786
36841
  var init_validator = __esm(() => {
36787
36842
  init_context();
36788
36843
  init_error_parser();
@@ -36790,17 +36845,11 @@ var init_validator = __esm(() => {
36790
36845
  init_activity_stream();
36791
36846
  init_platform();
36792
36847
  init_sockethub_config();
36793
- import_ajv2 = __toESM(require_ajv(), 1);
36848
+ import_ajv = __toESM(require_ajv(), 1);
36794
36849
  import_ajv_formats = __toESM(require_dist(), 1);
36795
36850
  import_ajv_formats_draft2019 = __toESM(require_ajv_formats_draft2019(), 1);
36796
- ajv2 = new import_ajv2.default({ strictTypes: false, allErrors: true });
36797
- import_ajv_formats.default(ajv2);
36798
- import_ajv_formats_draft2019.default(ajv2);
36799
36851
  schemas = {};
36800
36852
  schemas[`${schemaURL}/activity-stream`] = ActivityStreamSchema;
36801
- for (const uri in schemas) {
36802
- ajv2.addSchema(schemas[uri], uri);
36803
- }
36804
36853
  registerSystemPlatformContexts();
36805
36854
  });
36806
36855
 
@@ -73881,6 +73930,8 @@ var init_config_schema = __esm(() => {
73881
73930
  "rateLimiter.maxRequests": { format: "nat" },
73882
73931
  "rateLimiter.blockDurationMs": { format: "nat" },
73883
73932
  "rateLimiter.maxConnectionsPerIp": { format: "nat" },
73933
+ "limits.maxInstancesPerSession": { format: "nat" },
73934
+ "limits.maxInstancesPerIp": { format: "nat" },
73884
73935
  "limits.maxPlatformInstances": { format: "nat" },
73885
73936
  "credentials.ttlMs": {
73886
73937
  format: "nat",
@@ -24,16 +24,8 @@ declare class ProcessManager {
24
24
  */
25
25
  private ensureProcess;
26
26
  private ensureProcessNow;
27
- /**
28
- * Each platform instance forks a full child process; without an upper
29
- * bound, a public instance can be driven into resource exhaustion.
30
- * Throws when the configured cap (`limits.maxPlatformInstances`, 0 =
31
- * disabled) has been reached and a new instance would be created.
32
- *
33
- * `identifier`'s existing (dead) slot, if any, is excluded from the
34
- * count: replacing a crashed instance for the same actor doesn't
35
- * increase the total instance count, so it shouldn't be blocked by
36
- * the cap.
27
+ /** Stateless workers are bounded by the enabled platform count, independently
28
+ * of client occupancy. Check owners even when attaching to a shared worker.
37
29
  */
38
30
  private assertInstanceCapacity;
39
31
  private isProcessAlive;
@@ -1,3 +1,22 @@
1
+ import type { IncomingMessage } from "node:http";
2
+ import type { Express } from "express";
3
+ /** Resolve socket admission using the same compiled proxy policy as Express req.ip. */
4
+ export declare function getRequestClientIp(request: IncomingMessage, app: Express): string;
5
+ /**
6
+ * Client address for socket admission, per-IP quotas, and anonymous reconnect.
7
+ *
8
+ * `x-forwarded-for` is a chain. The nearest proxy appends the address it
9
+ * actually accepted, and anything to the left of that may have been set by
10
+ * the client. Reading the first entry treats that prefix as the client, so
11
+ * an attacker can present a victim's address and, during janitor grace,
12
+ * take over that victim's anonymous connection (the reconnect key is the
13
+ * actor plus this address). Hop count is already what HTTP uses: one trusted
14
+ * hop yields the address the proxy appended, not the spoofed prefix.
15
+ *
16
+ * A custom `proxyHeader` is not a chain Express understands. The operator
17
+ * vouched for that header, and its first address is the value.
18
+ */
19
+ export declare function resolveSocketClientIp(request: IncomingMessage, app: Express | undefined, reconnectIpSource: unknown, proxyHeader: unknown, fallbackAddress: string | undefined): string;
1
20
  /**
2
21
  * Resolution of Express's `trust proxy` setting.
3
22
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sockethub/server",
3
3
  "description": "A polyglot messaging service, server libraries",
4
- "version": "5.0.0-beta.0",
4
+ "version": "5.0.0-beta.1",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "author": "Nick Jennings <nick@silverbucket.net>",
@@ -53,17 +53,19 @@
53
53
  "dependencies": {
54
54
  "@sentry/node": "^10.36.0",
55
55
  "@sentry/profiling-node": "10.36.0",
56
- "@sockethub/client": "^5.0.0-beta.0",
57
- "@sockethub/data-layer": "^1.0.0-beta.0",
58
- "@sockethub/logger": "^1.0.0-beta.0",
59
- "@sockethub/schemas": "^3.0.0-beta.0",
60
- "@sockethub/util": "^1.0.0-beta.0",
56
+ "@sockethub/client": "^5.0.0-beta.1",
57
+ "@sockethub/data-layer": "^1.0.0-beta.1",
58
+ "@sockethub/logger": "^1.0.0-beta.1",
59
+ "@sockethub/schemas": "^3.0.0-beta.1",
60
+ "@sockethub/util": "^1.0.0-beta.1",
61
61
  "chalk": "^5.6.2",
62
62
  "convict": "^6.2.5",
63
+ "debug": "^4.4.3",
63
64
  "ejs": "^3.1.10",
64
65
  "express": "^5.1.0",
65
66
  "express-rate-limit": "^8.2.1",
66
67
  "jquery": "^3.7.1",
68
+ "proxy-addr": "^2.0.7",
67
69
  "rate-limit-redis": "^4.2.0",
68
70
  "socket.io": "^4.8.3"
69
71
  },
@@ -82,21 +84,22 @@
82
84
  "@types/debug": "^4.1.12",
83
85
  "@types/express": "^5.0.0",
84
86
  "@types/jquery": "^3.5.33",
87
+ "@types/proxy-addr": "^2.0.3",
85
88
  "@types/sinon": "^17.0.4",
86
89
  "jaribu": "^2.2.3",
87
90
  "sinon": "^17.0.2",
88
91
  "web-streams-polyfill": "^3.3.3"
89
92
  },
90
93
  "optionalDependencies": {
91
- "@sockethub/examples": "^1.0.0-beta.0",
92
- "@sockethub/platform-caldav": "^1.0.0-beta.0",
93
- "@sockethub/platform-carddav": "^1.0.0-beta.0",
94
- "@sockethub/platform-dummy": "^3.0.0-beta.0",
95
- "@sockethub/platform-feeds": "^4.0.0-beta.0",
96
- "@sockethub/platform-irc": "^4.0.0-beta.0",
97
- "@sockethub/platform-metadata": "^1.0.1-beta.0",
98
- "@sockethub/platform-xmpp": "^5.0.0-beta.0"
94
+ "@sockethub/examples": "^1.0.0-beta.1",
95
+ "@sockethub/platform-caldav": "^1.0.0-beta.1",
96
+ "@sockethub/platform-carddav": "^1.0.0-beta.1",
97
+ "@sockethub/platform-dummy": "^3.0.0-beta.1",
98
+ "@sockethub/platform-feeds": "^4.0.0-beta.1",
99
+ "@sockethub/platform-irc": "^4.0.0-beta.1",
100
+ "@sockethub/platform-metadata": "^1.0.1-beta.1",
101
+ "@sockethub/platform-xmpp": "^5.0.0-beta.1"
99
102
  },
100
- "gitHead": "a17e337ee7d2dd496d225c0d6e8ae48f4f4ce73c",
103
+ "gitHead": "928f0c480e4828f492fb29a97712474a8f1bcaef",
101
104
  "types": "./dist/index.d.ts"
102
105
  }