= ADR 0001 — Nexus client monolith, no Node.* references, ports-only for Graph/Time :revdate: 2026-07-25 == Status Accepted. == Context Robotico apps that talk to a remote Mishima Nexus server need a single, small SDK: parse a `nexus+*://` connection URI, dial the right gRPC plane, and expose the same `Robotico.Graph` / `Robotico.Time` ports application code already codes against for local/embedded engines. Nexus exposes four wire planes (Graph, Time, Document, Relational) plus a SQL-compatibility plane that is explicitly out of scope for this client. `Mishima.Nexus.Abstractions` (the server-side config/URI library) pulls in `Mishima.Nexus.Config.FSharp` and `Mishima.Nexus.Domain`, which are not published to the NuGet feed and are not appropriate dependencies for a thin remote client. == Decision * Publish a single package, `Robotico.Nexus.Client` (YAGNI — no `Robotico.Nexus.Client.Graph.dll` split etc.), with internal namespaces `Robotico.Nexus.Client`, `.Graph`, `.Time`, `.Document`, `.Relational`. * `NexusClient.Connect(connectionUri, options)` is the single entry point: parses the URI, dials a gRPC channel (TLS/mTLS or plaintext h2c), and exposes `Graph` (`IGraphStore`), `Time` (`ITimeStore`), `CheckHealth()`, and `CreateDocumentClient()` / `CreateRelationalClient()`. * **Do not reference `Mishima.Nexus.Abstractions` or any `Mishima.Nexus.Node.*` assembly.** The URI parsing logic (`NexusConnectionUri`) and TLS material handling (`NexusTlsMaterial`) are intentionally duplicated (not shared) from the server-side equivalents, using the same URI shape and the same environment variable names (POLA) so operators don't need to learn two conventions. This keeps the client's dependency graph to `Mishima.Nexus.Wire.V1` (generated gRPC stubs only), `Robotico.Graph`, `Robotico.Time`, and `Robotico.Result` — no F# assemblies, no server config, no host process wiring. * `nexus+sql://` is recognized and rejected with a dedicated error message pointing at `Mishima.Nexus.Sql` — this client never speaks the SQL-compatibility wire. * Graph and Time are **ports-only**: `NexusGraphStore`/`NexusGraphSession` implement `IGraphStore`/ `IGraphSession` and `NexusTimeStore`/`NexusTimeSession` implement `ITimeStore`/`ITimeSession` exactly — no Cypher/Gremlin/SPARQL/Vacuum surface on `IGraphSession`, and `Doctor()` is always available on `ITimeSession` (synthesized from `NexusTimeEngineService.Doctor`, since Nexus does not expose server-side filesystem paths to remote clients — `PrimaryPath`/`WalPath` report the logical `nexus://` identity instead, and `MaxActiveSeries` is always `0`). * Document and Relational get **thin connect helpers**, not a full `IMishimaDatabase` façade: `NexusDocumentClient` (Ping + single-document upsert/get/delete) and `NexusRelationalClient` (Ping only). `CreateDocumentWireClient()` / `CreateRelationalWireClient()` return the generated Wire.V1 clients directly for callers who need the full RPC surface. * `Create`/`Open`/`OpenOrCreate` on `IGraphStore`/`ITimeStore` are logical-database-name operations (the `databaseFilePath` parameter name is the port's, not this client's — see README). Nexus provisions logical databases lazily on first use, and `Ping` is engine-level (not per-database), so: - `Open` == ping + bind session (works even before the host has materialized any state for that name). - `OpenOrCreate` == `Open` (no separate provisioning RPC to race). - `Create` fails with `NXS_ALREADY_EXISTS` once `Ping` succeeds, since a successful `Ping` cannot distinguish "engine reachable, database still empty" from "database already has data". * All failures — wire status envelopes (`NexusStatus`) and gRPC (`RpcException`) — map to `Robotico.Result.Result` with stable `NXS_*` codes (`NexusErrors`), so callers see one consistent error surface regardless of failure layer. == Consequences * Apps depend on `Robotico.Nexus.Client` alongside `Robotico.Graph`/`Robotico.Time` and code against the same ports whether the backing store is embedded (`MishimaGraph`/`MishimaTime`) or remote (Nexus over gRPC) — swapping is a `NexusClient.Connect(...)` call, not a port change. * If `Mishima.Nexus.Abstractions` is later published cleanly (without Node.* transitive dependencies), this client can migrate to referencing it instead of duplicating URI/TLS parsing — tracked as a follow-up, not required for the 1.0.0 MVP. * Document/Relational callers who need more than Ping/basic CRUD must drop to the generated Wire.V1 client via `CreateDocumentWireClient()`/`CreateRelationalWireClient()` until (if ever) a richer façade ships.