# Neuron — Runtime & Deployment > Process/port topology, the end-to-end MCP request path, local vs GKE > blue/green production, and a high-level view of secrets/config. Grounded in > `entrypoint.sh`, `scripts/blue-green-deploy.sh`, the k8s manifests under > `infrastructure/platform/k8s/neuron-mcp/`, and `.gitea/workflows/`. ## Process & port topology A running neuron is **two processes in one container**: the soul and the engram, started by `entrypoint.sh`. ``` container (one pod) ┌──────────────────────────────────────────────────────────┐ │ entrypoint.sh │ │ 1. start engram (background) ── listens :8742 │ │ 2. wait /health up to 60s │ │ 3. exec soul (PID 1 foreground) ── listens :7770 │ │ │ │ soul :7770 ──HTTP──► engram :8742 │ │ (ENGRAM_URL=http://localhost:8742, HTTP mode) │ │ │ │ /data (PVC mount) ◄── engram snapshot.json │ └──────────────────────────────────────────────────────────┘ ``` - `entrypoint.sh` starts engram with `ENGRAM_BIND=:8742` and `ENGRAM_DATA_DIR=/data`, polls `http://localhost:8742/health` (up to 60s; Autopilot cold starts are slow), then `exec`s the soul. `SOUL_ENGRAM_PATH` is deliberately unset so `ENGRAM_URL` triggers **HTTP mode** (soul talks to engram over localhost HTTP, not an in-process embed). - EL HTTP runtime is tuned down for co-located calls: `EL_HTTP_TIMEOUT_MS=10000`, `EL_HTTP_CONNECT_TIMEOUT_MS=3000` (`entrypoint.sh`). ### Full port map | Port | Process | Role | Source | |---|---|---|---| | 7779 | mcp-proxy | MCP client entry; byte-forward + retry | `mcp-proxy/src/main.el` | | 17779 | mcp-wrapper | MCP JSON-RPC ⇄ soul REST; tool catalog | `mcp-wrapper/src/main.el` | | 7770 | soul | HTTP cognitive API + `handle_request` | `NEURON_PORT`, `deployment-blue.yaml` | | 8742 | engram | graph store HTTP | `entrypoint.sh`, `server.el:711` | | 7771 | neuron-connectd | MCP connector bridges | `routes.el` `connectd_*` | **Local vs prod, an important distinction.** The proxy → wrapper chain is the **local developer adapter**: a stdio MCP client (Claude Code) needs to reach an HTTP soul, so the proxy/wrapper translate and add resilience. In **production**, the `neuron-mcp` Kubernetes Service is a ClusterIP that targets the soul's `:7770` directly (`service.yaml`) — external access is "to be wired via Cloudflare Tunnel later" (annotation, same file). So in prod the MCP/HTTP boundary is the soul's own HTTP surface; the proxy/wrapper are not (yet) in the cluster path. *(inference from the ClusterIP-only Service + the local-only proxy/wrapper binaries.)* ## The MCP request path (end to end) A single `tools/call` from an MCP client, local topology: ``` client proxy :7779 wrapper :17779 soul :7770 engram :8742 │ JSON-RPC │ │ │ │ │ tools/call ─────────► │ forward+retry │ │ │ │ │ ─────────────────► │ map tool→REST │ │ │ │ │ ─── HTTP POST ────► │ handle_request │ │ │ │ /api/neuron/... │ → handle_api_* │ │ │ │ │ engram_* builtin │ │ │ │ │ ── (HTTP mode) ───► │ activate/search/ │ │ │ │ │ save │ │ │ │ ◄─── nodes/edges ── │ │ │ │ ◄── JSON result ── │ │ │ │ │ fire_activation │ │ │ │ │ /recall warm-up ─► soul (side effect) │ │ ◄──── result ──────── │ ◄───────────────── │ │ │ ``` Responsibilities per hop, and the volatility each isolates (VBD reading): 1. **proxy** — transport resilience. Isolates *client connection volatility* (drops, retries, health) from everything above. No MCP semantics. 2. **wrapper** — protocol translation. Isolates the *MCP protocol* from the soul: owns `initialize`/`tools/list`/`tools/call`, the ~90-tool catalog, and `dispatch_tool_call`. Also fires the `fire_activation` `/recall` side effect so tool use warms working memory. 3. **soul** — cognition. `handle_request` dispatch → `handle_api_*` → engram builtins. In HTTP mode it reaches engram over localhost; otherwise embedded. 4. **engram** — the graph. Spreading activation, Hebbian edges, snapshot persistence. For the user-facing chat pipeline (not tool calls), `/api/chat` enters `layered_cycle` (soul.el) — L1 safety → L2 stewardship → L3 imprint — described in `02-components.md §3c`. ## Production: GKE blue/green Neuron prod runs on GKE cluster **`neuron-platform`** (Autopilot, us-central1), namespace **`neuron-prod`**. Two Deployments, `neuron-mcp-blue` and `neuron-mcp-green`, share one Service selector that names the *active slot*. - **Deployments** (`deployment-blue.yaml` / `deployment-green.yaml`): one container `soul`, image pinned by **digest** (not `:latest`) so Argo CD can't drift the active slot to an untested build (see the pin comment in `deployment-blue.yaml`). `strategy: Recreate` — the PVC is RWO so only one pod can hold it at a time. Probes hit `/health` on `:7770`. - **Service** (`service.yaml`): ClusterIP `neuron-mcp`, port 7770 → 7770, `selector: {app: neuron-mcp, slot: blue}`. The blue/green script patches `slot`. - **Storage** (`pvc.yaml`): `neuron-engram-data`, `standard-rwo` (pd-balanced), 10Gi, RWO. Engram data is the single `snapshot.json` (~8MB active). - **The swap** (`scripts/blue-green-deploy.sh`): (1) set image on the target slot; (2) scale target to 1, wait for rollout; (3) **patch the Service selector to the new slot** (traffic flip); (4) scale the old slot to 0. Imperative `kubectl` for the live swap, then git-update the Argo manifests so a sync doesn't revert replica counts. - **Backup** (`backup-cronjob.yaml`): every 15 min, tar `/data` → GCS, keep 96. ### Resource sizing (learned the hard way) `deployment-blue.yaml` documents the memory history in comments: idle soul RSS ~860Mi; the `beginSession` call (loads memories + backlog + preferences) spikes past 1Gi and OOM-killed the pod mid-request (client socket closed). Current setting: `requests = limits = 2Gi`, cpu 250m/1000m. This is *why* the cognitive API projects/compacts payloads so aggressively (doc 02 §2, doc 01 axis 1) — the memory ceiling is real and close. ## CI/CD Two Gitea Actions workflows (`.gitea/workflows/`), serialized on a single GCE runner (`concurrency: neuron-runner`). - **`ci.yaml`** (push/PR to `main`): - **build:** free disk → checkout → install gcc/libcurl/gcloud → download `el-runtime-c`/`el-runtime-h` from Artifact Registry `foundation-prod` (`elc`/`elb` intentionally **not** downloaded) → compile the committed `dist/soul.c` directly: `cc -O2 -DHAVE_CURL dist/soul.c el_runtime.c -lssl -lcrypto -lcurl -lpthread -lm -o dist/neuron` → `strip -s` → smoke test `dist/neuron --help` → publish `neuron-soul@` to AR (push only). - **deploy** (push-to-main only): auth GCP → `get-credentials neuron-platform` → **determine idle slot** (the deployment at 0 replicas) → prepare artifacts (soul binary + `elc` + runtime for the Docker build) → **clone the engram repo** into `./engram/` (Dockerfile builds engram from source) → `docker build`+push `neuron-soul:` → `scripts/blue-green-deploy.sh --image --slot` → git-push updated infra manifests → `kubectl rollout status` → verify `neuron-mcp` endpoints. - **`deploy-gke.yaml`** (`workflow_dispatch` only, slot default `green`) — manual rollback / forced-slot deploy without a rebuild; same auth → slot → docker → blue-green → manifest-sync → verify steps. The Docker image (`Dockerfile`) is a two-stage build: stage 1 compiles `engram/src/server.el` → `engram.c` → `engram` binary via `elc` + `cc`; stage 2 is an Ubuntu 24.04 runtime (GLIBC 2.39 satisfies both binaries) with `soul` + `engram` + `entrypoint.sh`. ## Config & secrets (high level) Runtime configuration is injected as environment, sourced from a Kubernetes Secret `neuron-soul-secrets` via ExternalSecret (ESO → GCP Secret Manager, Workload Identity — no key files). From `deployment-blue.yaml`: | Env | Meaning | |---|---| | `NEURON_PORT` | soul HTTP port (7770) | | `NEURON_LLM_0_URL` / `_KEY` / `_FORMAT` | primary LLM endpoint (Anthropic format) | | `SOUL_CGI_ID` / `SOUL_IDENTITY` | CGI id + identity seed (→ `seed_persona_from_env`, `soul.el:250`) | | `NEURON_TOKEN` | auth token *(present in env; note the HTTP dispatch does not currently check it — doc 01 Divergence 3)* | | `NEURON_API_URL` | self-callback URL (`http://neuron-mcp.neuron-prod.svc.cluster.local:7770`) | | `ENGRAM_URL` / `ENGRAM_DATA_DIR` | `http://localhost:8742` / `/data` | There is also an in-graph config surface: `ConfigEntry` nodes read/written by `inspect_config` / `tune_config` (`neuron-api.el:616-653`) — runtime-tunable persona/behavior keys stored *in* the engram rather than the environment. > **Operational note to flag.** The `deployment-blue.yaml` image pin comment > (dated Jul 2026) records that `:latest` resolved to an untested build lacking a > `mem_save`/genesis-SIGSEGV fix, which is why the active slot is pinned to a > digest. Any promotion must (a) rebuild a good soul and (b) update the digest in > git so Argo CD and `blue-green-deploy.sh` agree. *(state as-of the manifests > read; verify current slot before deploying.)*