# Gateway control

> memory-tencentdb-ctl start/stop/status/health/logs and config subcommands in standalone vs hermes modes, path env overrides, and LLM credential injection.

- Repository: TencentCloud/TencentDB-Agent-Memory
- GitHub: https://github.com/TencentCloud/TencentDB-Agent-Memory
- Human docs: https://grok-wiki.com/public/docs/tencentcloud-tencentdb-agent-memory-14fefdd76c97
- Complete Markdown: https://grok-wiki.com/public/docs/tencentcloud-tencentdb-agent-memory-14fefdd76c97/llms-full.txt

## Source Files

- `scripts/memory-tencentdb-ctl.sh`
- `scripts/README.memory-tencentdb-ctl.md`
- `scripts/install_hermes_memory_tencentdb.sh`
- `src/gateway/server.ts`
- `src/gateway/config.ts`

---

---
title: "Gateway control"
description: "memory-tencentdb-ctl start/stop/status/health/logs and config subcommands in standalone vs hermes modes, path env overrides, and LLM credential injection."
---

`scripts/memory-tencentdb-ctl.sh` is the operations entry for the standalone TDAI Gateway HTTP sidecar (`src/gateway/server.ts`). It starts and stops the process, probes `GET /health`, tails logs, and merges LLM / embedding / Tencent VectorDB settings into `$TDAI_DATA_DIR/tdai-gateway.json` (mode `0600`, atomic write). Default mode is **standalone** (no writes under `~/.hermes`). Pass `--hermes` or set `MEMORY_TENCENTDB_MODE=hermes` to also write Hermes env fragments and expose `enable-hermes-memory`.

The script ships with the npm package under `scripts/` but is **not** registered in `package.json` `bin`. Call it by absolute path or symlink it into `PATH` yourself.

## Modes

| Mode | Activate | Lifecycle + `tdai-gateway.json` | Extra side effects |
|---|---|---|---|
| `standalone` (default) | No flag, or `--standalone` | Yes | Logs under `$TDAI_DATA_DIR/logs/`; does **not** touch `$HERMES_HOME` |
| `hermes` | `--hermes` or `MEMORY_TENCENTDB_MODE=hermes` | Yes | Logs under `$HERMES_HOME/logs/memory_tencentdb/`; `config llm` also writes `$HERMES_HOME/env.d/memory-tencentdb-llm.sh`; `enable-hermes-memory` allowed |

Hermes mode exists because Hermes supervisors spawn Gateway with `os.environ.copy()`. That child does not inherit a shell that already loaded `tdai-gateway.json` credentials, so LLM keys must also land in `$HERMES_HOME/env.d/*.sh` for Hermes to `source` before spawn. Embedding and VDB are **never** written to env files—only the JSON config.

```mermaid
flowchart TB
  subgraph ctl ["memory-tencentdb-ctl"]
    life["start / stop / restart / status / health / logs"]
    cfg["config llm | embedding | vdb | vdb-off | show"]
    enhm["enable-hermes-memory"]
  end

  subgraph disk ["On disk"]
    json["$TDAI_DATA_DIR/tdai-gateway.json"]
    logs_s["standalone: $TDAI_DATA_DIR/logs/"]
    logs_h["hermes: $HERMES_HOME/logs/memory_tencentdb/"]
    envd["hermes only: $HERMES_HOME/env.d/memory-tencentdb-llm.sh"]
    hcfg["hermes only: $HERMES_HOME/config.yaml"]
  end

  subgraph process ["Gateway process"]
    srv["src/gateway/server.ts → TdaiGateway"]
    load["loadGatewayConfig()"]
  end

  life --> logs_s
  life --> logs_h
  life --> srv
  cfg --> json
  cfg -->|MODE=hermes and section=llm| envd
  enhm --> hcfg
  json --> load
  envd -->|TDAI_LLM_* env override| load
  load --> srv
```

## Paths and environment overrides

Canonical root (0.4.x+): `$MEMORY_TENCENTDB_ROOT` defaults to `~/.memory-tencentdb`.

| Variable | Default | Role |
|---|---|---|
| `MEMORY_TENCENTDB_ROOT` | `~/.memory-tencentdb` | Unified root for install + data |
| `TDAI_INSTALL_DIR` | `$MEMORY_TENCENTDB_ROOT/tdai-memory-openclaw-plugin` | Plugin tree + `src/gateway/server.ts` + `node_modules` |
| `TDAI_DATA_DIR` | `$MEMORY_TENCENTDB_ROOT/memory-tdai` | Data + `tdai-gateway.json` |
| `HERMES_HOME` | `~/.hermes` | Hermes home (hermes mode only for writes) |
| `MEMORY_TENCENTDB_LOG_DIR` | mode-dependent (see below) | Override log/PID directory |
| `MEMORY_TENCENTDB_MODE` | `standalone` | Mode without CLI flags |
| `MEMORY_TENCENTDB_GATEWAY_HOST` | `127.0.0.1` | Host used by **ctl** for port listen / health |
| `MEMORY_TENCENTDB_GATEWAY_PORT` | `8420` | Port used by **ctl** for listen / health / stop |
| `MEMORY_TENCENTDB_GATEWAY_CMD` | (empty → fallback) | Full shell command to spawn Gateway |

Log directory after `_apply_mode_paths`:

- **standalone:** `$MEMORY_TENCENTDB_LOG_DIR` or `$TDAI_DATA_DIR/logs`
- **hermes:** `$MEMORY_TENCENTDB_LOG_DIR` or `$HERMES_HOME/logs/memory_tencentdb`

Files under the log dir: `gateway.pid`, `gateway.stdout.log`, `gateway.stderr.log`.

Legacy paths `~/tdai-memory-openclaw-plugin` and `~/memory-tdai` trigger WARN only; migration is owned by `install_hermes_memory_tencentdb.sh`, not the ctl.

### Gateway process bind vs ctl probe

Two env namespaces:

| Concern | Variables | Defaults |
|---|---|---|
| **ctl** status/health/stop by listening port | `MEMORY_TENCENTDB_GATEWAY_HOST` / `_PORT` | `127.0.0.1:8420` |
| **Node process** bind (`loadGatewayConfig`) | `TDAI_GATEWAY_HOST` / `TDAI_GATEWAY_PORT`, or `server.host` / `server.port` in the gateway config file | `127.0.0.1:8420` |

If you change only the ctl port variables, health checks track the new port while the process may still bind to 8420. Keep them aligned, or set `TDAI_GATEWAY_*` / `server.*` for the process and matching `MEMORY_TENCENTDB_GATEWAY_*` for the ctl.

### Config file resolution (Gateway process)

`loadGatewayConfig()` resolves config in order:

1. `TDAI_GATEWAY_CONFIG` (explicit path)
2. `./tdai-gateway.yaml` or `./tdai-gateway.json` in CWD
3. `<dataDir>/tdai-gateway.yaml` or `<dataDir>/tdai-gateway.json` (data dir from `TDAI_DATA_DIR` / defaults)
4. Environment-only defaults (no file)

Environment overrides beat file fields for LLM (`TDAI_LLM_BASE_URL`, `TDAI_LLM_API_KEY`, `TDAI_LLM_MODEL`, …) and server (`TDAI_GATEWAY_*`). Optional `TDAI_GATEWAY_API_KEY` enables Bearer auth on all routes except `GET /health`.

## Prerequisites

| Requirement | Notes |
|---|---|
| `bash` | Script shell |
| `python3` | Health check (`urllib`), JSON merge, hermes YAML edit |
| `node` ≥ 22 | Gateway runtime |
| `npx` | Fallback start command when `MEMORY_TENCENTDB_GATEWAY_CMD` unset |
| `lsof` or `ss` | Detect listening PID on Gateway port |
| `setsid` (optional) | Detach process group on start; falls back to `nohup` only |

Install first with `scripts/install_hermes_memory_tencentdb.sh` so `$TDAI_INSTALL_DIR` has sources and `node_modules`. The installer writes:

- `/etc/profile.d/memory-tencentdb-env.sh` — `MEMORY_TENCENTDB_GATEWAY_CMD`, host, port (interactive shells)
- `$HERMES_HOME/.env` — same keys for systemd-style Hermes that never sources `/etc/profile.d`

Install-time `GATEWAY_CMD` prefers absolute `node` + `--import tsx/esm` (PATH-independent). Ctl fallback (when `MEMORY_TENCENTDB_GATEWAY_CMD` is empty) is:

```bash
sh -c 'cd $TDAI_INSTALL_DIR && exec npx tsx src/gateway/server.ts'
```

## Invoke the script

Not a package `bin`. Examples:

```bash
# From npm install (local)
"$(npm root)/@tencentdb-agent-memory/memory-tencentdb/scripts/memory-tencentdb-ctl.sh" --help

# Global npm install
"$(npm root -g)/@tencentdb-agent-memory/memory-tencentdb/scripts/memory-tencentdb-ctl.sh" --help

# Symlink for ops
sudo ln -sf "$SCRIPT" /usr/local/bin/memory-tencentdb-ctl
```

Global flags (any position before the subcommand in the flag-strip pass):

| Flag | Effect |
|---|---|
| `--hermes` | `MODE=hermes` |
| `--standalone` | `MODE=standalone` |
| `--dry-run` | Print planned writes / kills; no disk or process mutation |
| `-h` / `--help` | Usage |

## Lifecycle commands

Commands that spawn Gateway call `source_user_envs` first:

1. Always (if readable): `/etc/profile.d/memory-tencentdb-env.sh`
2. Hermes mode only: `/etc/profile.d/hermes-env.sh` and every `$HERMES_HOME/env.d/*.sh`

### `start`

1. Create log + data dirs (`ensure_paths`).
2. If something already listens on `$GATEWAY_PORT`, log WARN and return 0.
3. Resolve start command (`MEMORY_TENCENTDB_GATEWAY_CMD` or `npx tsx` fallback).
4. Spawn with `setsid nohup … &` (or `nohup` only), write shell wrapper PID to `gateway.pid`.
5. Poll up to ~15s for port listen + `GET /health`; WARN + exit 1 if still unhealthy.

### `stop`

1. Resolve PIDs via `lsof`/`ss` on `$GATEWAY_PORT`.
2. `SIGTERM`; after ~5s still listening → `SIGKILL`.
3. Remove `gateway.pid` (also tries leftover wrapper PID if no listener).

### `restart`

`stop` (ignore failure) → short sleep → `start`.

### `status`

Prints mode, host:port, install/data/log paths, config file existence, RUNNING/STOPPED, and health OK/UNHEALTHY. Hermes mode also prints `memory.provider` from `$HERMES_HOME/config.yaml` and lists `$HERMES_HOME/env.d/*.sh`.

### `health`

Python3 `GET http://$GATEWAY_HOST:$GATEWAY_PORT/health` (timeout 3s). Exit 0 + log healthy, or exit 1.

### `logs [out|err|all] [N]`

`tail -n N -f` on stdout, stderr, or both. Defaults: `all`, `N=200`.

```bash
memory-tencentdb-ctl start
memory-tencentdb-ctl status
memory-tencentdb-ctl health
memory-tencentdb-ctl logs err 500
memory-tencentdb-ctl stop
```

## Config subcommands

All merges target `$TDAI_DATA_DIR/tdai-gateway.json` with mode `0600` via temp file + rename.

JSON layout written by the ctl:

| Subcommand | JSON path |
|---|---|
| `config llm` | top-level `llm.{baseUrl,apiKey,model}` |
| `config embedding` | `memory.embedding.{…}` |
| `config vdb` | `memory.tcvdb.{…}` and usually `memory.storeBackend` |
| `config vdb-off` | `memory.storeBackend = "sqlite"` (± drop `memory.tcvdb`) |

Optional `--restart` on write commands runs `cmd_restart` after a successful write.

### `config llm`

Required: `--api-key`, `--base-url` (`http://` or `https://`), `--model`.

**Both modes:** merge into `tdai-gateway.json` `$.llm`.

**Hermes only:** also write `$HERMES_HOME/env.d/memory-tencentdb-llm.sh` (`0600`):

```bash
export TDAI_LLM_BASE_URL=...
export TDAI_LLM_API_KEY=...
export TDAI_LLM_MODEL=...
export MEMORY_TENCENTDB_LLM_BASE_URL="$TDAI_LLM_BASE_URL"
export MEMORY_TENCENTDB_LLM_API_KEY="$TDAI_LLM_API_KEY"
export MEMORY_TENCENTDB_LLM_MODEL="$TDAI_LLM_MODEL"
```

`TDAI_LLM_*` match Gateway `loadGatewayConfig` env overrides. `MEMORY_TENCENTDB_LLM_*` aliases are for the Hermes Python provider schema.

```bash
# standalone — JSON only
memory-tencentdb-ctl config llm \
  --api-key "sk-..." \
  --base-url "https://api.openai.com/v1" \
  --model "gpt-4o" \
  --restart

# hermes — JSON + env.d
memory-tencentdb-ctl --hermes config llm \
  --api-key "sk-..." \
  --base-url "https://api.openai.com/v1" \
  --model "gpt-4o" \
  --restart
```

### `config embedding`

| Flag | Required | Notes |
|---|---|---|
| `--provider` | yes | `none` disables vectors; else e.g. `openai`, `deepseek`, `qclaw`, … |
| `--api-key` | yes if not `none` | |
| `--base-url` | yes if not `none` | must be `http(s)://` |
| `--model` | yes if not `none` | |
| `--dimensions` | yes if not `none` | positive integer |
| `--proxy-url` | if `provider=qclaw` | |
| `--restart` | no | |

`provider=none` writes `{"provider":"none","enabled":false}` only. Non-`none` sets `enabled: true` plus credentials.

```bash
memory-tencentdb-ctl config embedding \
  --provider openai \
  --api-key "sk-..." \
  --base-url "https://api.openai.com/v1" \
  --model "text-embedding-3-small" \
  --dimensions 1536 \
  --restart

memory-tencentdb-ctl config embedding --provider none --restart
```

### `config vdb`

| Flag | Required | Notes |
|---|---|---|
| `--url` | yes | `http(s)://` |
| `--api-key` | yes | |
| `--database` | yes | |
| `--username` | no | default `root` |
| `--alias` | no | |
| `--ca-pem` | no | path must be readable; not copied |
| `--embedding-model` | no | |
| `--no-set-backend` | no | keep current `storeBackend` |
| `--restart` | no | |

Default also sets `memory.storeBackend` to `"tcvdb"`.

### `config vdb-off`

Sets `memory.storeBackend` to `"sqlite"`.

| Flag | Effect |
|---|---|
| (default) | Keep `memory.tcvdb` credentials |
| `--purge-creds` | Delete entire `memory.tcvdb` object |
| `--restart` | Restart Gateway |

Does **not** change Hermes `memory.provider`. If the JSON file is missing, writes a minimal `{"memory":{"storeBackend":"sqlite"}}`.

There is no zero-arg `vdb-on`: re-run full `config vdb` with required fields to switch back.

### `config show`

Prints `tdai-gateway.json` with `apiKey` / `password` / `token` redacted as `<redacted:N chars>`. Hermes mode also prints `memory-tencentdb-*.sh` env files with keys partially redacted.

## Hermes-only: `enable-hermes-memory`

Requires `MODE=hermes`. Idempotently sets `$HERMES_HOME/config.yaml` → `memory.provider: memory_tencentdb` without rewriting the whole file:

1. Prefer `ruamel.yaml` round-trip (comments/order/quotes preserved) if installed.
2. Else minimal in-place line edit of the existing `provider:` line (sibling indent copied).
3. If no `memory:` section, append a minimal block.

```bash
memory-tencentdb-ctl --hermes enable-hermes-memory
source "$HERMES_HOME/env.d/memory-tencentdb-llm.sh"
# then restart hermes per your host process manager
```

Standalone mode: command exits with code 1.

## Typical workflows

<Tabs>
  <Tab title="Standalone Gateway">
```bash
bash scripts/install_hermes_memory_tencentdb.sh

memory-tencentdb-ctl config llm \
  --api-key "sk-..." --base-url "https://api.openai.com/v1" --model gpt-4o
memory-tencentdb-ctl config embedding \
  --provider openai --api-key "sk-..." --base-url "https://api.openai.com/v1" \
  --model text-embedding-3-small --dimensions 1536
# optional remote store:
# memory-tencentdb-ctl config vdb --url "http://...:8100" --api-key "..." --database openclaw_memory

memory-tencentdb-ctl start
memory-tencentdb-ctl health   # expect JSON body with status ok
```
  </Tab>
  <Tab title="Hermes sidecar">
```bash
bash scripts/install_hermes_memory_tencentdb.sh
export MEMORY_TENCENTDB_MODE=hermes   # optional: skip --hermes each time

memory-tencentdb-ctl config llm \
  --api-key "sk-..." --base-url "https://api.openai.com/v1" --model gpt-4o
memory-tencentdb-ctl config embedding \
  --provider openai --api-key "sk-..." --base-url "https://api.openai.com/v1" \
  --model text-embedding-3-small --dimensions 1536

memory-tencentdb-ctl start            # manual fallback; Hermes supervisor may own this
memory-tencentdb-ctl enable-hermes-memory
source "$HERMES_HOME/env.d/memory-tencentdb-llm.sh"
# restart hermes agent/gateway per your install
```
  </Tab>
  <Tab title="SQLite fallback (keep Hermes provider)">
```bash
# Keep tcvdb creds, local store only
memory-tencentdb-ctl config vdb-off --restart

# Later: re-declare full vdb (required flags always validated)
memory-tencentdb-ctl config vdb \
  --url "http://...:8100" --api-key "..." --database openclaw_memory --restart

# Or purge creds permanently
memory-tencentdb-ctl config vdb-off --purge-creds --restart
```
  </Tab>
</Tabs>

## Exit codes

| Code | Meaning |
|---|---|
| `0` | Success |
| `1` | Bad args / validation (e.g. non-http base URL; hermes-only command in standalone) |
| `2` | Write failure (disk/permissions) — reserved for write helpers |
| `127` | Missing dependency (`python3` / `node` / `npx`) |

## Security and ops notes

- Sensitive files (`tdai-gateway.json`, `env.d/memory-tencentdb-llm.sh`) are `0600`. Env files contain plaintext API keys—do not commit them.
- Default Gateway bind is loopback. Without `TDAI_GATEWAY_API_KEY` / `server.apiKey`, routes other than `/health` are unauthenticated; the process logs a WARN at startup (louder if non-loopback).
- `--dry-run` previews config and kill actions without applying them.
- Foreground debug: `cd "$TDAI_INSTALL_DIR" && npx tsx src/gateway/server.ts` (or the installer’s absolute `node --import tsx/esm` command).
- Port conflict example that moves **ctl** probes: `MEMORY_TENCENTDB_GATEWAY_PORT=18420 memory-tencentdb-ctl restart` — also set `TDAI_GATEWAY_PORT=18420` (or `server.port`) so the Node process binds the same port.
- Optional systemd: `Type=forking` around `memory-tencentdb-ctl start` / `stop` (stateless HTTP sidecar; no special readiness protocol).

## Verification

| Check | Expected signal |
|---|---|
| `memory-tencentdb-ctl status` | `state: RUNNING`, paths match your env overrides |
| `memory-tencentdb-ctl health` | HTTP body from `/health`, exit 0 |
| `memory-tencentdb-ctl config show` | Redacted JSON; hermes mode also lists env.d |
| Hermes env inheritance | `env.d/memory-tencentdb-llm.sh` present after `config llm --hermes` |
| Hermes provider | `enable-hermes-memory` → `memory.provider = memory_tencentdb` in `status` |

## Related pages

<CardGroup>
  <Card title="Install on Hermes" href="/install-hermes">
    Deploy plugin tree, Gateway CMD env files, and Hermes provider packaging.
  </Card>
  <Card title="Gateway HTTP API" href="/gateway-http-api">
    Endpoint contracts for /health, /recall, /capture, search, session, and seed.
  </Card>
  <Card title="Host adapters" href="/host-adapters">
    StandaloneHostAdapter and TdaiCore boundary used by the Gateway process.
  </Card>
  <Card title="Data directories" href="/data-directories">
    MEMORY_TENCENTDB_ROOT / memory-tdai layout for logs, vectors, and persona.
  </Card>
  <Card title="Configure storage backends" href="/configure-storage">
    sqlite vs tcvdb fields and embedding provider quadruplets.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Gateway circuit breaker, silent failures, and recovery probes.
  </Card>
</CardGroup>
