geonix 1.32.0 → 1.32.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.
package/README.md CHANGED
@@ -295,6 +295,7 @@ Each returned part has:
295
295
  | `GX_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` |
296
296
  | `GX_STREAM_TIMEOUT` | `90000` | Max wait for a stream consumer to connect (ms) |
297
297
  | `GX_INACTIVITY_TIMEOUT` | `90000` | TCP proxy (HTTP-over-NATS) inactivity timeout (ms) |
298
+ | `GX_HEALTH_TIMEOUT` | `2000` | Registry health-probe timeout per advertised address (ms) |
298
299
  | `GX_SECRET` | — | Encryption key: AES-256-GCM payloads + HMAC-SHA256 subjects. Services without the same key cannot communicate. |
299
300
  | `GX_DEBUG_ENDPOINT` | — | Mount path for the debug router (e.g. `/_debug`). Disabled when unset. |
300
301
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geonix",
3
- "version": "1.32.0",
3
+ "version": "1.32.1",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "bin": {
package/src/Registry.js CHANGED
@@ -8,6 +8,11 @@ import { logger } from "./Logger.js";
8
8
 
9
9
  const REGISTRY_ENTRY_TIMEOUT = 5000;
10
10
  const GARBAGE_COLLECTOR_INTERVAL = 500;
11
+ const HEALTH_TIMEOUT = 2000;
12
+
13
+ // Read via function so tests can mutate the env after module load. Mirrors the
14
+ // pattern used by getTransportTimeout / getStreamTimeout (per CLAUDE.md N6).
15
+ const getHealthTimeout = () => parseInt(process.env.GX_HEALTH_TIMEOUT, 10) || HEALTH_TIMEOUT;
11
16
 
12
17
  /**
13
18
  * Maintains a local, in-process view of all services currently present on the NATS bus.
@@ -43,7 +48,7 @@ class Registry extends EventEmitter {
43
48
  this.#isActive = false;
44
49
  }
45
50
 
46
- async #checkHealth(address, addresses, onFirstHealthy, timeout = 500) {
51
+ async #checkHealth(address, addresses, onFirstHealthy, timeout = getHealthTimeout()) {
47
52
  try {
48
53
  const result = await (await fetchWithTimeout(`http://${address}/!!_gx/health`, {}, timeout)).json();
49
54
 
@@ -137,17 +142,22 @@ class Registry extends EventEmitter {
137
142
  }
138
143
  }
139
144
 
140
- // Refresh timeout for a known entry; if the beacon carries an `.a` that differs from
141
- // the cached advertised list, kick off a re-probe to rebuild the healthy subset.
145
+ // Refresh timeout for a known entry; kick off a re-probe when either (a) the advertised
146
+ // list changed, or (b) the entry's healthy `a` is empty and the source is still advertising
147
+ // addresses — that's the "all probes failed at registration" case that needs to retry.
142
148
  #refreshKnown(entry, advertised) {
143
149
  entry.timeout = Date.now() + REGISTRY_ENTRY_TIMEOUT;
144
150
  if (!Array.isArray(advertised)) {
145
151
  return;
146
152
  }
147
- if (this.#sameAddresses(entry.advertised ?? [], advertised)) {
153
+ const changed = !this.#sameAddresses(entry.advertised ?? [], advertised);
154
+ const stuckEmpty = (entry.a?.length ?? 0) === 0 && advertised.length > 0;
155
+ if (!changed && !stuckEmpty) {
148
156
  return;
149
157
  }
150
- entry.advertised = [...advertised];
158
+ if (changed) {
159
+ entry.advertised = [...advertised];
160
+ }
151
161
  this.#reprobe(entry).catch((e) => logger.error("registry.reprobe:", e));
152
162
  }
153
163
 
package/src/Util.js CHANGED
@@ -211,6 +211,15 @@ export function getNetworkAddresses() {
211
211
  for (const interfaceAddresses of Object.values(interfaces)) {
212
212
  if (!interfaceAddresses) { continue; }
213
213
  for (const addressObject of interfaceAddresses) {
214
+ // Skip IPv6 link-local addresses (fe80::/10). They require a zone identifier
215
+ // (e.g. %eth0) to route, which `fetch()` doesn't accept — advertising them
216
+ // just wastes a probe slot.
217
+ if (
218
+ addressObject.family === "IPv6" &&
219
+ addressObject.address.toLowerCase().startsWith("fe80:")
220
+ ) {
221
+ continue;
222
+ }
214
223
  const addr = addressObject.family === "IPv4"
215
224
  ? addressObject.address
216
225
  : addressObject.family === "IPv6"