@xdarkicex/openclaw-memory-libravdb 1.5.0 → 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 +3 -1
- package/dist/grpc-client.js +31 -6
- package/dist/index.js +70 -20
- package/dist/plugin-runtime.js +28 -12
- package/dist/types.d.ts +11 -0
- package/docs/mTLS_configuration.md +78 -0
- package/openclaw.plugin.json +9 -1
- package/package.json +1 -1
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), [TLS configuration](./docs/TLS_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
|
@@ -5,6 +5,8 @@ export interface GrpcClientOptions {
|
|
|
5
5
|
timeoutMs?: number;
|
|
6
6
|
tlsCaPath?: string;
|
|
7
7
|
tlsMode?: "auto" | "tls" | "insecure";
|
|
8
|
+
tlsClientCertPath?: string;
|
|
9
|
+
tlsClientKeyPath?: string;
|
|
8
10
|
}
|
|
9
11
|
export declare function resolveGrpcTarget(endpoint: string): string;
|
|
10
12
|
/**
|
|
@@ -21,7 +23,7 @@ export declare function resolveGrpcTarget(endpoint: string): string;
|
|
|
21
23
|
* (Let's Encrypt, cert-manager) — the system CA pool is used.
|
|
22
24
|
*/
|
|
23
25
|
export declare function resolveGrpcCredentialMode(endpoint: string, tlsMode?: "auto" | "tls" | "insecure"): "insecure" | "tls";
|
|
24
|
-
export declare function resolveGrpcCredentials(endpoint: string, tlsCaPath?: string, tlsMode?: "auto" | "tls" | "insecure"): grpc.ChannelCredentials;
|
|
26
|
+
export declare function resolveGrpcCredentials(endpoint: string, tlsCaPath?: string, tlsMode?: "auto" | "tls" | "insecure", tlsClientCertPath?: string, tlsClientKeyPath?: string): grpc.ChannelCredentials;
|
|
25
27
|
export declare class GrpcKernelClient {
|
|
26
28
|
private client;
|
|
27
29
|
private readonly secret;
|
package/dist/grpc-client.js
CHANGED
|
@@ -1,7 +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
|
|
4
|
+
import fs from "node:fs";
|
|
5
5
|
import * as grpc from "@grpc/grpc-js";
|
|
6
6
|
import * as protoLoader from "@grpc/proto-loader";
|
|
7
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -36,12 +36,13 @@ export function resolveGrpcCredentialMode(endpoint, tlsMode) {
|
|
|
36
36
|
const host = extractGrpcHost(target);
|
|
37
37
|
return isLoopbackHost(host) ? "insecure" : "tls";
|
|
38
38
|
}
|
|
39
|
-
export function resolveGrpcCredentials(endpoint, tlsCaPath, tlsMode) {
|
|
39
|
+
export function resolveGrpcCredentials(endpoint, tlsCaPath, tlsMode, tlsClientCertPath, tlsClientKeyPath) {
|
|
40
40
|
if (resolveGrpcCredentialMode(endpoint, tlsMode) === "insecure") {
|
|
41
41
|
return grpc.credentials.createInsecure();
|
|
42
42
|
}
|
|
43
|
+
// tlsMode is "tls" or "auto"/undefined resolved to "tls"
|
|
44
|
+
let rootCerts = null;
|
|
43
45
|
if (tlsCaPath) {
|
|
44
|
-
let rootCerts;
|
|
45
46
|
try {
|
|
46
47
|
rootCerts = fs.readFileSync(tlsCaPath);
|
|
47
48
|
}
|
|
@@ -49,9 +50,33 @@ export function resolveGrpcCredentials(endpoint, tlsCaPath, tlsMode) {
|
|
|
49
50
|
const msg = err instanceof Error ? err.message : String(err);
|
|
50
51
|
throw new Error(`LibraVDB: failed to load TLS CA certificate from "${tlsCaPath}": ${msg}`);
|
|
51
52
|
}
|
|
52
|
-
return grpc.credentials.createSsl(rootCerts, null, null);
|
|
53
53
|
}
|
|
54
|
-
|
|
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);
|
|
55
80
|
}
|
|
56
81
|
function extractGrpcHost(target) {
|
|
57
82
|
const withoutDnsPrefix = target.startsWith("dns:///") ? target.slice("dns:///".length) : target;
|
|
@@ -84,7 +109,7 @@ export class GrpcKernelClient {
|
|
|
84
109
|
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
|
85
110
|
const kernelService = protoDescriptor.intelligence_kernel.v1.IntelligenceKernel;
|
|
86
111
|
const target = resolveGrpcTarget(options.endpoint);
|
|
87
|
-
this.client = new kernelService(target, resolveGrpcCredentials(options.endpoint, options.tlsCaPath, options.tlsMode));
|
|
112
|
+
this.client = new kernelService(target, resolveGrpcCredentials(options.endpoint, options.tlsCaPath, options.tlsMode, options.tlsClientCertPath, options.tlsClientKeyPath));
|
|
88
113
|
}
|
|
89
114
|
getMetadata(signed = true) {
|
|
90
115
|
const md = new grpc.Metadata();
|
package/dist/index.js
CHANGED
|
@@ -39661,7 +39661,7 @@ 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
|
|
39664
|
+
import fs3 from "node:fs";
|
|
39665
39665
|
var __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
|
|
39666
39666
|
var PROTO_PATH = path3.resolve(__dirname2, "./proto/intelligence_kernel/v1/kernel.proto");
|
|
39667
39667
|
function resolveGrpcTarget(endpoint) {
|
|
@@ -39675,12 +39675,12 @@ function resolveGrpcCredentialMode(endpoint, tlsMode) {
|
|
|
39675
39675
|
const host = extractGrpcHost(target);
|
|
39676
39676
|
return isLoopbackHost(host) ? "insecure" : "tls";
|
|
39677
39677
|
}
|
|
39678
|
-
function resolveGrpcCredentials(endpoint, tlsCaPath, tlsMode) {
|
|
39678
|
+
function resolveGrpcCredentials(endpoint, tlsCaPath, tlsMode, tlsClientCertPath, tlsClientKeyPath) {
|
|
39679
39679
|
if (resolveGrpcCredentialMode(endpoint, tlsMode) === "insecure") {
|
|
39680
39680
|
return grpc.credentials.createInsecure();
|
|
39681
39681
|
}
|
|
39682
|
+
let rootCerts = null;
|
|
39682
39683
|
if (tlsCaPath) {
|
|
39683
|
-
let rootCerts;
|
|
39684
39684
|
try {
|
|
39685
39685
|
rootCerts = fs3.readFileSync(tlsCaPath);
|
|
39686
39686
|
} catch (err) {
|
|
@@ -39689,9 +39689,35 @@ function resolveGrpcCredentials(endpoint, tlsCaPath, tlsMode) {
|
|
|
39689
39689
|
`LibraVDB: failed to load TLS CA certificate from "${tlsCaPath}": ${msg}`
|
|
39690
39690
|
);
|
|
39691
39691
|
}
|
|
39692
|
-
return grpc.credentials.createSsl(rootCerts, null, null);
|
|
39693
39692
|
}
|
|
39694
|
-
|
|
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);
|
|
39695
39721
|
}
|
|
39696
39722
|
function extractGrpcHost(target) {
|
|
39697
39723
|
const withoutDnsPrefix = target.startsWith("dns:///") ? target.slice("dns:///".length) : target;
|
|
@@ -39724,7 +39750,13 @@ var GrpcKernelClient = class {
|
|
|
39724
39750
|
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
|
39725
39751
|
const kernelService = protoDescriptor.intelligence_kernel.v1.IntelligenceKernel;
|
|
39726
39752
|
const target = resolveGrpcTarget(options.endpoint);
|
|
39727
|
-
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
|
+
));
|
|
39728
39760
|
}
|
|
39729
39761
|
getMetadata(signed = true) {
|
|
39730
39762
|
const md = new grpc.Metadata();
|
|
@@ -40191,7 +40223,7 @@ function sleep2(delayMs) {
|
|
|
40191
40223
|
}
|
|
40192
40224
|
|
|
40193
40225
|
// src/plugin-runtime.ts
|
|
40194
|
-
import { readFileSync as
|
|
40226
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
40195
40227
|
var DEFAULT_RPC_TIMEOUT_MS = 3e4;
|
|
40196
40228
|
var STARTUP_HEALTH_TIMEOUT_MS = 2e3;
|
|
40197
40229
|
var VALID_TLS_MODES = ["auto", "tls", "insecure"];
|
|
@@ -40210,6 +40242,32 @@ function createPluginRuntime(cfg, logger = console) {
|
|
|
40210
40242
|
}
|
|
40211
40243
|
if (!started) {
|
|
40212
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
|
+
}
|
|
40213
40271
|
const sidecar = await startSidecar(cfg, logger);
|
|
40214
40272
|
const rpc = new RpcClient(sidecar.socket, {
|
|
40215
40273
|
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS
|
|
@@ -40226,24 +40284,16 @@ function createPluginRuntime(cfg, logger = console) {
|
|
|
40226
40284
|
}
|
|
40227
40285
|
let kernel = null;
|
|
40228
40286
|
if (cfg.grpcEndpoint) {
|
|
40287
|
+
const secret = loadSecretFromEnv();
|
|
40229
40288
|
try {
|
|
40230
|
-
const secret = loadSecretFromEnv();
|
|
40231
|
-
if (cfg.grpcEndpointTlsMode !== void 0 && !isTlsModeValid(cfg.grpcEndpointTlsMode)) {
|
|
40232
|
-
throw new Error(
|
|
40233
|
-
`LibraVDB: invalid grpcEndpointTlsMode "${cfg.grpcEndpointTlsMode}" \u2014 must be "auto", "tls", or "insecure"`
|
|
40234
|
-
);
|
|
40235
|
-
}
|
|
40236
|
-
if (cfg.grpcEndpointTlsMode === "insecure" && cfg.grpcEndpointTlsCa) {
|
|
40237
|
-
logger.warn?.(
|
|
40238
|
-
`LibraVDB: grpcEndpointTlsCa is set but grpcEndpointTlsMode is "insecure" \u2014 the CA file will not be used`
|
|
40239
|
-
);
|
|
40240
|
-
}
|
|
40241
40289
|
kernel = new GrpcKernelClient({
|
|
40242
40290
|
endpoint: cfg.grpcEndpoint,
|
|
40243
40291
|
secret,
|
|
40244
40292
|
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS,
|
|
40245
40293
|
tlsCaPath: cfg.grpcEndpointTlsCa,
|
|
40246
|
-
tlsMode: cfg.grpcEndpointTlsMode
|
|
40294
|
+
tlsMode: cfg.grpcEndpointTlsMode,
|
|
40295
|
+
tlsClientCertPath: cfg.grpcEndpointTlsClientCert,
|
|
40296
|
+
tlsClientKeyPath: cfg.grpcEndpointTlsClientKey
|
|
40247
40297
|
});
|
|
40248
40298
|
} catch (error) {
|
|
40249
40299
|
logger.warn?.(`LibraVDB: failed to initialize gRPC kernel client: ${formatError(error)}`);
|
|
@@ -40312,7 +40362,7 @@ function loadSecretFromEnv() {
|
|
|
40312
40362
|
const path5 = process.env.LIBRAVDB_AUTH_SECRET_FILE;
|
|
40313
40363
|
if (path5) {
|
|
40314
40364
|
try {
|
|
40315
|
-
return
|
|
40365
|
+
return readFileSync2(path5, "utf8").trim();
|
|
40316
40366
|
} catch {
|
|
40317
40367
|
return void 0;
|
|
40318
40368
|
}
|
package/dist/plugin-runtime.js
CHANGED
|
@@ -21,6 +21,30 @@ export function createPluginRuntime(cfg, logger = console) {
|
|
|
21
21
|
}
|
|
22
22
|
if (!started) {
|
|
23
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
|
+
}
|
|
24
48
|
const sidecar = await startSidecar(cfg, logger);
|
|
25
49
|
const rpc = new RpcClient(sidecar.socket, {
|
|
26
50
|
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS,
|
|
@@ -39,28 +63,20 @@ export function createPluginRuntime(cfg, logger = console) {
|
|
|
39
63
|
}
|
|
40
64
|
let kernel = null;
|
|
41
65
|
if (cfg.grpcEndpoint) {
|
|
66
|
+
const secret = loadSecretFromEnv();
|
|
42
67
|
try {
|
|
43
|
-
const secret = loadSecretFromEnv();
|
|
44
|
-
if (cfg.grpcEndpointTlsMode !== undefined &&
|
|
45
|
-
!isTlsModeValid(cfg.grpcEndpointTlsMode)) {
|
|
46
|
-
throw new Error(`LibraVDB: invalid grpcEndpointTlsMode "${cfg.grpcEndpointTlsMode}" — ` +
|
|
47
|
-
`must be "auto", "tls", or "insecure"`);
|
|
48
|
-
}
|
|
49
|
-
if (cfg.grpcEndpointTlsMode === "insecure" &&
|
|
50
|
-
cfg.grpcEndpointTlsCa) {
|
|
51
|
-
// logger is provided by the host and may not have all methods
|
|
52
|
-
logger.warn?.(`LibraVDB: grpcEndpointTlsCa is set but grpcEndpointTlsMode ` +
|
|
53
|
-
`is "insecure" — the CA file will not be used`);
|
|
54
|
-
}
|
|
55
68
|
kernel = new GrpcKernelClient({
|
|
56
69
|
endpoint: cfg.grpcEndpoint,
|
|
57
70
|
secret,
|
|
58
71
|
timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS,
|
|
59
72
|
tlsCaPath: cfg.grpcEndpointTlsCa,
|
|
60
73
|
tlsMode: cfg.grpcEndpointTlsMode,
|
|
74
|
+
tlsClientCertPath: cfg.grpcEndpointTlsClientCert,
|
|
75
|
+
tlsClientKeyPath: cfg.grpcEndpointTlsClientKey,
|
|
61
76
|
});
|
|
62
77
|
}
|
|
63
78
|
catch (error) {
|
|
79
|
+
// Only gRPC init errors land here — config validation already threw above
|
|
64
80
|
logger.warn?.(`LibraVDB: failed to initialize gRPC kernel client: ${formatError(error)}`);
|
|
65
81
|
}
|
|
66
82
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -91,6 +91,17 @@ export interface PluginConfig {
|
|
|
91
91
|
logLevel?: "debug" | "info" | "warn" | "error";
|
|
92
92
|
grpcEndpoint?: string;
|
|
93
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;
|
|
94
105
|
/** Controls gRPC credential mode.
|
|
95
106
|
* "auto" (default) — loopback and unix → plaintext, remote → TLS.
|
|
96
107
|
* "tls" — always use TLS regardless of address.
|
|
@@ -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.
|
|
5
|
+
"version": "1.5.1",
|
|
6
6
|
"kind": [
|
|
7
7
|
"memory",
|
|
8
8
|
"context-engine"
|
|
@@ -50,6 +50,14 @@
|
|
|
50
50
|
"type": "string",
|
|
51
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
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
|
+
},
|
|
53
61
|
"grpcEndpointTlsMode": {
|
|
54
62
|
"type": "string",
|
|
55
63
|
"enum": [
|