Connecting to Elasticsearch with four fields and no database
An index has no namespace above it, so a database selector would be a control with no effect, and a self-signed cluster certificate has nowhere to go here.
Open the connection dialog for Elasticsearch and it asks for four things: host,
port, user, password. Nothing is installed to connect; the client on the 9200
side of this form is the runtime’s own fetch, and package.json is untouched.
There is no Database field, no connection-string tab, and no TLS row.
Those absences do not share a cause, and the difference decides what you can do about each. The Database field is missing because it would be a control with no effect. The CA you would paste into a TLS row has nowhere to go because this transport cannot carry it.
The four fields on 9200, and the one that is missing
| Field | Required | What it does |
|---|---|---|
host |
Yes | validate() throws DatabaseConfigError - “Elasticsearch requires a host”. There is nothing to substitute for it |
port |
No | Defaults to 9200, applied both in the provider and again at the transport |
user / password |
No | Sent as HTTP Basic only when user is set, for the security plugin |
database |
- | Not offered, and ignored if set |
Credentials are genuinely optional rather than optional-in-principle. Measured
against a node with security disabled, a bogus Basic header is ignored and the
request answers HTTP 200, so a cluster that does not check will not tell you your
password was wrong either.
Connecting proves more than the port. connect() sends one SELECT 1 - measured
HTTP 200, one column named 1, type integer - which needs no index and so also
succeeds against a cluster holding nothing yet. Because the SQL endpoint path is
product-specific, that same request proves the product: POST /_plugins/_sql
against Elasticsearch answers {"error":"no handler found for uri [/_plugins/_sql] and method [POST]"}. A connection pointed at the wrong one of
the two search products fails at the form, quoting the cluster’s own words,
rather than failing later on somebody’s query.
The local fixture is the shape the four fields were designed against:
docker compose -f database-compose.yml up -d elasticsearch
It pins docker.elastic.co/elasticsearch/elasticsearch:9.1.4 with
discovery.type: single-node, xpack.security.enabled: "false" and
ES_JAVA_OPTS: -Xms512m -Xmx512m, and publishes 9200 only - 9300, the transport
protocol, is deliberately not published, because nothing here speaks it. Host
localhost, port 9200, no user, no password. The health check waits for
yellow rather than green: a single node that has been asked for a replica is
yellow forever.
Why an index needs no namespace above it
The missing Database field is not an unimplemented one. An index has no namespace
above it, and the product’s own SQL says so. SHOW TABLES reports a catalog of
docker-cluster - the cluster name - and that catalog is not addressable in a
statement. Measured:
SELECT customer FROM "docker-cluster".probe_orders
answers parsing_exception. So a database selector would be a control that
changes nothing, and worse, one implying a scoping decision the user does not
actually have. The monitoring rows carry an empty schema name for the same
reason, which renders as no prefix at all.
What sits in that space instead is the product’s own vocabulary. An index is the
table, a document is the row, and the select action reads “Select Top 50
Documents”. Column types are the mapping’s words rather than SQL’s: SELECT customer, total FROM probe_orders declares keyword and double, and SELECT note declares text - the same vocabulary the schema tree shows, because the
tree is read from GET /<index>/_mapping rather than from a statement. That is
not decoration. The schema has to come from the mapping, because SELECT *
describes the statement rather than the index: measured, an index mapping a
flattened and a nested field answers SELECT * with
{"columns":[],"rows":[[]]}, a table with no columns at all.
Two counts in the schema read the same way. Foreign keys are always [] and
declaresForeignKeys is false, so the empty list means impossible here rather
than none declared. indexCount is 0 and stays 0, because every mapped field is
inverted-indexed as a property of being mapped, so there is no index object to
name. The engine list states the short version: no row editing, no
ER diagrams.
There is no connection string either
supportsConnectionString is false and the form has no paste tab. Two
independent reasons, and the second one is the sharper of the two.
There is no URI convention for this HTTP surface. A cluster is addressed by host
and port; the official client takes a node URL, which is not a
credential-carrying DSN a shared parser could round-trip.
And http:// and https:// are already claimed in the shared connection-string
parser, where an HTTP URL is the canonical connection target for ClickHouse.
Pasting http://localhost:9200 therefore selects ClickHouse. That consequence is
recorded rather than hidden: connection-string-parser.ts is not touched by this
provider, and the connection form’s unparseable-string message lists the schemes
that do exist and deliberately omits these two.
What a self-signed certificate runs into
config.ssl with any mode but disable switches the transport from http to
https. The port does not move with it. This product serves HTTPS on the same
9200, so there is no second well-known number to fall back to, and inventing
one would send credentials to a port nothing is listening on.
Three TLS settings are not honoured here at all: ssl.caCert, ssl.clientCert
and ssl.rejectUnauthorized. Global fetch cannot carry a custom CA, present a
client certificate, or relax verification without an undici Agent as its
dispatcher, and undici is not a dependency. A cluster behind a self-signed
certificate therefore fails verification, and there is no field on this form to
paste the CA into and no connection string to smuggle it through. A
publicly-trusted certificate works.
That matters more here than on most engines, because a secured Elasticsearch
commonly ships a self-signed certificate on first boot. It also means one of the
four TLS modes describes this transport honestly and three do not:
verify-system is the handshake fetch actually performs. require does not
mean encrypt-without-checking the way it does on the driver-based providers -
nothing in this transport can skip a check - and verify-ca and verify-full
cannot pin against a pasted CA. Nothing in the code branches on which one is
selected.
The deployment shapes that work today
Three shapes connect with what the form offers.
A local or private-network cluster over plain HTTP, which is the fixture above and the shape the tool is built for: the container runs beside the database, and what travels to the engineer is a URL rather than the data.
A cluster fronted by a load balancer or reverse proxy holding a publicly-trusted
certificate. This is also the answer to a second absence, because there is no
sniffing, no failover and no retry: one statement is one fetch to one host, and
a refused socket surfaces as an error rather than being retried against another
node. A deployment that wants failover puts a balancer in front, and that
balancer is the host a connection points at.
A cluster with the security plugin on, reached with user and password over a certificate the platform’s store already trusts.
Elastic Cloud, Elastic Cloud Enterprise and Elastic Cloud on Kubernetes are where people run this engine, and none of them has been probed. Managed-only services are listed as deliberately absent rather than assumed to work, so treat those three as untested rather than supported.
Agent AUTO mode - the tool-using run - does not open on this connection at all.
queryReadOnly exists on exactly three providers, PostgreSQL, SQLite and DuckDB,
because there the read-only profile is enforced by the database itself; the
search providers implement none, so an AUTO run ends engine-unsupported. The
fact that this grammar cannot write - INSERT, UPDATE, DELETE, CREATE TABLE and ALTER TABLE are each a 400 parsing_exception on 9.1.4 - is a
property of the engine, not a per-statement guarantee the database enforces, and
it does not substitute for one. Agent PLAN mode does open here: it is toolless,
executes nothing, and drafts a statement for a human to run, grounded on the
mapping read the schema tree already made.