# Robotico.Nexus.Client [![Woodpecker CI](https://dvalin.robotico.dev/api/badges/robotico/robotico-nexus-client-csharp/status.svg)](https://dvalin.robotico.dev/robotico/robotico-nexus-client-csharp) [![.NET 10](https://img.shields.io/badge/.NET-10.0-512BD4?logo=dotnet)](https://dotnet.microsoft.com/download/dotnet/10.0) [![C#](https://img.shields.io/badge/C%23-latest-239120?logo=csharp)](https://learn.microsoft.com/dotnet/csharp/) [![gRPC](https://img.shields.io/badge/gRPC-Nexus%20Wire-5C4EE5?logo=grpc)](https://grpc.io/) [![NuGet](https://img.shields.io/badge/NuGet-Robotico.Nexus.Client-blue?logo=nuget)](https://download.robotico.dev/nuget/) [![Robotico](https://img.shields.io/badge/Robotico-Nexus%20client-1f883d?logo=github)](https://brokkr.robotico.dev/robotico/robotico-nexus-client-csharp) Unified client SDK for a remote [Mishima Nexus](https://brokkr.robotico.dev/robotico/mishima-suite) server: connect via a `nexus+*://` URI and get `Robotico.Graph` / `Robotico.Time` ports bound to a logical database over gRPC, plus thin Document/Relational connect helpers. - NuGet: `Robotico.Nexus.Client` - Feed: `https://download.robotico.dev/nuget/v3/index.json` - Brokkr: `https://brokkr.robotico.dev/robotico/robotico-nexus-client-csharp` This package does **not** reference `Mishima.Nexus.Node.*` (the server host assemblies) — it only depends on the generated `Mishima.Nexus.Wire.V1` gRPC stubs, `Robotico.Graph`, and `Robotico.Time`. ## Quick start ```csharp using Robotico.Nexus.Client; Result connected = NexusClient.Connect("nexus+graph://localhost:50051/pathways"); using NexusClient client = connected.ExpectSuccess(); client.CheckHealth().ExpectSuccess(); Result opened = client.Graph.Open("pathways"); using IGraphSession session = opened.ExpectSuccess(); session.RegisterLabel("Person").ExpectSuccess(); session.UpsertVertex(1, ["Person"]).ExpectSuccess(); ``` ## Connection URIs | Scheme | Plane | Default database (empty path) | |-----------------------|------------|--------------------------------| | `nexus+graph://` | Graph | `pathways` | | `nexus+time://` | Time | `telemetry` | | `nexus+document://` | Document | `catalog` | | `nexus+relational://` | Relational | `ledger` | | `nexus+sql://` | — | rejected — use `Mishima.Nexus.Sql` directly | - The URI **host:port** is the gRPC endpoint; the **port is always explicit** (no default port is ever inferred, so a typo can't silently connect to the wrong service). - The URI **path** (e.g. `/pathways`) is the **logical database name**, sent as the `x-nexus-database` gRPC metadata header on every call — it is not a filesystem path, even though the underlying `IGraphStore`/`ITimeStore` port parameter is historically named `databaseFilePath`. - One `NexusClient` is bound to a single scheme + logical database from the connect URI. Open a second client to talk to another plane or database. ## TLS / mTLS / bearer auth `NexusClientOptions` mirrors `Mishima.Nexus.Wire.Clients.WireTlsMaterial`'s environment variables (same names, no shared assembly dependency): | Variable | Purpose | |---------------------------------|-------------------------------------------| | `NEXUS_TLS_CA_FILE` | CA certificate PEM (trust root) | | `NEXUS_TLS_CLIENT_CERT_FILE` | Client (machine credential) certificate | | `NEXUS_TLS_CLIENT_KEY_FILE` | Client private key PEM | | `NEXUS_TLS_MODE` | `https` (default) or `insecure` | | `NEXUS_WIRE_BEARER` | Bearer token attached as `Authorization` | | `NEXUS_WIRE_DEFAULT_BEARER` | Fallback bearer token | Pass an explicit `NexusTlsMaterial` / `BearerToken` via `NexusClientOptions` to override the environment. When no TLS material is present and `UseHttps` is unset, the client dials plaintext h2c (loopback/dev only). ## Create / Open / OpenOrCreate semantics (POLA) Nexus provisions logical databases lazily — the host creates a database's on-disk state the first time an RPC actually touches it, not via a separate "create database" call on the Graph/Time wire services. This client documents that behavior explicitly rather than pretending otherwise: - **`Open(name)`** — pings the engine with `x-nexus-database: name` and returns a session bound to that logical name. Succeeds even for a database the host hasn't materialized yet (the host creates it lazily on first write). - **`OpenOrCreate(name)`** — identical to `Open(name)`. There is no separate provisioning step to race against, so "open if exists, else create" collapses to "open" once the engine is reachable. - **`Create(name)`** — fails with `NXS_ALREADY_EXISTS` when `Ping` against `name` succeeds, because Nexus's `Ping` is an **engine-liveness** probe, not a **per-database existence** check — once the Graph/Time engine is reachable at all, `Create` cannot distinguish "database already has data" from "database is merely reachable". Use `OpenOrCreate` for greenfield databases instead. ## Document / Relational `NexusClient.CreateDocumentClient()` / `CreateRelationalClient()` return small façades (`Ping` plus, for Document, single-document upsert/get/delete) — enough for MVP smoke usage. They are **not** a full `IMishimaDatabase` façade. For the complete generated RPC surface, use `CreateDocumentWireClient()` / `CreateRelationalWireClient()`, which return the raw `Mishima.Nexus.Wire.V1` gRPC clients bound to the same channel and `x-nexus-database` metadata. ## Error codes All failures map to `Robotico.Result.Result` with `NXS_*` codes (`NexusErrors`): `NXS_URI_INVALID`, `NXS_URI_UNSUPPORTED_SCHEME`, `NXS_CONNECT_FAILED`, `NXS_TLS_REJECTED`, `NXS_ALREADY_EXISTS`, `NXS_NOT_FOUND`, `NXS_INVALID_ARGUMENT`, `NXS_DISPOSED`, `NXS_UNAVAILABLE`, `NXS_WIRE_FAILED`. `Robotico.Graph` (`GR_*`) and `Robotico.Time` (`TM_*`) codes surface unchanged from the ports. ## Quality gate Same Library 10 bar as other Robotico C# packages: - Analyzers on (`TreatWarningsAsErrors`, Meziantou MA0004/MA0042/MA0155, IDE0008/IDE0090/IDE0301/IDE0330) - `dotnet test` with coverlet **≥ 90%** line coverage on `Robotico.Nexus.Client`, exercised against an in-process fake gRPC server (Kestrel + in-memory fake `NexusGraphEngineService` / `NexusTimeEngineService` / `NexusDocumentEngineService` / `NexusRelationalEngineService` / `NexusHealthService`) — no live Nexus process required. - Woodpecker / GitHub CI run `bash scripts/woodpecker-verify.sh` ## Build ```bash bash scripts/woodpecker-verify.sh bash scripts/pack-release.sh ```