@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 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)
@@ -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;
@@ -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 * as fs from "fs";
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
- return grpc.credentials.createSsl();
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 * as fs3 from "fs";
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
- return grpc.credentials.createSsl();
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(options.endpoint, options.tlsCaPath, options.tlsMode));
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 readFileSync3 } from "node:fs";
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 readFileSync3(path5, "utf8").trim();
40365
+ return readFileSync2(path5, "utf8").trim();
40316
40366
  } catch {
40317
40367
  return void 0;
40318
40368
  }
@@ -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 |
@@ -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.0",
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": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdarkicex/openclaw-memory-libravdb",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",