@xdarkicex/openclaw-memory-libravdb 1.4.80 → 1.5.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 +1 -1
- package/dist/grpc-client.d.ts +20 -1
- package/dist/grpc-client.js +63 -8
- package/dist/index.js +100 -20
- package/dist/plugin-runtime.d.ts +2 -0
- package/dist/plugin-runtime.js +32 -1
- package/dist/types.d.ts +17 -0
- package/docs/TLS_configuration.md +112 -0
- package/docs/configuration.md +51 -1
- package/docs/mTLS_configuration.md +78 -0
- package/openclaw.plugin.json +22 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -187,7 +187,7 @@ All keys are optional. For the full reference, see [Configuration](./docs/config
|
|
|
187
187
|
|
|
188
188
|
- New install: [Install](./docs/install.md), [Installation reference](./docs/installation.md)
|
|
189
189
|
- Understand the design: [Problem](./docs/problem.md), [Architecture](./docs/architecture.md), [ADRs](./docs/architecture-decisions/README.md)
|
|
190
|
-
- Configure: [Configuration](./docs/configuration.md), [Features](./docs/features.md), [Embedding profiles](./docs/embedding-profiles.md), [Models](./docs/models.md)
|
|
190
|
+
- Configure: [Configuration](./docs/configuration.md), [TLS configuration](./docs/TLS_configuration.md), [mTLS configuration](./docs/mTLS_configuration.md), [Features](./docs/features.md), [Embedding profiles](./docs/embedding-profiles.md), [Models](./docs/models.md)
|
|
191
191
|
- Operate safely: [Security](./docs/security.md), [Uninstall](./docs/uninstall.md)
|
|
192
192
|
- Advanced operations: [Performance and tuning](./docs/performance-and-tuning.md)
|
|
193
193
|
- Work from source: [Development](./docs/development.md), [Contributing](./docs/contributing.md)
|
package/dist/grpc-client.d.ts
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
1
2
|
export interface GrpcClientOptions {
|
|
2
3
|
endpoint: string;
|
|
3
4
|
secret?: string;
|
|
4
5
|
timeoutMs?: number;
|
|
6
|
+
tlsCaPath?: string;
|
|
7
|
+
tlsMode?: "auto" | "tls" | "insecure";
|
|
8
|
+
tlsClientCertPath?: string;
|
|
9
|
+
tlsClientKeyPath?: string;
|
|
5
10
|
}
|
|
6
11
|
export declare function resolveGrpcTarget(endpoint: string): string;
|
|
7
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Selects gRPC credential mode based on endpoint address class.
|
|
14
|
+
*
|
|
15
|
+
* - Unix socket endpoints → plaintext (local-only transport)
|
|
16
|
+
* - Loopback addresses (localhost, 127.0.0.1, ::1) → plaintext
|
|
17
|
+
* - All other TCP and DNS targets → TLS
|
|
18
|
+
*
|
|
19
|
+
* resolveGrpcCredentials uses this classification to return the
|
|
20
|
+
* appropriate grpc.ChannelCredentials. Pass tlsCaPath to load a
|
|
21
|
+
* custom CA certificate PEM file for self-signed or private CA
|
|
22
|
+
* deployments. Omit tlsCaPath for publicly trusted certificates
|
|
23
|
+
* (Let's Encrypt, cert-manager) — the system CA pool is used.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveGrpcCredentialMode(endpoint: string, tlsMode?: "auto" | "tls" | "insecure"): "insecure" | "tls";
|
|
26
|
+
export declare function resolveGrpcCredentials(endpoint: string, tlsCaPath?: string, tlsMode?: "auto" | "tls" | "insecure", tlsClientCertPath?: string, tlsClientKeyPath?: string): grpc.ChannelCredentials;
|
|
8
27
|
export declare class GrpcKernelClient {
|
|
9
28
|
private client;
|
|
10
29
|
private readonly secret;
|
package/dist/grpc-client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHmac } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import fs from "node:fs";
|
|
4
5
|
import * as grpc from "@grpc/grpc-js";
|
|
5
6
|
import * as protoLoader from "@grpc/proto-loader";
|
|
6
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -10,18 +11,72 @@ const PROTO_PATH = path.resolve(__dirname, "./proto/intelligence_kernel/v1/kerne
|
|
|
10
11
|
export function resolveGrpcTarget(endpoint) {
|
|
11
12
|
return endpoint.startsWith("tcp:") ? endpoint.substring(4) : endpoint;
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Selects gRPC credential mode based on endpoint address class.
|
|
16
|
+
*
|
|
17
|
+
* - Unix socket endpoints → plaintext (local-only transport)
|
|
18
|
+
* - Loopback addresses (localhost, 127.0.0.1, ::1) → plaintext
|
|
19
|
+
* - All other TCP and DNS targets → TLS
|
|
20
|
+
*
|
|
21
|
+
* resolveGrpcCredentials uses this classification to return the
|
|
22
|
+
* appropriate grpc.ChannelCredentials. Pass tlsCaPath to load a
|
|
23
|
+
* custom CA certificate PEM file for self-signed or private CA
|
|
24
|
+
* deployments. Omit tlsCaPath for publicly trusted certificates
|
|
25
|
+
* (Let's Encrypt, cert-manager) — the system CA pool is used.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveGrpcCredentialMode(endpoint, tlsMode) {
|
|
28
|
+
if (tlsMode === "tls")
|
|
29
|
+
return "tls";
|
|
30
|
+
if (tlsMode === "insecure")
|
|
31
|
+
return "insecure";
|
|
32
|
+
// "auto" or undefined — address-based heuristic
|
|
14
33
|
const target = resolveGrpcTarget(endpoint).trim();
|
|
15
|
-
if (target.startsWith("unix:"))
|
|
34
|
+
if (target.startsWith("unix:"))
|
|
16
35
|
return "insecure";
|
|
17
|
-
}
|
|
18
36
|
const host = extractGrpcHost(target);
|
|
19
37
|
return isLoopbackHost(host) ? "insecure" : "tls";
|
|
20
38
|
}
|
|
21
|
-
function resolveGrpcCredentials(endpoint) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
39
|
+
export function resolveGrpcCredentials(endpoint, tlsCaPath, tlsMode, tlsClientCertPath, tlsClientKeyPath) {
|
|
40
|
+
if (resolveGrpcCredentialMode(endpoint, tlsMode) === "insecure") {
|
|
41
|
+
return grpc.credentials.createInsecure();
|
|
42
|
+
}
|
|
43
|
+
// tlsMode is "tls" or "auto"/undefined resolved to "tls"
|
|
44
|
+
let rootCerts = null;
|
|
45
|
+
if (tlsCaPath) {
|
|
46
|
+
try {
|
|
47
|
+
rootCerts = fs.readFileSync(tlsCaPath);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
51
|
+
throw new Error(`LibraVDB: failed to load TLS CA certificate from "${tlsCaPath}": ${msg}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Client certificate and key must both be present or both absent
|
|
55
|
+
const hasCert = tlsClientCertPath !== undefined;
|
|
56
|
+
const hasKey = tlsClientKeyPath !== undefined;
|
|
57
|
+
if (hasCert !== hasKey) {
|
|
58
|
+
throw new Error("LibraVDB: grpcEndpointTlsClientCert and grpcEndpointTlsClientKey " +
|
|
59
|
+
"must both be set or both be omitted");
|
|
60
|
+
}
|
|
61
|
+
let clientKey = null;
|
|
62
|
+
let clientCert = null;
|
|
63
|
+
if (tlsClientCertPath && tlsClientKeyPath) {
|
|
64
|
+
try {
|
|
65
|
+
clientCert = fs.readFileSync(tlsClientCertPath);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
69
|
+
throw new Error(`LibraVDB: failed to load TLS client certificate from "${tlsClientCertPath}": ${msg}`);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
clientKey = fs.readFileSync(tlsClientKeyPath);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
76
|
+
throw new Error(`LibraVDB: failed to load TLS client key from "${tlsClientKeyPath}": ${msg}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return grpc.credentials.createSsl(rootCerts, clientKey, clientCert);
|
|
25
80
|
}
|
|
26
81
|
function extractGrpcHost(target) {
|
|
27
82
|
const withoutDnsPrefix = target.startsWith("dns:///") ? target.slice("dns:///".length) : target;
|
|
@@ -54,7 +109,7 @@ export class GrpcKernelClient {
|
|
|
54
109
|
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
|
55
110
|
const kernelService = protoDescriptor.intelligence_kernel.v1.IntelligenceKernel;
|
|
56
111
|
const target = resolveGrpcTarget(options.endpoint);
|
|
57
|
-
this.client = new kernelService(target, resolveGrpcCredentials(options.endpoint));
|
|
112
|
+
this.client = new kernelService(target, resolveGrpcCredentials(options.endpoint, options.tlsCaPath, options.tlsMode, options.tlsClientCertPath, options.tlsClientKeyPath));
|
|
58
113
|
}
|
|
59
114
|
getMetadata(signed = true) {
|
|
60
115
|
const md = new grpc.Metadata();
|
package/dist/index.js
CHANGED
|
@@ -8810,14 +8810,14 @@ var require_tls_helpers = __commonJS({
|
|
|
8810
8810
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
8811
8811
|
exports2.CIPHER_SUITES = void 0;
|
|
8812
8812
|
exports2.getDefaultRootsData = getDefaultRootsData;
|
|
8813
|
-
var
|
|
8813
|
+
var fs5 = __require("fs");
|
|
8814
8814
|
exports2.CIPHER_SUITES = process.env.GRPC_SSL_CIPHER_SUITES;
|
|
8815
8815
|
var DEFAULT_ROOTS_FILE_PATH = process.env.GRPC_DEFAULT_SSL_ROOTS_FILE_PATH;
|
|
8816
8816
|
var defaultRootsData = null;
|
|
8817
8817
|
function getDefaultRootsData() {
|
|
8818
8818
|
if (DEFAULT_ROOTS_FILE_PATH) {
|
|
8819
8819
|
if (defaultRootsData === null) {
|
|
8820
|
-
defaultRootsData =
|
|
8820
|
+
defaultRootsData = fs5.readFileSync(DEFAULT_ROOTS_FILE_PATH);
|
|
8821
8821
|
}
|
|
8822
8822
|
return defaultRootsData;
|
|
8823
8823
|
}
|
|
@@ -14324,7 +14324,7 @@ var require_fetch = __commonJS({
|
|
|
14324
14324
|
module2.exports = fetch;
|
|
14325
14325
|
var asPromise = require_aspromise();
|
|
14326
14326
|
var inquire2 = require_inquire();
|
|
14327
|
-
var
|
|
14327
|
+
var fs5 = inquire2("fs");
|
|
14328
14328
|
function fetch(filename, options, callback) {
|
|
14329
14329
|
if (typeof options === "function") {
|
|
14330
14330
|
callback = options;
|
|
@@ -14333,8 +14333,8 @@ var require_fetch = __commonJS({
|
|
|
14333
14333
|
options = {};
|
|
14334
14334
|
if (!callback)
|
|
14335
14335
|
return asPromise(fetch, this, filename, options);
|
|
14336
|
-
if (!options.xhr &&
|
|
14337
|
-
return
|
|
14336
|
+
if (!options.xhr && fs5 && fs5.readFile)
|
|
14337
|
+
return fs5.readFile(filename, function fetchReadFileCallback(err, contents) {
|
|
14338
14338
|
return err && typeof XMLHttpRequest !== "undefined" ? fetch.xhr(filename, options, callback) : err ? callback(err) : callback(null, options.binary ? contents : contents.toString("utf8"));
|
|
14339
14339
|
});
|
|
14340
14340
|
return fetch.xhr(filename, options, callback);
|
|
@@ -20608,7 +20608,7 @@ var require_util2 = __commonJS({
|
|
|
20608
20608
|
"use strict";
|
|
20609
20609
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20610
20610
|
exports2.addCommonProtos = exports2.loadProtosWithOptionsSync = exports2.loadProtosWithOptions = void 0;
|
|
20611
|
-
var
|
|
20611
|
+
var fs5 = __require("fs");
|
|
20612
20612
|
var path5 = __require("path");
|
|
20613
20613
|
var Protobuf = require_protobufjs();
|
|
20614
20614
|
function addIncludePathResolver(root, includePaths) {
|
|
@@ -20620,7 +20620,7 @@ var require_util2 = __commonJS({
|
|
|
20620
20620
|
for (const directory of includePaths) {
|
|
20621
20621
|
const fullPath = path5.join(directory, target);
|
|
20622
20622
|
try {
|
|
20623
|
-
|
|
20623
|
+
fs5.accessSync(fullPath, fs5.constants.R_OK);
|
|
20624
20624
|
return fullPath;
|
|
20625
20625
|
} catch (err) {
|
|
20626
20626
|
continue;
|
|
@@ -30442,7 +30442,7 @@ var require_certificate_provider = __commonJS({
|
|
|
30442
30442
|
"use strict";
|
|
30443
30443
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
30444
30444
|
exports2.FileWatcherCertificateProvider = void 0;
|
|
30445
|
-
var
|
|
30445
|
+
var fs5 = __require("fs");
|
|
30446
30446
|
var logging = require_logging();
|
|
30447
30447
|
var constants_1 = require_constants();
|
|
30448
30448
|
var util_1 = __require("util");
|
|
@@ -30450,7 +30450,7 @@ var require_certificate_provider = __commonJS({
|
|
|
30450
30450
|
function trace(text) {
|
|
30451
30451
|
logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, text);
|
|
30452
30452
|
}
|
|
30453
|
-
var readFilePromise = (0, util_1.promisify)(
|
|
30453
|
+
var readFilePromise = (0, util_1.promisify)(fs5.readFile);
|
|
30454
30454
|
var FileWatcherCertificateProvider = class {
|
|
30455
30455
|
constructor(config) {
|
|
30456
30456
|
this.config = config;
|
|
@@ -39661,21 +39661,63 @@ var protoLoader = __toESM(require_src2(), 1);
|
|
|
39661
39661
|
import { createHmac } from "node:crypto";
|
|
39662
39662
|
import path3 from "node:path";
|
|
39663
39663
|
import { fileURLToPath } from "node:url";
|
|
39664
|
+
import fs3 from "node:fs";
|
|
39664
39665
|
var __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
|
|
39665
39666
|
var PROTO_PATH = path3.resolve(__dirname2, "./proto/intelligence_kernel/v1/kernel.proto");
|
|
39666
39667
|
function resolveGrpcTarget(endpoint) {
|
|
39667
39668
|
return endpoint.startsWith("tcp:") ? endpoint.substring(4) : endpoint;
|
|
39668
39669
|
}
|
|
39669
|
-
function resolveGrpcCredentialMode(endpoint) {
|
|
39670
|
+
function resolveGrpcCredentialMode(endpoint, tlsMode) {
|
|
39671
|
+
if (tlsMode === "tls") return "tls";
|
|
39672
|
+
if (tlsMode === "insecure") return "insecure";
|
|
39670
39673
|
const target = resolveGrpcTarget(endpoint).trim();
|
|
39671
|
-
if (target.startsWith("unix:"))
|
|
39672
|
-
return "insecure";
|
|
39673
|
-
}
|
|
39674
|
+
if (target.startsWith("unix:")) return "insecure";
|
|
39674
39675
|
const host = extractGrpcHost(target);
|
|
39675
39676
|
return isLoopbackHost(host) ? "insecure" : "tls";
|
|
39676
39677
|
}
|
|
39677
|
-
function resolveGrpcCredentials(endpoint) {
|
|
39678
|
-
|
|
39678
|
+
function resolveGrpcCredentials(endpoint, tlsCaPath, tlsMode, tlsClientCertPath, tlsClientKeyPath) {
|
|
39679
|
+
if (resolveGrpcCredentialMode(endpoint, tlsMode) === "insecure") {
|
|
39680
|
+
return grpc.credentials.createInsecure();
|
|
39681
|
+
}
|
|
39682
|
+
let rootCerts = null;
|
|
39683
|
+
if (tlsCaPath) {
|
|
39684
|
+
try {
|
|
39685
|
+
rootCerts = fs3.readFileSync(tlsCaPath);
|
|
39686
|
+
} catch (err) {
|
|
39687
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39688
|
+
throw new Error(
|
|
39689
|
+
`LibraVDB: failed to load TLS CA certificate from "${tlsCaPath}": ${msg}`
|
|
39690
|
+
);
|
|
39691
|
+
}
|
|
39692
|
+
}
|
|
39693
|
+
const hasCert = tlsClientCertPath !== void 0;
|
|
39694
|
+
const hasKey = tlsClientKeyPath !== void 0;
|
|
39695
|
+
if (hasCert !== hasKey) {
|
|
39696
|
+
throw new Error(
|
|
39697
|
+
"LibraVDB: grpcEndpointTlsClientCert and grpcEndpointTlsClientKey must both be set or both be omitted"
|
|
39698
|
+
);
|
|
39699
|
+
}
|
|
39700
|
+
let clientKey = null;
|
|
39701
|
+
let clientCert = null;
|
|
39702
|
+
if (tlsClientCertPath && tlsClientKeyPath) {
|
|
39703
|
+
try {
|
|
39704
|
+
clientCert = fs3.readFileSync(tlsClientCertPath);
|
|
39705
|
+
} catch (err) {
|
|
39706
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39707
|
+
throw new Error(
|
|
39708
|
+
`LibraVDB: failed to load TLS client certificate from "${tlsClientCertPath}": ${msg}`
|
|
39709
|
+
);
|
|
39710
|
+
}
|
|
39711
|
+
try {
|
|
39712
|
+
clientKey = fs3.readFileSync(tlsClientKeyPath);
|
|
39713
|
+
} catch (err) {
|
|
39714
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39715
|
+
throw new Error(
|
|
39716
|
+
`LibraVDB: failed to load TLS client key from "${tlsClientKeyPath}": ${msg}`
|
|
39717
|
+
);
|
|
39718
|
+
}
|
|
39719
|
+
}
|
|
39720
|
+
return grpc.credentials.createSsl(rootCerts, clientKey, clientCert);
|
|
39679
39721
|
}
|
|
39680
39722
|
function extractGrpcHost(target) {
|
|
39681
39723
|
const withoutDnsPrefix = target.startsWith("dns:///") ? target.slice("dns:///".length) : target;
|
|
@@ -39708,7 +39750,13 @@ var GrpcKernelClient = class {
|
|
|
39708
39750
|
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
|
39709
39751
|
const kernelService = protoDescriptor.intelligence_kernel.v1.IntelligenceKernel;
|
|
39710
39752
|
const target = resolveGrpcTarget(options.endpoint);
|
|
39711
|
-
this.client = new kernelService(target, resolveGrpcCredentials(
|
|
39753
|
+
this.client = new kernelService(target, resolveGrpcCredentials(
|
|
39754
|
+
options.endpoint,
|
|
39755
|
+
options.tlsCaPath,
|
|
39756
|
+
options.tlsMode,
|
|
39757
|
+
options.tlsClientCertPath,
|
|
39758
|
+
options.tlsClientKeyPath
|
|
39759
|
+
));
|
|
39712
39760
|
}
|
|
39713
39761
|
getMetadata(signed = true) {
|
|
39714
39762
|
const md = new grpc.Metadata();
|
|
@@ -39782,7 +39830,7 @@ var GrpcKernelClient = class {
|
|
|
39782
39830
|
};
|
|
39783
39831
|
|
|
39784
39832
|
// src/sidecar.ts
|
|
39785
|
-
import
|
|
39833
|
+
import fs4 from "node:fs";
|
|
39786
39834
|
import net from "node:net";
|
|
39787
39835
|
import os3 from "node:os";
|
|
39788
39836
|
import path4 from "node:path";
|
|
@@ -40077,7 +40125,7 @@ function resolveConfiguredEndpoint(cfg) {
|
|
|
40077
40125
|
function daemonProvisioningHint() {
|
|
40078
40126
|
return "If you installed the npm package, install and start libravdbd separately; the package does not provision the daemon binary, ONNX Runtime, or model assets.";
|
|
40079
40127
|
}
|
|
40080
|
-
function defaultEndpoint(platform = process.platform, homeDir = os3.homedir(), pathExists =
|
|
40128
|
+
function defaultEndpoint(platform = process.platform, homeDir = os3.homedir(), pathExists = fs4.existsSync) {
|
|
40081
40129
|
const envEndpoint = normalizeConfiguredEndpoint(process.env.LIBRAVDB_RPC_ENDPOINT);
|
|
40082
40130
|
if (envEndpoint) {
|
|
40083
40131
|
return envEndpoint;
|
|
@@ -40178,6 +40226,8 @@ function sleep2(delayMs) {
|
|
|
40178
40226
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
40179
40227
|
var DEFAULT_RPC_TIMEOUT_MS = 3e4;
|
|
40180
40228
|
var STARTUP_HEALTH_TIMEOUT_MS = 2e3;
|
|
40229
|
+
var VALID_TLS_MODES = ["auto", "tls", "insecure"];
|
|
40230
|
+
var isTlsModeValid = (m) => VALID_TLS_MODES.includes(m);
|
|
40181
40231
|
function resolveStartupHealthTimeoutMs(cfg) {
|
|
40182
40232
|
return Math.max(STARTUP_HEALTH_TIMEOUT_MS, cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS);
|
|
40183
40233
|
}
|
|
@@ -40192,6 +40242,32 @@ function createPluginRuntime(cfg, logger = console) {
|
|
|
40192
40242
|
}
|
|
40193
40243
|
if (!started) {
|
|
40194
40244
|
started = (async () => {
|
|
40245
|
+
if (cfg.grpcEndpoint) {
|
|
40246
|
+
if (cfg.grpcEndpointTlsMode !== void 0 && !isTlsModeValid(cfg.grpcEndpointTlsMode)) {
|
|
40247
|
+
throw new Error(
|
|
40248
|
+
`LibraVDB: invalid grpcEndpointTlsMode "${cfg.grpcEndpointTlsMode}" \u2014 must be "auto", "tls", or "insecure"`
|
|
40249
|
+
);
|
|
40250
|
+
}
|
|
40251
|
+
const hasClientCert = cfg.grpcEndpointTlsClientCert !== void 0;
|
|
40252
|
+
const hasClientKey = cfg.grpcEndpointTlsClientKey !== void 0;
|
|
40253
|
+
if (hasClientCert !== hasClientKey) {
|
|
40254
|
+
throw new Error(
|
|
40255
|
+
"LibraVDB: grpcEndpointTlsClientCert and grpcEndpointTlsClientKey must both be set or both be omitted"
|
|
40256
|
+
);
|
|
40257
|
+
}
|
|
40258
|
+
if (cfg.grpcEndpointTlsMode === "insecure") {
|
|
40259
|
+
if (cfg.grpcEndpointTlsCa) {
|
|
40260
|
+
logger.warn?.(
|
|
40261
|
+
`LibraVDB: grpcEndpointTlsCa is set but grpcEndpointTlsMode is "insecure" \u2014 the CA file will not be used`
|
|
40262
|
+
);
|
|
40263
|
+
}
|
|
40264
|
+
if (cfg.grpcEndpointTlsClientCert) {
|
|
40265
|
+
logger.warn?.(
|
|
40266
|
+
`LibraVDB: grpcEndpointTlsClientCert is set but grpcEndpointTlsMode is "insecure" \u2014 client certificate will not be sent`
|
|
40267
|
+
);
|
|
40268
|
+
}
|
|
40269
|
+
}
|
|
40270
|
+
}
|
|
40195
40271
|
const sidecar = await startSidecar(cfg, logger);
|
|
40196
40272
|
const rpc = new RpcClient(sidecar.socket, {
|
|
40197
40273
|
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS
|
|
@@ -40208,12 +40284,16 @@ function createPluginRuntime(cfg, logger = console) {
|
|
|
40208
40284
|
}
|
|
40209
40285
|
let kernel = null;
|
|
40210
40286
|
if (cfg.grpcEndpoint) {
|
|
40287
|
+
const secret = loadSecretFromEnv();
|
|
40211
40288
|
try {
|
|
40212
|
-
const secret = loadSecretFromEnv();
|
|
40213
40289
|
kernel = new GrpcKernelClient({
|
|
40214
40290
|
endpoint: cfg.grpcEndpoint,
|
|
40215
40291
|
secret,
|
|
40216
|
-
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS
|
|
40292
|
+
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS,
|
|
40293
|
+
tlsCaPath: cfg.grpcEndpointTlsCa,
|
|
40294
|
+
tlsMode: cfg.grpcEndpointTlsMode,
|
|
40295
|
+
tlsClientCertPath: cfg.grpcEndpointTlsClientCert,
|
|
40296
|
+
tlsClientKeyPath: cfg.grpcEndpointTlsClientKey
|
|
40217
40297
|
});
|
|
40218
40298
|
} catch (error) {
|
|
40219
40299
|
logger.warn?.(`LibraVDB: failed to initialize gRPC kernel client: ${formatError(error)}`);
|
package/dist/plugin-runtime.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { LoggerLike, PluginConfig } from "./types.js";
|
|
|
4
4
|
export type RpcGetter = () => Promise<RpcClient>;
|
|
5
5
|
export declare const DEFAULT_RPC_TIMEOUT_MS = 30000;
|
|
6
6
|
export declare const STARTUP_HEALTH_TIMEOUT_MS = 2000;
|
|
7
|
+
export declare const VALID_TLS_MODES: readonly ["auto", "tls", "insecure"];
|
|
8
|
+
export type ValidTlsMode = typeof VALID_TLS_MODES[number];
|
|
7
9
|
export declare function resolveStartupHealthTimeoutMs(cfg: PluginConfig): number;
|
|
8
10
|
export interface LifecycleHint {
|
|
9
11
|
hook: "before_reset" | "session_end";
|
package/dist/plugin-runtime.js
CHANGED
|
@@ -5,6 +5,8 @@ import { formatError } from "./format-error.js";
|
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
export const DEFAULT_RPC_TIMEOUT_MS = 30000;
|
|
7
7
|
export const STARTUP_HEALTH_TIMEOUT_MS = 2000;
|
|
8
|
+
export const VALID_TLS_MODES = ["auto", "tls", "insecure"];
|
|
9
|
+
const isTlsModeValid = (m) => VALID_TLS_MODES.includes(m);
|
|
8
10
|
export function resolveStartupHealthTimeoutMs(cfg) {
|
|
9
11
|
return Math.max(STARTUP_HEALTH_TIMEOUT_MS, cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS);
|
|
10
12
|
}
|
|
@@ -19,6 +21,30 @@ export function createPluginRuntime(cfg, logger = console) {
|
|
|
19
21
|
}
|
|
20
22
|
if (!started) {
|
|
21
23
|
started = (async () => {
|
|
24
|
+
if (cfg.grpcEndpoint) {
|
|
25
|
+
if (cfg.grpcEndpointTlsMode !== undefined &&
|
|
26
|
+
!isTlsModeValid(cfg.grpcEndpointTlsMode)) {
|
|
27
|
+
throw new Error(`LibraVDB: invalid grpcEndpointTlsMode "${cfg.grpcEndpointTlsMode}" — ` +
|
|
28
|
+
`must be "auto", "tls", or "insecure"`);
|
|
29
|
+
}
|
|
30
|
+
const hasClientCert = cfg.grpcEndpointTlsClientCert !== undefined;
|
|
31
|
+
const hasClientKey = cfg.grpcEndpointTlsClientKey !== undefined;
|
|
32
|
+
if (hasClientCert !== hasClientKey) {
|
|
33
|
+
throw new Error("LibraVDB: grpcEndpointTlsClientCert and " +
|
|
34
|
+
"grpcEndpointTlsClientKey must both be set or both be omitted");
|
|
35
|
+
}
|
|
36
|
+
if (cfg.grpcEndpointTlsMode === "insecure") {
|
|
37
|
+
if (cfg.grpcEndpointTlsCa) {
|
|
38
|
+
logger.warn?.(`LibraVDB: grpcEndpointTlsCa is set but grpcEndpointTlsMode ` +
|
|
39
|
+
`is "insecure" — the CA file will not be used`);
|
|
40
|
+
}
|
|
41
|
+
if (cfg.grpcEndpointTlsClientCert) {
|
|
42
|
+
logger.warn?.(`LibraVDB: grpcEndpointTlsClientCert is set but ` +
|
|
43
|
+
`grpcEndpointTlsMode is "insecure" — client certificate ` +
|
|
44
|
+
`will not be sent`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
22
48
|
const sidecar = await startSidecar(cfg, logger);
|
|
23
49
|
const rpc = new RpcClient(sidecar.socket, {
|
|
24
50
|
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS,
|
|
@@ -37,15 +63,20 @@ export function createPluginRuntime(cfg, logger = console) {
|
|
|
37
63
|
}
|
|
38
64
|
let kernel = null;
|
|
39
65
|
if (cfg.grpcEndpoint) {
|
|
66
|
+
const secret = loadSecretFromEnv();
|
|
40
67
|
try {
|
|
41
|
-
const secret = loadSecretFromEnv();
|
|
42
68
|
kernel = new GrpcKernelClient({
|
|
43
69
|
endpoint: cfg.grpcEndpoint,
|
|
44
70
|
secret,
|
|
45
71
|
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS,
|
|
72
|
+
tlsCaPath: cfg.grpcEndpointTlsCa,
|
|
73
|
+
tlsMode: cfg.grpcEndpointTlsMode,
|
|
74
|
+
tlsClientCertPath: cfg.grpcEndpointTlsClientCert,
|
|
75
|
+
tlsClientKeyPath: cfg.grpcEndpointTlsClientKey,
|
|
46
76
|
});
|
|
47
77
|
}
|
|
48
78
|
catch (error) {
|
|
79
|
+
// Only gRPC init errors land here — config validation already threw above
|
|
49
80
|
logger.warn?.(`LibraVDB: failed to initialize gRPC kernel client: ${formatError(error)}`);
|
|
50
81
|
}
|
|
51
82
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -90,6 +90,23 @@ export interface PluginConfig {
|
|
|
90
90
|
maxRetries?: number;
|
|
91
91
|
logLevel?: "debug" | "info" | "warn" | "error";
|
|
92
92
|
grpcEndpoint?: string;
|
|
93
|
+
grpcEndpointTlsCa?: string;
|
|
94
|
+
/** Path to a client certificate PEM file for mTLS.
|
|
95
|
+
* The file must contain a PEM-encoded X.509 certificate. Leaf first;
|
|
96
|
+
* intermediates may follow in the same file. Required when the daemon
|
|
97
|
+
* requires a client certificate (mTLS). Must be paired with
|
|
98
|
+
* grpcEndpointTlsClientKey. */
|
|
99
|
+
grpcEndpointTlsClientCert?: string;
|
|
100
|
+
/** Path to the client private key PEM file.
|
|
101
|
+
* Must correspond to the leaf certificate in grpcEndpointTlsClientCert.
|
|
102
|
+
* Accepts RSA (PKCS#1/PKCS#8), ECDSA (P-256/P-384/P-521), Ed25519.
|
|
103
|
+
* Required when grpcEndpointTlsClientCert is set. */
|
|
104
|
+
grpcEndpointTlsClientKey?: string;
|
|
105
|
+
/** Controls gRPC credential mode.
|
|
106
|
+
* "auto" (default) — loopback and unix → plaintext, remote → TLS.
|
|
107
|
+
* "tls" — always use TLS regardless of address.
|
|
108
|
+
* "insecure" — always use plaintext (service mesh, tunnel). */
|
|
109
|
+
grpcEndpointTlsMode?: "auto" | "tls" | "insecure";
|
|
93
110
|
}
|
|
94
111
|
export interface SearchResult {
|
|
95
112
|
id: string;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# TLS Configuration
|
|
2
|
+
|
|
3
|
+
The plugin selects the right credentials automatically based on which address it connects to. Unix sockets and loopback addresses (localhost, 127.0.0.1, ::1) always use plaintext. All other addresses use TLS. In most deployments no TLS configuration is needed at all. The only time manual configuration is required is when the daemon serves TLS on a loopback address, when the daemon uses a self-signed or private-CA certificate, or when infrastructure such as a service mesh handles TLS outside the plugin.
|
|
4
|
+
|
|
5
|
+
## Default behavior
|
|
6
|
+
|
|
7
|
+
The plugin applies the following rules automatically:
|
|
8
|
+
|
|
9
|
+
| Endpoint type | Credential mode |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Unix socket (`unix:/path/to/socket`) | Plaintext |
|
|
12
|
+
| Loopback (`tcp:127.0.0.1:port`, `tcp:localhost:port`, `[::1]:port`) | Plaintext |
|
|
13
|
+
| Any other TCP or DNS address | TLS |
|
|
14
|
+
|
|
15
|
+
Because these rules are automatic, most users do not set any TLS-related fields. Plaintext is used where it is safe (local transport) and TLS is used where it is needed (network transport).
|
|
16
|
+
|
|
17
|
+
## Configuration fields
|
|
18
|
+
|
|
19
|
+
| Field | Type | Default | When to use |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| `grpcEndpoint` | string | — | The daemon address. Set to a unix socket path, a loopback address, or a remote host. |
|
|
22
|
+
| `grpcEndpointTlsCa` | string | — | Path to a CA certificate PEM file. Required only when the daemon certificate is self-signed or signed by a private CA not in the system certificate store. |
|
|
23
|
+
| `grpcEndpointTlsMode` | `"auto"` \| `"tls"` \| `"insecure"` | `"auto"` | Override the automatic selection. `"auto"` applies the default rules above. `"tls"` forces TLS regardless of address. `"insecure"` forces plaintext regardless of address. |
|
|
24
|
+
|
|
25
|
+
`grpcEndpointTlsMode` values explained:
|
|
26
|
+
|
|
27
|
+
- **`"auto"`** (default) — apply the automatic rules. Unix sockets and loopback use plaintext; all other addresses use TLS.
|
|
28
|
+
- **`"tls"`** — always use TLS, even for loopback addresses. Use this when the daemon has TLS enabled on a loopback address.
|
|
29
|
+
- **`"insecure"`** — always use plaintext, even for remote addresses. Use this only when a service mesh or TLS-terminating tunnel handles encryption externally.
|
|
30
|
+
|
|
31
|
+
## Deployment scenarios
|
|
32
|
+
|
|
33
|
+
### Local daemon (default)
|
|
34
|
+
|
|
35
|
+
The daemon runs on the same machine, listening on a unix socket or a loopback address.
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"grpcEndpoint": "unix:/home/user/.libravdbd/run/libravdb.sock"
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
or:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"grpcEndpoint": "tcp:127.0.0.1:9090"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The plugin automatically uses plaintext. No TLS fields are needed.
|
|
52
|
+
|
|
53
|
+
### Remote daemon with a trusted certificate
|
|
54
|
+
|
|
55
|
+
The daemon runs on a remote host and presents a certificate issued by a public CA such as Let's Encrypt or cert-manager.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"grpcEndpoint": "tcp:libravdb.k8s.internal:9090"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
TLS is automatic. The plugin uses the system certificate store to verify the daemon's certificate, so no additional configuration is needed.
|
|
64
|
+
|
|
65
|
+
### Remote daemon with a self-signed or private CA certificate
|
|
66
|
+
|
|
67
|
+
The daemon runs on a remote host and uses a self-signed certificate or a certificate signed by a private/internal CA not in the system certificate store.
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"grpcEndpoint": "tcp:libravdb.internal:9090",
|
|
72
|
+
"grpcEndpointTlsCa": "/etc/certs/company-ca.pem"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The CA certificate must be the certificate of the CA that signed the daemon's server certificate — not the server certificate itself. The plugin uses this CA to verify the daemon's certificate during the TLS handshake. Without it, the plugin will reject the daemon's certificate as untrusted.
|
|
77
|
+
|
|
78
|
+
### TLS on a loopback address
|
|
79
|
+
|
|
80
|
+
The daemon has TLS enabled on a loopback address. This is uncommon. The automatic rules would select plaintext for a loopback address, so an explicit override is required.
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"grpcEndpoint": "tcp:127.0.0.1:9090",
|
|
85
|
+
"grpcEndpointTlsMode": "tls"
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Set `grpcEndpointTlsMode` to `"tls"` to force the plugin to use TLS even on the loopback address. The plugin will use the system certificate store for verification. If the daemon uses a self-signed certificate, add `grpcEndpointTlsCa` as well.
|
|
90
|
+
|
|
91
|
+
## Service mesh and tunnels
|
|
92
|
+
|
|
93
|
+
When the daemon runs behind Istio, Envoy, or any other infrastructure that terminates TLS at the mesh or tunnel layer, the plugin should not attempt its own TLS. Set `grpcEndpointTlsMode` to `"insecure"` so the plugin uses plaintext and lets the mesh handle encryption:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"grpcEndpoint": "tcp:libravdb.mesh.svc:9090",
|
|
98
|
+
"grpcEndpointTlsMode": "insecure"
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This applies even for remote addresses. The mesh terminates TLS at the boundary, and the plugin communicates with the mesh over plaintext on the inside.
|
|
103
|
+
|
|
104
|
+
## Error reference
|
|
105
|
+
|
|
106
|
+
| Error | Likely cause | Fix |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `UNAVAILABLE / connection closed / TLS handshake failed` when connecting to a loopback address | The daemon has TLS enabled on a loopback address but the plugin is using plaintext (the default for loopback). | Add `"grpcEndpointTlsMode": "tls"` to the plugin config. |
|
|
109
|
+
| `x509: certificate signed by unknown authority` | The daemon uses a self-signed certificate or a certificate from a private CA not trusted by the system. | Set `grpcEndpointTlsCa` to the path of the CA certificate PEM file that signed the daemon's server certificate. |
|
|
110
|
+
| `failed to load TLS CA certificate from "...": ENOENT: no such file or directory` | The file path given in `grpcEndpointTlsCa` does not exist on the machine. | Verify the file path is correct and the CA certificate file exists. |
|
|
111
|
+
| `LibraVDB: invalid grpcEndpointTlsMode "..."` | The value set in `grpcEndpointTlsMode` is not one of the accepted values. | Change the value to `"auto"`, `"tls"`, or `"insecure"`. |
|
|
112
|
+
| `LIBRAVDB: grpcEndpointTlsCa is set but grpcEndpointTlsMode is "insecure"` (warning) | Both `grpcEndpointTlsCa` and `grpcEndpointTlsMode: "insecure"` are set. The CA file will not be used. | Remove `grpcEndpointTlsCa` if plaintext is intended, or change `grpcEndpointTlsMode` to `"auto"` or `"tls"` to use the CA file. |
|
package/docs/configuration.md
CHANGED
|
@@ -12,10 +12,60 @@ CPU when a provider is unavailable.
|
|
|
12
12
|
| Key | Type | Default | Notes |
|
|
13
13
|
|---|---|---|---|
|
|
14
14
|
| `sidecarPath` | string | `auto` | `"auto"` probes standard socket paths; set `unix:/path` or `tcp:host:port` to override |
|
|
15
|
-
| `grpcEndpoint` | string | — |
|
|
15
|
+
| `grpcEndpoint` | string | — | gRPC kernel endpoint. See `grpcEndpointTlsMode` for credential control. |
|
|
16
|
+
| `grpcEndpointTlsCa` | string | — | Path to CA certificate PEM file. Only needed for self-signed or private CA certs. Omit when using Let's Encrypt or cert-manager. |
|
|
17
|
+
| `grpcEndpointTlsMode` | string | `"auto"` | gRPC credential mode. `"auto"`: loopback/unix → plaintext, remote → TLS. `"tls"`: always TLS. `"insecure"`: always plaintext. |
|
|
16
18
|
| `rpcTimeoutMs` | number | `30000` | Per-call timeout for service RPC (ms) |
|
|
17
19
|
| `dbPath` | string | auto-named | Explicit DB path; when set bypasses model-specific naming |
|
|
18
20
|
|
|
21
|
+
### gRPC TLS behavior
|
|
22
|
+
|
|
23
|
+
The plugin selects credentials automatically based on the endpoint:
|
|
24
|
+
|
|
25
|
+
| Endpoint format | Credential mode |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `unix:/path/to/sock` | Plaintext (local) |
|
|
28
|
+
| `tcp:127.0.0.1:port` / `tcp:localhost:port` / `[::1]:port` | Plaintext (loopback) |
|
|
29
|
+
| Any other TCP or DNS target | TLS |
|
|
30
|
+
|
|
31
|
+
Use `grpcEndpointTlsMode` to override the default behavior:
|
|
32
|
+
|
|
33
|
+
| Value | When to use |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `"auto"` (default) | Standard operation — plugin heuristic matches daemon TLS setting automatically. |
|
|
36
|
+
| `"tls"` | Daemon has TLS enabled on loopback or unix socket (rare; use when the daemon's `LIBRAVDB_GRPC_TLS_*` env vars are set on a local address). |
|
|
37
|
+
| `"insecure"` | Service mesh or TLS-terminating tunnel handles encryption externally; both sides are plaintext. |
|
|
38
|
+
|
|
39
|
+
**Default (local daemon):** No TLS configuration needed.
|
|
40
|
+
Unix socket and loopback endpoints are always plaintext regardless
|
|
41
|
+
of any TLS settings.
|
|
42
|
+
|
|
43
|
+
**K8 / remote daemon with CA-issued cert:**
|
|
44
|
+
No extra configuration needed. The plugin uses the system CA pool,
|
|
45
|
+
which trusts certs issued by Let's Encrypt, cert-manager, and
|
|
46
|
+
other public CAs automatically.
|
|
47
|
+
|
|
48
|
+
**Remote daemon with self-signed or private CA cert:**
|
|
49
|
+
Set `grpcEndpointTlsCa` to the path of the CA certificate PEM file:
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"grpcEndpoint": "tcp:yourdaemon.internal:50051",
|
|
53
|
+
"grpcEndpointTlsCa": "/etc/certs/ca.pem"
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
The daemon must be configured with matching TLS cert and key via
|
|
57
|
+
`LIBRAVDB_GRPC_TLS_CERT` and `LIBRAVDB_GRPC_TLS_KEY`.
|
|
58
|
+
|
|
59
|
+
**Local daemon with TLS enabled:**
|
|
60
|
+
If the daemon has `LIBRAVDB_GRPC_TLS_CERT`/`LIBRAVDB_GRPC_TLS_KEY` set on a loopback
|
|
61
|
+
address, explicitly set `grpcEndpointTlsMode: "tls"` to match:
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"grpcEndpoint": "tcp:127.0.0.1:9090",
|
|
65
|
+
"grpcEndpointTlsMode": "tls"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
19
69
|
## Embedding
|
|
20
70
|
|
|
21
71
|
| Key | Type | Default | Notes |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# mTLS Configuration
|
|
2
|
+
|
|
3
|
+
The plugin supports mutual TLS (mTLS) for gRPC connections to the Vector Service. In mTLS, both the client and the server present X.509 certificates, and each side verifies the other's certificate against a trusted CA. When the Vector Service is configured with a client CA, it will reject any client that does not present a valid certificate signed by that CA.
|
|
4
|
+
|
|
5
|
+
## When mTLS is required
|
|
6
|
+
|
|
7
|
+
The Vector Service operator enables mTLS by configuring a client CA on the service side. When this is enabled, the Vector Service requires every connecting client to present a certificate signed by that CA. If the plugin is configured to use TLS but does not present a client certificate, the TLS handshake will fail and the connection will be rejected.
|
|
8
|
+
|
|
9
|
+
The plugin cannot detect whether the daemon requires mTLS — it must be configured explicitly. If connections fail with a "client did not provide a certificate" error, the plugin likely needs a client certificate configured.
|
|
10
|
+
|
|
11
|
+
## Configuration fields
|
|
12
|
+
|
|
13
|
+
| Field | Type | When to use |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `grpcEndpointTlsClientCert` | string (file path) | Path to a PEM-encoded X.509 client certificate. Required when the Vector Service requires a client certificate. |
|
|
16
|
+
| `grpcEndpointTlsClientKey` | string (file path) | Path to the PEM-encoded private key that corresponds to the certificate. Required when `grpcEndpointTlsClientCert` is set. |
|
|
17
|
+
|
|
18
|
+
Both fields must be set together or not at all. Setting one without the other will cause a configuration error at startup.
|
|
19
|
+
|
|
20
|
+
These fields only take effect when the connection uses TLS — that is, when `grpcEndpointTlsMode` is not `"insecure"` and the endpoint is not a loopback address or Unix socket. See [TLS configuration](./TLS_configuration.md) for the full TLS behavior reference.
|
|
21
|
+
|
|
22
|
+
## Certificate requirements
|
|
23
|
+
|
|
24
|
+
- **Format**: PEM-encoded X.509 certificate (-----BEGIN CERTIFICATE-----)
|
|
25
|
+
- **Chain order**: Leaf certificate must be first in the file; intermediate certificates may follow in the same file
|
|
26
|
+
- **Signing**: The certificate must be signed by the CA that the Vector Service operator configured as the client CA
|
|
27
|
+
- **Key types accepted**:
|
|
28
|
+
- RSA (any key size)
|
|
29
|
+
- ECDSA (P-256, P-384, P-521)
|
|
30
|
+
- Ed25519
|
|
31
|
+
- **Key file format**: PEM-encoded private key (-----BEGIN PRIVATE KEY----- or -----BEGIN RSA PRIVATE KEY----- etc.)
|
|
32
|
+
- **Key/cert match**: The private key must correspond to the leaf certificate's public key
|
|
33
|
+
- **Revocation**: The Vector Service does not perform revocation checking. Expired certificates are rejected (`NotAfter` boundary). Revoked but unexpired certificates are not detected. Keep certificate lifetimes short.
|
|
34
|
+
|
|
35
|
+
## Example configuration
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"grpcEndpoint": "tcp:libravdb.internal:9090",
|
|
40
|
+
"grpcEndpointTlsCa": "/etc/certs/company-ca.pem",
|
|
41
|
+
"grpcEndpointTlsClientCert": "/etc/certs/plugin-client.crt",
|
|
42
|
+
"grpcEndpointTlsClientKey": "/etc/certs/plugin-client.key"
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This example shows all four gRPC TLS fields configured together for a remote Vector Service that uses a private CA and requires mTLS.
|
|
47
|
+
|
|
48
|
+
## Generating a client certificate (quick reference)
|
|
49
|
+
|
|
50
|
+
The following example shows OpenSSL commands to generate a client key, create a CSR, and sign it with a private CA. This is for illustration only — operators may use cert-manager, Vault, step, or other PKI tooling instead.
|
|
51
|
+
|
|
52
|
+
**1. Create a client private key:**
|
|
53
|
+
```bash
|
|
54
|
+
openssl genpkey -algorithm ED25519 -out client.key
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**2. Create a certificate signing request (CSR):**
|
|
58
|
+
```bash
|
|
59
|
+
openssl req -new -key client.key -out client.csr -subj "/CN=openclaw-plugin/O=LibraVDB"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**3. Sign the CSR with the private CA:**
|
|
63
|
+
```bash
|
|
64
|
+
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
|
|
65
|
+
-CAcreateserial -out client.crt -days 365
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Replace `ca.crt` and `ca.key` with the CA certificate and key that the daemon operator provided.
|
|
69
|
+
|
|
70
|
+
## Error reference
|
|
71
|
+
|
|
72
|
+
| Error | Likely cause | Fix |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `tls: client did not provide a certificate` | mTLS is required by the Vector Service, but no client certificate is configured in the plugin | Set `grpcEndpointTlsClientCert` and `grpcEndpointTlsClientKey` in the plugin config |
|
|
75
|
+
| `tls: failed to verify client certificate: x509: certificate signed by unknown authority` | The client certificate is not signed by the Vector Service's client CA | Obtain a certificate signed by the CA the Vector Service operator configured as the client CA |
|
|
76
|
+
| `LibraVDB: failed to load TLS client certificate from "...": ENOENT: no such file or directory` | The certificate file path does not exist or is not readable | Verify the path set in `grpcEndpointTlsClientCert` exists and has correct permissions |
|
|
77
|
+
| `LibraVDB: failed to load TLS client key from "...": ENOENT: no such file or directory` | The key file path does not exist or is not readable | Verify the path set in `grpcEndpointTlsClientKey` exists and has correct permissions |
|
|
78
|
+
| `LibraVDB: grpcEndpointTlsClientCert and grpcEndpointTlsClientKey must both be set or both be omitted` | Only one of the two fields is set | Set both fields or remove both from the configuration |
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "libravdb-memory",
|
|
3
3
|
"name": "LibraVDB Memory",
|
|
4
4
|
"description": "Persistent vector memory with three-tier hybrid scoring",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.5.1",
|
|
6
6
|
"kind": [
|
|
7
7
|
"memory",
|
|
8
8
|
"context-engine"
|
|
@@ -46,6 +46,27 @@
|
|
|
46
46
|
"type": "string",
|
|
47
47
|
"description": "Optional gRPC kernel endpoint for hosts using the daemon kernel transport."
|
|
48
48
|
},
|
|
49
|
+
"grpcEndpointTlsCa": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "Path to a CA certificate PEM file for verifying the daemon's TLS certificate. Only needed for self-signed or private CA certificates. Not required when using a publicly trusted certificate (Let's Encrypt, cert-manager, etc)."
|
|
52
|
+
},
|
|
53
|
+
"grpcEndpointTlsClientCert": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "Path to a PEM-encoded X.509 client certificate for mTLS. Required when the daemon requires a client certificate. Must be paired with grpcEndpointTlsClientKey."
|
|
56
|
+
},
|
|
57
|
+
"grpcEndpointTlsClientKey": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "Path to the client private key PEM file. Required when grpcEndpointTlsClientCert is set. Accepts RSA, ECDSA (P-256/P-384/P-521), Ed25519 keys."
|
|
60
|
+
},
|
|
61
|
+
"grpcEndpointTlsMode": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"enum": [
|
|
64
|
+
"auto",
|
|
65
|
+
"tls",
|
|
66
|
+
"insecure"
|
|
67
|
+
],
|
|
68
|
+
"description": "gRPC credential mode. auto (default): loopback and unix socket use plaintext, remote addresses use TLS. tls: always use TLS regardless of address. insecure: always use plaintext (use with service mesh or tunnel that handles TLS externally)."
|
|
69
|
+
},
|
|
49
70
|
"authoredHardBudgetFraction": {
|
|
50
71
|
"type": "number",
|
|
51
72
|
"minimum": 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xdarkicex/openclaw-memory-libravdb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"pluginSdkVersion": "2026.4.11"
|
|
44
44
|
},
|
|
45
45
|
"install": {
|
|
46
|
-
"minHostVersion": ">=2026.
|
|
46
|
+
"minHostVersion": ">=2026.4.11"
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|