hippo-memory 1.38.0 → 1.38.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.
- package/dist/capture.js +1 -1
- package/dist/capture.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/compare.d.ts +26 -13
- package/dist/compare.d.ts.map +1 -1
- package/dist/compare.js +50 -20
- package/dist/compare.js.map +1 -1
- package/dist/consolidate.js +5 -4
- package/dist/consolidate.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +61 -0
- package/dist/db.js.map +1 -1
- package/dist/dedupe.d.ts +9 -7
- package/dist/dedupe.d.ts.map +1 -1
- package/dist/dedupe.js +12 -8
- package/dist/dedupe.js.map +1 -1
- package/dist/extensions/openclaw-plugin/index.js +585 -0
- package/dist/extensions/openclaw-plugin/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +7 -16
- package/dist/mcp/server.js.map +1 -1
- package/dist/memory-value.js +1 -1
- package/dist/memory-value.js.map +1 -1
- package/dist/project-identity.d.ts +8 -0
- package/dist/project-identity.d.ts.map +1 -1
- package/dist/project-identity.js +34 -18
- package/dist/project-identity.js.map +1 -1
- package/dist/server.d.ts +28 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +62 -19
- package/dist/server.js.map +1 -1
- package/dist/sleep-redact.d.ts +7 -6
- package/dist/sleep-redact.d.ts.map +1 -1
- package/dist/sleep-redact.js +7 -6
- package/dist/sleep-redact.js.map +1 -1
- package/dist/src/capture.js +1 -1
- package/dist/src/capture.js.map +1 -1
- package/dist/src/cli.js +1 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compare.js +50 -20
- package/dist/src/compare.js.map +1 -1
- package/dist/src/consolidate.js +5 -4
- package/dist/src/consolidate.js.map +1 -1
- package/dist/src/db.js +61 -0
- package/dist/src/db.js.map +1 -1
- package/dist/src/dedupe.js +12 -8
- package/dist/src/dedupe.js.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/server.js +7 -16
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/memory-value.js +1 -1
- package/dist/src/memory-value.js.map +1 -1
- package/dist/src/project-identity.js +34 -18
- package/dist/src/project-identity.js.map +1 -1
- package/dist/src/server.js +62 -19
- package/dist/src/server.js.map +1 -1
- package/dist/src/sleep-redact.js +7 -6
- package/dist/src/sleep-redact.js.map +1 -1
- package/dist/src/store.js +4 -3
- package/dist/src/store.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/store.d.ts +3 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +4 -3
- package/dist/store.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/extensions/openclaw-plugin/index.ts +7 -7
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +2 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -3
package/dist/server.js
CHANGED
|
@@ -391,6 +391,32 @@ function buildMcpClientKey(req) {
|
|
|
391
391
|
const addr = req.socket.remoteAddress ?? 'unknown';
|
|
392
392
|
return `http:${tokenHash}:${addr}`;
|
|
393
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Rate-limit key for a request. Defaults to the socket's remote address.
|
|
396
|
+
*
|
|
397
|
+
* Behind a TLS-terminating proxy (Fly, most PaaS ingress) every socket
|
|
398
|
+
* carries the proxy's address, so per-IP buckets collapse into one global
|
|
399
|
+
* bucket that unauthenticated traffic can drain before auth runs. Set
|
|
400
|
+
* HIPPO_CLIENT_IP_HEADER to the header the proxy stamps with the real
|
|
401
|
+
* client address (fly-client-ip on Fly, which the edge always overwrites)
|
|
402
|
+
* to key buckets per client instead.
|
|
403
|
+
*
|
|
404
|
+
* Only set this when a trusted proxy fronts EVERY request: a directly
|
|
405
|
+
* reachable server honoring the header would let clients mint a fresh
|
|
406
|
+
* bucket per request and bypass the limiter entirely.
|
|
407
|
+
*/
|
|
408
|
+
export function clientIpForRateLimit(req) {
|
|
409
|
+
const header = process.env.HIPPO_CLIENT_IP_HEADER?.toLowerCase();
|
|
410
|
+
if (header) {
|
|
411
|
+
const raw = req.headers[header];
|
|
412
|
+
const first = Array.isArray(raw) ? raw[0] : raw;
|
|
413
|
+
// Take the first entry of a comma-joined list (proxy chains append).
|
|
414
|
+
const ip = first?.split(',')[0]?.trim();
|
|
415
|
+
if (ip)
|
|
416
|
+
return ip;
|
|
417
|
+
}
|
|
418
|
+
return req.socket.remoteAddress ?? 'unknown';
|
|
419
|
+
}
|
|
394
420
|
/**
|
|
395
421
|
* Build a per-request Context from the Authorization header and remote
|
|
396
422
|
* address. Throws HttpError(401) for invalid / missing credentials. Opens
|
|
@@ -526,25 +552,34 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
|
|
|
526
552
|
rejectEncodedSlash(req.url ?? '/');
|
|
527
553
|
const { method, path, query } = parseRequest(req);
|
|
528
554
|
if (method === 'GET' && path === '/health') {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
555
|
+
// Loopback callers (detectServer's stale-pidfile probe reads version and
|
|
556
|
+
// pid) get the full body. Non-loopback callers get liveness only: the
|
|
557
|
+
// version string would fingerprint the build for the public internet and
|
|
558
|
+
// the pid is noise. Platform health checks only need the 200.
|
|
559
|
+
if (isLoopback(req.socket.remoteAddress)) {
|
|
560
|
+
sendJson(res, 200, {
|
|
561
|
+
ok: true,
|
|
562
|
+
version: VERSION,
|
|
563
|
+
started_at: startedAt,
|
|
564
|
+
pid: process.pid,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
sendJson(res, 200, { ok: true });
|
|
569
|
+
}
|
|
535
570
|
return;
|
|
536
571
|
}
|
|
537
572
|
// E3: per-IP rate limit on /v1/* to bound api-key-id enumeration. /health
|
|
538
573
|
// (a liveness probe) and non-/v1 paths are never throttled. A 429 thrown
|
|
539
574
|
// here lands in the createServer catch like any other HttpError.
|
|
540
575
|
//
|
|
541
|
-
// Keyed on the socket's remote address.
|
|
542
|
-
//
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
//
|
|
576
|
+
// Keyed on the socket's remote address by default. Behind a TLS-terminating
|
|
577
|
+
// proxy every socket carries the proxy's address, collapsing the per-IP
|
|
578
|
+
// buckets into one global bucket that pre-auth traffic can drain; set
|
|
579
|
+
// HIPPO_CLIENT_IP_HEADER there so each real client gets its own bucket
|
|
580
|
+
// (see clientIpForRateLimit).
|
|
546
581
|
if (limiter && path.startsWith('/v1/')) {
|
|
547
|
-
const ip = req
|
|
582
|
+
const ip = clientIpForRateLimit(req);
|
|
548
583
|
if (!limiter.check(ip)) {
|
|
549
584
|
throw new HttpError(429, 'rate limit exceeded');
|
|
550
585
|
}
|
|
@@ -3051,10 +3086,18 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
|
|
|
3051
3086
|
/**
|
|
3052
3087
|
* Boot the HTTP daemon on host:port and write the pidfile under hippoRoot.
|
|
3053
3088
|
*
|
|
3054
|
-
* Refuses non-loopback hosts at boot (Footgun #3 from the A1 plan)
|
|
3055
|
-
*
|
|
3056
|
-
*
|
|
3057
|
-
*
|
|
3089
|
+
* Refuses non-loopback hosts at boot (Footgun #3 from the A1 plan) unless
|
|
3090
|
+
* HIPPO_REQUIRE_AUTH=1 is set. The A5 v2 auth middleware (buildContextWithAuth /
|
|
3091
|
+
* requireAuth) has shipped and every route checks it except GET /health
|
|
3092
|
+
* (public by design for platform health checks) and the two connector
|
|
3093
|
+
* webhooks in PUBLIC_ROUTES, which are HMAC-gated by their own signing
|
|
3094
|
+
* secrets and 404 when those secrets are unset. But the loopback
|
|
3095
|
+
* no-auth fallback inside buildContextWithAuth still admits unauthenticated
|
|
3096
|
+
* requests from a loopback remote address, so binding to a non-loopback host
|
|
3097
|
+
* is only safe once that fallback is disabled with HIPPO_REQUIRE_AUTH=1,
|
|
3098
|
+
* which forces every request (loopback or not) through Bearer-token
|
|
3099
|
+
* validation. Without that env var set, a non-loopback bind would expose the
|
|
3100
|
+
* DB to the network with no auth, so we fail fast instead.
|
|
3058
3101
|
*
|
|
3059
3102
|
* Use port: 0 in tests to bind to an ephemeral port and read the actual
|
|
3060
3103
|
* port back via server.address() after listen.
|
|
@@ -3062,10 +3105,10 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
|
|
|
3062
3105
|
export async function serve(opts) {
|
|
3063
3106
|
const host = opts.host ?? '127.0.0.1';
|
|
3064
3107
|
const requestedPort = opts.port ?? Number(process.env.HIPPO_PORT ?? 6789);
|
|
3065
|
-
if (!LOOPBACK_HOSTS.has(host)) {
|
|
3108
|
+
if (!LOOPBACK_HOSTS.has(host) && process.env.HIPPO_REQUIRE_AUTH !== '1') {
|
|
3066
3109
|
throw new Error(`Refusing to bind hippo serve to non-loopback host '${host}' without auth. ` +
|
|
3067
|
-
`
|
|
3068
|
-
`Bind to 127.0.0.1 / ::1 / localhost
|
|
3110
|
+
`Set HIPPO_REQUIRE_AUTH=1 to bind non-loopback; every request then requires ` +
|
|
3111
|
+
`a valid API key. Bind to 127.0.0.1 / ::1 / localhost otherwise.`);
|
|
3069
3112
|
}
|
|
3070
3113
|
// H3: refuse to start if a live hippo server already serves this hippoRoot.
|
|
3071
3114
|
// detectServer probes the recorded /health — a stale pidfile is unlinked and
|