@hivemagnus/hive-router 1.0.0 → 1.0.2

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.
@@ -574,9 +574,12 @@ async function probeHiveService(key, name, role, internalUrl, publicUrl, healthP
574
574
  };
575
575
  }
576
576
  }
577
- async function buildHiveStatus(env, requestId) {
577
+ export async function buildHiveStatus(env, requestId) {
578
578
  const tlsCertificate = inspectTlsCertificate({ publicUrl: env.PUBLIC_ADMIN_URL, ...(env.TLS_CERT_PATH ? { certPath: env.TLS_CERT_PATH } : {}) });
579
579
  const securityExporterJwks = getSecurityExporterJwksStatus(env.HIVE_ROUTER_IAM_JWKS_MAX_AGE_MS);
580
+ // Exporter keys load on demand; an unused cache is expected on a fresh install.
581
+ const exporterKeysDegraded = securityExporterJwks.state !== 'healthy'
582
+ && securityExporterJwks.state !== 'uninitialized';
580
583
  const [providerCount, enabledProviders, routeRuleCount, traceCount, failureCount, failoverCount] = await Promise.all([
581
584
  prisma.provider.count(),
582
585
  prisma.provider.count({ where: { enabled: true } }),
@@ -589,7 +592,7 @@ async function buildHiveStatus(env, requestId) {
589
592
  key: 'router',
590
593
  name: 'Hive Router',
591
594
  role: 'traffic routing and OpenAI-compatible gateway',
592
- status: failureCount > 0 || tlsCertificate.status !== 'pass' || securityExporterJwks.state !== 'healthy' ? 'degraded' : 'online',
595
+ status: failureCount > 0 || tlsCertificate.status !== 'pass' || exporterKeysDegraded ? 'degraded' : 'online',
593
596
  summary: `${enabledProviders}/${providerCount} providers enabled`,
594
597
  internalUrl: env.PUBLIC_ADMIN_URL,
595
598
  publicUrl: env.PUBLIC_API_URL,
@@ -603,7 +606,7 @@ async function buildHiveStatus(env, requestId) {
603
606
  name: 'security-exporter-jwks',
604
607
  status: securityExporterJwks.state === 'healthy' ? 'pass' : securityExporterJwks.state === 'expired' ? 'fail' : 'warn',
605
608
  message: securityExporterJwks.state === 'uninitialized'
606
- ? 'not cached yet'
609
+ ? 'not cached yet; first exporter request will load IAM keys'
607
610
  : `${Math.ceil(securityExporterJwks.remainingMs / 1_000)}s remaining (${securityExporterJwks.state})`,
608
611
  },
609
612
  ],
@@ -151,15 +151,20 @@ function checkAuth(env, source, options, results) {
151
151
  }
152
152
  async function checkDependency(name, url, required, tls, probe, results) {
153
153
  const failStatus = required ? 'fail' : 'warn';
154
+ const displayUrl = new URL(url);
155
+ displayUrl.username = '';
156
+ displayUrl.password = '';
157
+ displayUrl.search = '';
158
+ displayUrl.hash = '';
154
159
  try {
155
160
  const result = await probe(url, tls());
156
161
  results.push(result.ok
157
- ? { name, status: 'ok', message: `${url} is reachable (HTTP ${result.statusCode})` }
158
- : { name, status: failStatus, message: `${url} answered HTTP ${result.statusCode}; the service is up but not ready.` });
162
+ ? { name, status: 'ok', message: `${displayUrl} is reachable (HTTP ${result.statusCode})` }
163
+ : { name, status: failStatus, message: `${displayUrl} answered HTTP ${result.statusCode}; the service is up but not ready.` });
159
164
  }
160
- catch (error) {
165
+ catch {
161
166
  const variable = name === 'iam' ? 'HIVE_IAM_INTERNAL_URL' : 'HIVEMEM_INTERNAL_URL';
162
- results.push({ name, status: failStatus, message: `${url} is unreachable (${error instanceof Error ? error.message : String(error)}). Start the service or fix ${variable}.` });
167
+ results.push({ name, status: failStatus, message: `${displayUrl} is unreachable. Start the service or fix ${variable}.` });
163
168
  }
164
169
  }
165
170
  const defaultProbe = async (url, tls) => {
@@ -1,3 +1,4 @@
1
+ import { packageInfo } from '../config/paths.js';
1
2
  export const builtinApiDocs = [
2
3
  { scope: 'router', method: 'GET', route: '/v1/models', title: 'List models', summary: 'Aggregated model registry across configured providers', implemented: true },
3
4
  { scope: 'router', method: 'POST', route: '/v1/responses', title: 'Responses API', summary: 'OpenAI-compatible responses proxy endpoint', implemented: true },
@@ -39,7 +40,7 @@ export function buildRouterOpenApiDocument(adminBaseUrl) {
39
40
  openapi: '3.1.0',
40
41
  info: {
41
42
  title: 'Hive Router Admin API',
42
- version: '0.1.0',
43
+ version: packageInfo().version,
43
44
  description: 'Administrative and enumerated API surface for Hive Router',
44
45
  },
45
46
  servers: [{ url: adminBaseUrl }],
@@ -33,7 +33,7 @@ const CLIENT_CONTROLLED_PROXY_HEADER_NAMES = new Set([
33
33
  ]);
34
34
  export function shouldForwardClientHeader(name) {
35
35
  const lower = name.toLowerCase();
36
- return !SENSITIVE_HEADER_NAMES.has(lower)
36
+ return !isSensitiveHeaderName(lower)
37
37
  && !HOP_BY_HOP_HEADER_NAMES.has(lower)
38
38
  && !CLIENT_CONTROLLED_PROXY_HEADER_NAMES.has(lower)
39
39
  && lower !== 'host'
@@ -49,7 +49,9 @@ function isSensitiveHeaderName(name) {
49
49
  return SENSITIVE_HEADER_NAMES.has(lower)
50
50
  || lower.includes('secret')
51
51
  || lower.includes('token')
52
- || lower.includes('credential');
52
+ || lower.includes('credential')
53
+ || lower.includes('password')
54
+ || /(?:^|[-_])api[-_]?key(?:$|[-_])/.test(lower);
53
55
  }
54
56
  export function redactHeaders(headers) {
55
57
  return Object.fromEntries(Object.entries(headers).map(([key, value]) => [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hivemagnus/hive-router",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Hive Router: OpenAI-compatible model gateway with routing, failover, quotas and traces, plus the admin API and the unified Hive console",
5
5
  "keywords": [
6
6
  "hive",