@routr/connect 2.17.0 → 2.19.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.
package/dist/envs.d.ts CHANGED
@@ -4,6 +4,7 @@ export declare const API_ADDR: string;
4
4
  export declare const CONNECT_VERIFIER_ADDR: string;
5
5
  export declare const CONNECT_VERIFIER_PUBLIC_KEY_PATH: string;
6
6
  export declare const CONNECT_VERIFIER_OPTIONS: any;
7
+ export declare const REGISTER_DEFAULT_EXPIRES: number;
7
8
  export declare const DEFAULT_UNTRUSTED_HEADERS_TO_STRIP: string[];
8
9
  export declare const UNTRUSTED_HEADERS_TO_STRIP: string[];
9
10
  export declare const TRUSTED_EDGEPORTS: string[];
package/dist/envs.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.TRUSTED_EDGEPORT_ADDRESSES = exports.TRUSTED_EDGEPORTS = exports.UNTRUSTED_HEADERS_TO_STRIP = exports.DEFAULT_UNTRUSTED_HEADERS_TO_STRIP = exports.CONNECT_VERIFIER_OPTIONS = exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = exports.CONNECT_VERIFIER_ADDR = exports.API_ADDR = exports.LOCATION_ADDR = exports.BIND_ADDR = void 0;
4
+ exports.TRUSTED_EDGEPORT_ADDRESSES = exports.TRUSTED_EDGEPORTS = exports.UNTRUSTED_HEADERS_TO_STRIP = exports.DEFAULT_UNTRUSTED_HEADERS_TO_STRIP = exports.REGISTER_DEFAULT_EXPIRES = exports.CONNECT_VERIFIER_OPTIONS = exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = exports.CONNECT_VERIFIER_ADDR = exports.API_ADDR = exports.LOCATION_ADDR = exports.BIND_ADDR = void 0;
5
5
  /*
6
6
  * Copyright (C) 2026 by Fonoster Inc (https://fonoster.com)
7
7
  * http://github.com/fonoster/routr
@@ -22,6 +22,22 @@ exports.TRUSTED_EDGEPORT_ADDRESSES = exports.TRUSTED_EDGEPORTS = exports.UNTRUST
22
22
  */
23
23
  const assertions_1 = require("./assertions");
24
24
  (0, assertions_1.assertOnlyOneEnvIsSet)(["CONNECT_VERIFIER_ADDR", "CONNECT_VERIFIER_PUBLIC_KEY"]);
25
+ /**
26
+ * Parses an optional environment variable that must hold a positive integer.
27
+ *
28
+ * @param {string} name - The variable name, used in the error message
29
+ * @param {string} value - The raw value
30
+ * @return {number | undefined} the parsed value, or undefined when unset
31
+ */
32
+ function parsePositiveInt(name, value) {
33
+ if (value === undefined || value.trim() === "")
34
+ return undefined;
35
+ const parsed = Number(value);
36
+ if (!Number.isInteger(parsed) || parsed <= 0) {
37
+ throw new Error(`${name} must be a positive integer, got "${value}"`);
38
+ }
39
+ return parsed;
40
+ }
25
41
  exports.BIND_ADDR = (_a = process.env.BIND_ADDR) !== null && _a !== void 0 ? _a : "0.0.0.0:51904";
26
42
  exports.LOCATION_ADDR = process.env.LOCATION_ADDR;
27
43
  exports.API_ADDR = process.env.API_ADDR;
@@ -30,6 +46,10 @@ exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = process.env.CONNECT_VERIFIER_PUBLIC_K
30
46
  exports.CONNECT_VERIFIER_OPTIONS = process.env.CONNECT_VERIFIER_OPTIONS
31
47
  ? JSON.parse(process.env.CONNECT_VERIFIER_OPTIONS)
32
48
  : { expiresIn: "1h", algorithm: ["RS256"] };
49
+ // Expiry, in seconds, granted to a REGISTER that carries neither a Contact
50
+ // expires parameter nor an Expires header (RFC 3261 section 10.3, step 7).
51
+ // Unset leaves the processor's default in place.
52
+ exports.REGISTER_DEFAULT_EXPIRES = parsePositiveInt("REGISTER_DEFAULT_EXPIRES", process.env.REGISTER_DEFAULT_EXPIRES);
33
53
  // Platform headers a caller must never be able to set. They are removed from
34
54
  // requests that come from untrusted sources (e.g., the PSTN or a client using a
35
55
  // Connect Token) before Routr adds its own. Override with a comma-separated list;
@@ -66,6 +66,7 @@ const location_1 = require("@routr/location");
66
66
  const processor_1 = require("@routr/processor");
67
67
  const common_1 = require("@routr/common");
68
68
  const utils_1 = require("../utils");
69
+ const envs_1 = require("../envs");
69
70
  const logger_1 = require("@fonoster/logger");
70
71
  const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
71
72
  const jwtVerifier = (0, utils_1.getVerifierImpl)();
@@ -93,10 +94,10 @@ const handleRegister = (apiClient, location) => {
93
94
  try {
94
95
  yield location.addRoute({
95
96
  aor: "aor" in peerOrAgent ? peerOrAgent.aor : processor_1.Target.getTargetAOR(request),
96
- route: location_1.Helper.createRoute(request),
97
+ route: location_1.Helper.createRoute(request, envs_1.REGISTER_DEFAULT_EXPIRES),
97
98
  maxContacts: peerOrAgent.maxContacts
98
99
  });
99
- res.sendRegisterOk(request);
100
+ res.sendRegisterOk(request, undefined, (0, utils_1.recordingMetadata)(peerOrAgent, (0, utils_1.domainOf)(peerOrAgent)));
100
101
  }
101
102
  catch (e) {
102
103
  if (e.code === grpc.status.INVALID_ARGUMENT) {
@@ -118,7 +119,7 @@ const handleRegister = (apiClient, location) => {
118
119
  }
119
120
  yield location.addRoute({
120
121
  aor: payload.aor,
121
- route: location_1.Helper.createRoute(request),
122
+ route: location_1.Helper.createRoute(request, envs_1.REGISTER_DEFAULT_EXPIRES),
122
123
  maxContacts: (_a = payload.maxContacts) !== null && _a !== void 0 ? _a : -1
123
124
  });
124
125
  res.sendRegisterOk(request);
package/dist/router.js CHANGED
@@ -121,7 +121,11 @@ function router(location, apiClient) {
121
121
  const result = (direction, route, extended) => route
122
122
  ? {
123
123
  direction,
124
- route: Object.assign(Object.assign({}, route), { metadata: extended })
124
+ route: Object.assign(Object.assign({}, route), {
125
+ // Resource metadata links the call to an account; the recording
126
+ // flag comes from any resource the call touches that asks for
127
+ // it; trace.* keys annotate the hop in the recording
128
+ metadata: Object.assign(Object.assign(Object.assign(Object.assign({}, extended), (0, utils_1.recordingMetadata)(caller, (0, utils_1.domainOf)(caller), callee, (0, utils_1.domainOf)(callee))), common_1.Trace.traceAnnotations({ direction })), route.metadata) })
125
129
  }
126
130
  : {
127
131
  direction,
@@ -174,7 +178,7 @@ function agentToAgent(location, req) {
174
178
  */
175
179
  function fromPSTN(apiClient, location, callee, req) {
176
180
  return __awaiter(this, void 0, void 0, function* () {
177
- var _a;
181
+ var _a, _b;
178
182
  const sessionAffinityRef = processor_1.Extensions.getHeaderValue(req, callee.sessionAffinityHeader);
179
183
  let backend;
180
184
  const peer = (yield apiClient.peers.findBy({
@@ -198,7 +202,8 @@ function fromPSTN(apiClient, location, callee, req) {
198
202
  }
199
203
  if (!route.headers)
200
204
  route.headers = [];
201
- (_a = callee.extraHeaders) === null || _a === void 0 ? void 0 : _a.forEach((prop) => {
205
+ route.metadata = Object.assign(Object.assign(Object.assign({}, route.metadata), (0, utils_1.recordingMetadata)(callee.trunk)), common_1.Trace.traceAnnotations({ trunk: (_a = callee.trunk) === null || _a === void 0 ? void 0 : _a.ref }));
206
+ (_b = callee.extraHeaders) === null || _b === void 0 ? void 0 : _b.forEach((prop) => {
202
207
  const p = {
203
208
  name: prop.name,
204
209
  value: prop.value,
@@ -242,6 +247,7 @@ function agentToPSTN(req, agent, calleeNumber) {
242
247
  edgePortRef: req.edgePortRef,
243
248
  listeningPoints: req.listeningPoints,
244
249
  localnets: req.localnets,
250
+ metadata: Object.assign(Object.assign({}, (0, utils_1.recordingMetadata)(trunk)), common_1.Trace.traceAnnotations({ trunk: trunk.ref })),
245
251
  externalAddrs: req.externalAddrs,
246
252
  headers: [
247
253
  // TODO: Find a more deterministic way to re-add the Privacy header
@@ -280,7 +286,7 @@ function agentToPeer(location, callee, req) {
280
286
  // eslint-disable-next-line require-jsdoc
281
287
  function peerToPSTN(apiClient, req) {
282
288
  return __awaiter(this, void 0, void 0, function* () {
283
- var _a;
289
+ var _a, _b;
284
290
  const numberTel = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.DOD_NUMBER);
285
291
  const privacy = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.DOD_PRIVACY);
286
292
  if (!numberTel) {
@@ -316,6 +322,7 @@ function peerToPSTN(apiClient, req) {
316
322
  edgePortRef: req.edgePortRef,
317
323
  listeningPoints: req.listeningPoints,
318
324
  localnets: req.localnets,
325
+ metadata: Object.assign(Object.assign({}, (0, utils_1.recordingMetadata)(number.trunk)), common_1.Trace.traceAnnotations({ trunk: (_b = number.trunk) === null || _b === void 0 ? void 0 : _b.ref })),
319
326
  externalAddrs: req.externalAddrs,
320
327
  headers: [
321
328
  // TODO: Find a more deterministic way to re-add the Privacy header
package/dist/service.js CHANGED
@@ -101,11 +101,15 @@ const createDispatcher = (deps) => (req, res) => __awaiter(void 0, void 0, void
101
101
  (0, utils_1.isInDialog)(req)) {
102
102
  return forwardInDialog(req, res);
103
103
  }
104
+ // Out-of-dialog REFER (RFC 3515 allows it) would be routed as a new call below;
105
+ // it is refused so transfer only happens on a dialog this proxy already
106
+ // authorized. SIP Connect 1.1 section 12.1 keeps REFER out of scope anyway.
104
107
  switch (req.method) {
105
108
  case common_1.Method.PUBLISH:
106
109
  case common_1.Method.NOTIFY:
107
110
  case common_1.Method.SUBSCRIBE:
108
111
  case common_1.Method.MESSAGE:
112
+ case common_1.Method.REFER:
109
113
  res.sendMethodNotAllowed();
110
114
  break;
111
115
  case common_1.Method.REGISTER:
package/dist/utils.d.ts CHANGED
@@ -74,3 +74,22 @@ export declare const getSourceAddress: (request: MessageRequest) => string | und
74
74
  * @return {boolean} true if the request comes from a trusted peer EdgePort
75
75
  */
76
76
  export declare const isFromTrustedEdgePort: (request: MessageRequest, trust: EdgePortTrust) => boolean;
77
+ type Traceable = Pick<CC.Agent, "ref" | "trace"> | undefined | null;
78
+ /**
79
+ * Metadata that asks the dispatcher to record the call when any of the
80
+ * resources it touches (Trunk, Agent, Peer, Domain) has `trace.enabled`.
81
+ * The value is the ref of the first one that asked. The dispatcher removes
82
+ * the key before the message leaves.
83
+ *
84
+ * @param {Traceable[]} resources - Resources the call touches, in order of precedence
85
+ * @return {Record<string, string>} The metadata to merge into the route
86
+ */
87
+ export declare const recordingMetadata: (...resources: Traceable[]) => Record<string, string>;
88
+ /**
89
+ * The domain a resource belongs to, when it carries one (Agents do).
90
+ *
91
+ * @param {object} resource - A caller or callee resource
92
+ * @return {CC.Domain | undefined} Its domain
93
+ */
94
+ export declare const domainOf: (resource: unknown) => CC.Domain | undefined;
95
+ export {};
package/dist/utils.js CHANGED
@@ -45,7 +45,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
45
45
  return (mod && mod.__esModule) ? mod : { "default": mod };
46
46
  };
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
- exports.isFromTrustedEdgePort = exports.getSourceAddress = exports.getUntrustedHeadersToStrip = exports.getVerifierImpl = exports.hasXConnectObjectHeader = exports.isInDialog = exports.getSipUri = exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findNumberByTelUrl = exports.findDomain = exports.isKind = void 0;
48
+ exports.domainOf = exports.recordingMetadata = exports.isFromTrustedEdgePort = exports.getSourceAddress = exports.getUntrustedHeadersToStrip = exports.getVerifierImpl = exports.hasXConnectObjectHeader = exports.isInDialog = exports.getSipUri = exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findNumberByTelUrl = exports.findDomain = exports.isKind = void 0;
49
49
  /*
50
50
  * Copyright (C) 2026 by Fonoster Inc (https://fonoster.com)
51
51
  * http://github.com/fonoster/routr
@@ -306,3 +306,25 @@ const isFromTrustedEdgePort = (request, trust) => {
306
306
  });
307
307
  };
308
308
  exports.isFromTrustedEdgePort = isFromTrustedEdgePort;
309
+ /**
310
+ * Metadata that asks the dispatcher to record the call when any of the
311
+ * resources it touches (Trunk, Agent, Peer, Domain) has `trace.enabled`.
312
+ * The value is the ref of the first one that asked. The dispatcher removes
313
+ * the key before the message leaves.
314
+ *
315
+ * @param {Traceable[]} resources - Resources the call touches, in order of precedence
316
+ * @return {Record<string, string>} The metadata to merge into the route
317
+ */
318
+ const recordingMetadata = (...resources) => {
319
+ const asked = resources.find((r) => { var _a; return ((_a = r === null || r === void 0 ? void 0 : r.trace) === null || _a === void 0 ? void 0 : _a.enabled) === true; });
320
+ return asked ? { [common_1.Trace.TRACE_FLAG_METADATA_KEY]: asked.ref } : {};
321
+ };
322
+ exports.recordingMetadata = recordingMetadata;
323
+ /**
324
+ * The domain a resource belongs to, when it carries one (Agents do).
325
+ *
326
+ * @param {object} resource - A caller or callee resource
327
+ * @return {CC.Domain | undefined} Its domain
328
+ */
329
+ const domainOf = (resource) => resource === null || resource === void 0 ? void 0 : resource.domain;
330
+ exports.domainOf = domainOf;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@routr/connect",
3
- "version": "2.17.0",
3
+ "version": "2.19.0",
4
4
  "description": "Default processor",
5
5
  "author": "Pedro Sanders <psanders@fonoster.com>",
6
6
  "homepage": "https://github.com/fonoster/routr#readme",
@@ -20,9 +20,9 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@fonoster/logger": "0.6.0",
23
- "@routr/common": "^2.17.0",
24
- "@routr/location": "^2.17.0",
25
- "@routr/processor": "^2.17.0",
23
+ "@routr/common": "^2.19.0",
24
+ "@routr/location": "^2.19.0",
25
+ "@routr/processor": "^2.19.0",
26
26
  "jsonwebtoken": "^9.0.3"
27
27
  },
28
28
  "devDependencies": {
@@ -41,5 +41,5 @@
41
41
  "bugs": {
42
42
  "url": "https://github.com/fonoster/routr/issues"
43
43
  },
44
- "gitHead": "9bc2d0a407a82e7b3724bb139ba62504c7b3d49e"
44
+ "gitHead": "357116800e150d8bbfbd0ad2406689c5793675b1"
45
45
  }