# Interfaces (gRPC · REST · API · HTTP) (/configure/reference/interfaces)



KubeMQ fronts its server core with four interfaces: the **gRPC** transport, the
**REST/WebSocket** transport, the management/dashboard **API** (with opt-in API
authentication), and the **shared HTTP server** that hosts MCP, A2A, and CloudEvents on
the REST port. Each setting is shown for both targets — Docker single-node (`config.yaml`
key · env var) and Kubernetes/Helm (`spec.*` path). A dash (`—`) in the Helm/CRD column
means the setting is not a typed CRD field; on Kubernetes it is reachable only through
`spec.configData` (raw config) or a directly-set pod env var.

<Callout type="info">
  **All four interfaces are opt-out (always-on).** gRPC, REST, and API are enabled by
  default on both targets. On Docker they use the `enable: true/false` model; on the chart
  they use the inverted `disabled: true/false` framing (`spec.grpc.disabled`,
  `spec.rest.disabled`, `spec.api.disabled`) — all shipped as `disabled: false`. Set
  `disabled: true` to turn one off. The shared HTTP server has no toggle of its own — it
  rides the REST port and is emitted only when you set one of its `spec.http.*` fields.

  **The shared HTTP listener does not belong to REST.** It starts if **any** of REST, MCP,
  A2A, or CloudEvents is enabled, and stops only when all four are off. Disabling REST alone
  retires the REST routes and leaves the other three serving on the same port.
</Callout>

<Callout type="warn">
  **`.port` on Kubernetes moves everything together.** Setting `spec.grpc.port` /
  `spec.rest.port` / `spec.api.port` makes the operator emit the matching listener env var
  (`CONNECTORS_GRPC_PORT` / `CONNECTORS_REST_PORT` / `API_PORT`) **and** set the Kubernetes
  `Service` `port`/`targetPort` **and** the container port — the in-pod listener and the
  Service port move as one. On Docker the same env var moves the actual listener, which you
  then publish with `docker run -p`.
</Callout>

<Callout type="info">
  **A default `helm install` publishes nothing outside the cluster.** The shipped
  `values.yaml` sets no `expose` and no `nodePort` for any interface, and a `KubemqCluster`
  with `expose&#x60; unset gets a &#x2A;*`ClusterIP`** Service. gRPC is *not* reachable on every node
  out of the box — to reach it from outside you set `expose` yourself (`NodePort` or
  `LoadBalancer`), and if you need a **predictable** port you must also set `nodePort`,
  because an unset one is assigned by the kernel and cannot be configured into a client
  ahead of the install.
</Callout>

## gRPC [#grpc]

The primary client transport. Enabled by default on both targets.

| Setting          | Type         | Default               | Valid values                              | Docker (config.yaml key · env var)                                       | Helm/CRD path                | Notes                                                                                                                                                                                           |
| ---------------- | ------------ | --------------------- | ----------------------------------------- | ------------------------------------------------------------------------ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Enable / disable | bool         | enabled (`true`)      | true / false                              | `connectors.grpc.enable` · `CONNECTORS_GRPC_ENABLE`                      | `spec.grpc.disabled`         | Inverted boolean: Docker `enable: true`, Helm `disabled: false`. When disabled, no listener starts and the rest of the section is skipped.                                                      |
| Port             | int (string) | `50000`               | 1–65535                                   | `connectors.grpc.port` · `CONNECTORS_GRPC_PORT`                          | `spec.grpc.port`             | Server field is a string port; rejected if non-numeric or out of range.                                                                                                                         |
| Sub buffer size  | int          | `100`                 | ≥ 0                                       | `connectors.grpc.subbuffsize` · `CONNECTORS_GRPC_SUB_BUFF_SIZE`          | `spec.grpc.bufferSize`       | Per-subscription channel buffer. Negative rejected. &#x2A;*Name divergence:** server `subBuffSize` ↔ CRD `bufferSize`.                                                                          |
| Body limit       | int (bytes)  | `104857600` (100 MB)  | ≥ 0 bytes                                 | `connectors.grpc.bodylimit` · `CONNECTORS_GRPC_BODY_LIMIT`               | `spec.grpc.bodyLimit`        | Server `int` bytes; CRD `int32` bytes, emitted only when non-zero (no CRD type default). Chart example sets `10000000`.                                                                         |
| gRPC reflection  | bool         | `false`               | true / false                              | `connectors.grpc.enablereflection` · `CONNECTORS_GRPC_ENABLE_REFLECTION` | `spec.grpc.enableReflection` | Enables server reflection for grpcurl/tooling.                                                                                                                                                  |
| Service exposure | enum         | `ClusterIP`           | `ClusterIP` / `NodePort` / `LoadBalancer` | (`-p` host port)                                                         | `spec.grpc.expose`           | Kubernetes `Service` type; empty ⇒ `ClusterIP`. The chart ships **no** `expose` value. On Docker use `-p`.                                                                                      |
| NodePort         | int          | `0` (kernel-assigned) | 30000–32767                               | (`-p`)                                                                   | `spec.grpc.nodePort`         | Applied only when `expose: NodePort` **and** the value is > 0. The chart ships no default — leave it unset and the kernel picks a port you cannot configure into a client ahead of the install. |

## REST · WebSocket [#rest--websocket]

The HTTP/WebSocket transport, which also hosts the shared HTTP server (MCP, A2A,
CloudEvents). Enabled by default on both targets.

| Setting          | Type         | Default               | Valid values                              | Docker (config.yaml key · env var)                               | Helm/CRD path            | Notes                                                                                                                                                                                                                                            |
| ---------------- | ------------ | --------------------- | ----------------------------------------- | ---------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Enable / disable | bool         | enabled (`true`)      | true / false                              | `connectors.rest.enable` · `CONNECTORS_REST_ENABLE`              | `spec.rest.disabled`     | Inverted boolean. Disabling REST removes the REST routes (they return `404`) but **leaves MCP, A2A and CloudEvents serving** — the shared HTTP server starts if *any* of the four is enabled. To take the whole listener down, disable all four. |
| Port             | int (string) | `9090`                | 1–65535                                   | `connectors.rest.port` · `CONNECTORS_REST_PORT`                  | `spec.rest.port`         | String port; rejected if non-numeric or out of range.                                                                                                                                                                                            |
| Body limit       | string / int | server `"100M"`       | Echo size string or bytes                 | `connectors.rest.bodylimit` · `CONNECTORS_REST_BODY_LIMIT`       | `spec.rest.bodyLimit`    | Server size-string (`"100M"`); CRD `int32` bytes, emitted only when non-zero. Chart example sets `1000000`.                                                                                                                                      |
| Sub buffer size  | int          | `100`                 | ≥ 0                                       | `connectors.rest.subbuffsize` · `CONNECTORS_REST_SUB_BUFF_SIZE`  | `spec.rest.bufferSize`   | Negative rejected. &#x2A;*Name divergence:** `subBuffSize` ↔ `bufferSize`.                                                                                                                                                                       |
| Read timeout     | int (s)      | `60`                  | ≥ 0 (CRD ≥ 1)                             | `connectors.rest.readtimeout` · `CONNECTORS_REST_READ_TIMEOUT`   | `spec.rest.readTimeout`  | Seconds. Server rejects negative; CRD enforces `Minimum=1`.                                                                                                                                                                                      |
| Write timeout    | int (s)      | `60`                  | ≥ 0 (CRD ≥ 1)                             | `connectors.rest.writetimeout` · `CONNECTORS_REST_WRITE_TIMEOUT` | `spec.rest.writeTimeout` | Seconds. Server rejects negative; CRD enforces `Minimum=1`.                                                                                                                                                                                      |
| Service exposure | enum         | `ClusterIP`           | `ClusterIP` / `NodePort` / `LoadBalancer` | (`-p` host port)                                                 | `spec.rest.expose`       | Kubernetes `Service` type; empty ⇒ `ClusterIP`. The chart ships **no** `expose` value. On Docker use `-p`.                                                                                                                                       |
| NodePort         | int          | `0` (kernel-assigned) | 30000–32767                               | (`-p`)                                                           | `spec.rest.nodePort`     | Applied only when `expose: NodePort` **and** the value is > 0. The chart ships no default.                                                                                                                                                       |

### REST CORS [#rest-cors]

CORS policy for the REST transport. On the CRD it is a first-class `spec.rest.cors.*`
sub-object; on Docker/env the keys ride the `CONNECTORS_REST_CORS_*` prefix.

| Setting           | Type      | Default          | Valid values | Docker (config.yaml key · env var)                                                 | Helm/CRD path                     | Notes                                                     |
| ----------------- | --------- | ---------------- | ------------ | ---------------------------------------------------------------------------------- | --------------------------------- | --------------------------------------------------------- |
| Allow origins     | string\[] | `["*"]`          | origin list  | `connectors.rest.cors.alloworigins` · `CONNECTORS_REST_CORS_ALLOW_ORIGINS`         | `spec.rest.cors.allowOrigins`     | **Must be non-empty** — validation rejects an empty list. |
| Allow methods     | string\[] | `["GET","POST"]` | method list  | `connectors.rest.cors.allowmethods` · `CONNECTORS_REST_CORS_ALLOW_METHODS`         | `spec.rest.cors.allowMethods`     | **Must be non-empty.**                                    |
| Allow headers     | string\[] | `[]`             | header list  | `connectors.rest.cors.allowheaders` · `CONNECTORS_REST_CORS_ALLOW_HEADERS`         | `spec.rest.cors.allowHeaders`     | Empty list allowed.                                       |
| Allow credentials | bool      | `false`          | true / false | `connectors.rest.cors.allowcredentials` · `CONNECTORS_REST_CORS_ALLOW_CREDENTIALS` | `spec.rest.cors.allowCredentials` |                                                           |
| Expose headers    | string\[] | `[]`             | header list  | `connectors.rest.cors.exposeheaders` · `CONNECTORS_REST_CORS_EXPOSE_HEADERS`       | `spec.rest.cors.exposeHeaders`    |                                                           |
| Max age           | int (s)   | `0`              | ≥ 0          | `connectors.rest.cors.maxage` · `CONNECTORS_REST_CORS_MAX_AGE`                     | `spec.rest.cors.maxAge`           | Negative rejected.                                        |

<Callout type="info">
  **REST → shared-HTTP inheritance.** When you explicitly set `Rest.ReadTimeout`,
  `Rest.BodyLimit`, `Rest.Cors.AllowOrigins`, or `Rest.Cors.AllowMethods` (and don't set the
  matching `Http.*` key), the value propagates to the shared HTTP server. `Http.Port` always
  inherits `Rest.Port` when `Http.Port` is unset. The shared server's distinct CORS defaults
  (the `MCP-*`/`OPTIONS`/`DELETE` headers below) are preserved whenever you did **not**
  override the REST side, so MCP and A2A keep working.
</Callout>

## Management API [#management-api]

The management/dashboard API. Enabled by default on both targets.

| Setting              | Type      | Default                         | Valid values                              | Docker (config.yaml key · env var)       | Helm/CRD path           | Notes                                                                                                                            |
| -------------------- | --------- | ------------------------------- | ----------------------------------------- | ---------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Enable / disable     | bool      | enabled (`true`)                | true / false                              | `api.enable` · `API_ENABLE`              | `spec.api.disabled`     | Inverted boolean.                                                                                                                |
| Port                 | int       | `8080`                          | 1–65535                                   | `api.port` · `API_PORT`                  | `spec.api.port`         | Validated only when the API is enabled.                                                                                          |
| Bind address         | string    | `127.0.0.1` (empty → `0.0.0.0`) | IP address                                | `api.bindaddress` · `API_BIND_ADDRESS`   | —                       | Config/env only; no typed CRD field. An empty value is normalized to `0.0.0.0`.                                                  |
| Allow origins (CORS) | string\[] | `["*"]`                         | origin list                               | `api.alloworigins` · `API_ALLOW_ORIGINS` | `spec.api.allowOrigins` | CRD joins the list with commas into the env var. **When API auth is enabled, `"*"` and an empty list are rejected** (see below). |
| Service exposure     | enum      | `ClusterIP`                     | `ClusterIP` / `NodePort` / `LoadBalancer` | (`-p` host port)                         | `spec.api.expose`       | Kubernetes `Service` type; empty ⇒ `ClusterIP`. The chart ships **no** `expose` value. On Docker use `-p`.                       |
| NodePort             | int       | `0` (kernel-assigned)           | 30000–32767                               | (`-p`)                                   | `spec.api.nodePort`     | Applied only when `expose: NodePort` **and** the value is > 0. The chart ships no default.                                       |

### Management API authentication [#management-api-authentication]

Opt-in authentication for the management API + web dashboard (`[Api.Auth]`). &#x2A;*Disabled by
default.** The data plane (gRPC/REST messaging) is unaffected. On the CRD it lives under
`spec.api.auth.*`.

| Setting                | Type   | Default               | Valid values     | Docker (config.yaml key · env var)                                      | Helm/CRD path                                     | Notes                                                                                                                                                                                                 |
| ---------------------- | ------ | --------------------- | ---------------- | ----------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Enable                 | bool   | `false`               | true / false     | `api.auth.enable` · `API_AUTH_ENABLE`                                   | `spec.api.auth.enable`                            | Gates the whole section.                                                                                                                                                                              |
| Session idle minutes   | int    | `30`                  | > 0 when enabled | `api.auth.sessionidleminutes` · `API_AUTH_SESSION_IDLE_MINUTES`         | `spec.api.auth.sessionIdleMinutes`                | Idle-session timeout. Must be positive.                                                                                                                                                               |
| Session absolute hours | int    | `8`                   | > 0 when enabled | `api.auth.sessionabsolutehours` · `API_AUTH_SESSION_ABSOLUTE_HOURS`     | `spec.api.auth.sessionAbsoluteHours`              | Absolute session lifetime. Must be positive.                                                                                                                                                          |
| Store path             | string | `""` → main store dir | filesystem path  | `api.auth.storepath` · `API_AUTH_STORE_PATH`                            | `spec.api.auth.storePath`                         | When empty and auth enabled, defaults to `Store.StorePath` (the main store directory).                                                                                                                |
| Trusted TLS proxy      | bool   | `false`               | true / false     | `api.auth.trustedtlsproxy` · `API_AUTH_TRUSTED_TLS_PROXY`               | `spec.api.auth.trustedTLSProxy`                   | Set `true` when a TLS-terminating reverse proxy fronts the API port; otherwise cookie/session login is refused on plain HTTP.                                                                         |
| Admin username         | string | `admin`               | username         | `KUBEMQ_API_ADMIN_USERNAME` (os env)                                    | `spec.api.auth.adminUsername`                     | **Env/secret only — not a viper config-file field.** Read directly from the environment.                                                                                                              |
| Admin password         | string | —                     | password         | `KUBEMQ_API_ADMIN_PASSWORD` / `KUBEMQ_API_ADMIN_PASSWORD_FILE` (os env) | `spec.api.auth.adminSecretRef` · `adminSecretKey` | **Secret only** — never a config field. The operator injects the password into the pod from a Secret; on Docker set the env var (or `_FILE`) yourself. Cluster mode requires it when auth is enabled. |

#### The account model [#the-account-model]

The two settings above (`adminUsername` / `adminPassword`) bootstrap the **first** account.
They are not the whole model — once auth is on, the control plane has three roles and two
account types, and only the first admin is configured through the server config at all.
Every other account is created through the API or dashboard.

**Three roles**, each a superset of the one below it:

| Role         | Can do                                                                    |
| ------------ | ------------------------------------------------------------------------- |
| `read_only`  | Read dashboards, stats, snapshots, audit logs                             |
| `read_write` | Everything in `read_only`, plus send/receive messages and subscribe       |
| `admin`      | Everything in `read_write`, plus manage accounts and revert configuration |

**Two account types:**

| Type      | Authenticates with                       | Notes                                                                                                                                                          |
| --------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `user`    | Username + password, cookie session      | Subject to the idle and absolute session limits above.                                                                                                         |
| `service` | A bearer API key, `kmq_<keyid>_<secret>` | Sent as `Authorization: Bearer …`. For scripts, agents, and CI. **A service account cannot hold the `admin` role** — that combination is rejected at creation. |

Two behaviors worth knowing before you turn auth on:

* **The bootstrap admin must rotate its password on first login.** The login response
  carries `must_change_password`, and the account stays in that state until the password
  is changed — an automation that logs in with the bootstrap credentials and ignores the
  flag will not get far.
* **Cookie-authenticated mutations require an `X-KubeMQ-CSRF` header.** Bearer-authenticated
  service accounts are exempt: there is no ambient credential for a malicious site to
  replay, so the guard applies to session cookies only.

<Callout type="info">
  **A `read_only` account is a real account, and it can read configuration.** If you are
  handing out dashboard access, `read_only` is the right default — but treat it as a
  principal with visibility into server settings, not as a view of nothing sensitive.
</Callout>

<Callout type="warn">
  **Wildcard CORS is refused when API auth is enabled.** With `api.auth.enable: true`, the
  server rejects boot if `api.alloworigins` is empty or contains `"*"` — credentialed auth
  requires a concrete origin list (e.g. `["https://app.example.com"]`). The
  `sessionIdleMinutes`/`sessionAbsoluteHours` values must both be positive.
</Callout>

## Shared HTTP server [#shared-http-server]

The shared HTTP server hosts MCP, A2A, and CloudEvents on the REST port. On Kubernetes it
is a first-class `spec.http.*` group; its CORS lives under `spec.http.cors.*`. It has no
enable/disable toggle — it is active whenever REST is, and the CRD emits its env vars only
for the `spec.http.*` fields you set.

| Setting                | Type      | Default                                                                                    | Valid values     | Docker (config.yaml key · env var)                                                 | Helm/CRD path                     | Notes                                                                                                        |
| ---------------------- | --------- | ------------------------------------------------------------------------------------------ | ---------------- | ---------------------------------------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Port                   | string    | inherits `Rest.Port` (`9090`)                                                              | 1–65535          | `connectors.http.port` · `CONNECTORS_HTTP_PORT`                                    | `spec.http.port`                  | Empty ⇒ inherits the REST port. A differing explicit value logs a warning; the HTTP server uses `Http.Port`. |
| Read timeout           | int (s)   | `60`                                                                                       | ≥ 0 (CRD ≥ 1)    | `connectors.http.readtimeout` · `CONNECTORS_HTTP_READ_TIMEOUT`                     | `spec.http.readTimeout`           | Negative rejected.                                                                                           |
| Body limit             | string    | `"100M"`                                                                                   | Echo size string | `connectors.http.bodylimit` · `CONNECTORS_HTTP_BODY_LIMIT`                         | `spec.http.bodyLimit`             | CRD field is a string.                                                                                       |
| Base URL               | string    | `""`                                                                                       | URL              | `connectors.http.baseurl` · `CONNECTORS_HTTP_BASE_URL`                             | `spec.http.baseUrl`               | Server field is `BaseURL`; env `..._BASE_URL`.                                                               |
| CORS allow origins     | string\[] | `["*"]`                                                                                    | origin list      | `connectors.http.cors.alloworigins` · `CONNECTORS_HTTP_CORS_ALLOW_ORIGINS`         | `spec.http.cors.allowOrigins`     |                                                                                                              |
| CORS allow methods     | string\[] | `GET, POST, DELETE, OPTIONS`                                                               | method list      | `connectors.http.cors.allowmethods` · `CONNECTORS_HTTP_CORS_ALLOW_METHODS`         | `spec.http.cors.allowMethods`     | Wider than REST — MCP/A2A need `DELETE`/`OPTIONS`.                                                           |
| CORS allow headers     | string\[] | `Authorization, Content-Type, MCP-Protocol-Version, MCP-Session-Id, Last-Event-ID, Accept` | header list      | `connectors.http.cors.allowheaders` · `CONNECTORS_HTTP_CORS_ALLOW_HEADERS`         | `spec.http.cors.allowHeaders`     | Includes the `MCP-*` headers.                                                                                |
| CORS allow credentials | bool      | `false`                                                                                    | true / false     | `connectors.http.cors.allowcredentials` · `CONNECTORS_HTTP_CORS_ALLOW_CREDENTIALS` | `spec.http.cors.allowCredentials` |                                                                                                              |
| CORS expose headers    | string\[] | `MCP-Session-Id, MCP-Protocol-Version`                                                     | header list      | `connectors.http.cors.exposeheaders` · `CONNECTORS_HTTP_CORS_EXPOSE_HEADERS`       | `spec.http.cors.exposeHeaders`    |                                                                                                              |
| CORS max age           | int (s)   | `86400`                                                                                    | ≥ 0              | `connectors.http.cors.maxage` · `CONNECTORS_HTTP_CORS_MAX_AGE`                     | `spec.http.cors.maxAge`           | Negative rejected.                                                                                           |

<Callout type="warn">
  **Version floor:** the shared-HTTP `spec.http.*` fields are present throughout the current
  GA chart line — `kubemq-crds` and `kubemq-cluster` **3.x** (latest **3.2.0**) with
  `kubemq-controller` **2.x** (operator **v2.3.0**). Anything older than the 3.0.0 / 2.0.0 GA
  release predates this reference: upgrade to the current line rather than trying to work out
  which pre-GA build carried which field. On Docker the `connectors.http.*` keys are
  available regardless of chart version.
</Callout>

## Example [#example]

Set the gRPC port on each target. On Kubernetes `spec.grpc.port` moves the in-pod listener,
the `Service` port/`targetPort`, and the container port together; on Docker it moves the
listener and you publish it with `-p`. This is a
single-setting snippet — see the [Docker guide](/configure/docker) and the
[Kubernetes guide](/configure/kubernetes) for complete, runnable configurations.

<Tabs items="[&#x22;Docker&#x22;, &#x22;Helm&#x22;]">
  <Tab value="Docker">
    ```yaml title="config.yaml"
    connectors:
      grpc:
        port: "50000"
    ```
  </Tab>

  <Tab value="Helm">
    ```yaml title="values.yaml"
    grpc:
      port: 50000
    ```
  </Tab>
</Tabs>

For the full Docker delivery methods and `docker run` port publishing see the
[Docker guide](/configure/docker); for `values.yaml` mapped to the
`KubemqCluster` spec and `Service` exposure see the
[Kubernetes guide](/configure/kubernetes).
