# Use the C SDK

**Applies to:** current development source bundles. Obtain a matched client/server release through [early-access support](mailto:info@simplemagic.com); registry publication is not available yet. Paths below are relative to the supplied source checkout.

C11/POSIX application client and shared transport for the Java, .NET, and PHP
bindings. Source release, ABI 1; not published to a package repository.
Linux and macOS build targets; macOS arm64 is currently validated.

## Build and install

Requires a C compiler, Make, POSIX threads, OpenSSL 3+, and json-c 0.16+.
On Debian/Ubuntu install `build-essential libssl-dev libjson-c-dev`.
On Apple Silicon macOS install `openssl@3 json-c` with Homebrew.

```sh
make -C c-smkv
make -C c-smkv install PREFIX=/your/install/prefix
```

Headers go in `include/`, the shared library in `lib/`. Keep the matching
OpenSSL/json-c runtime libraries installed. On Linux run your distribution's
loader-cache setup or set `LD_LIBRARY_PATH` to your chosen library directory.
On macOS the library has an `@rpath` install name; give applications an rpath.
Override `CPPFLAGS`/`LDFLAGS` or `DEPS_PREFIX` for other dependency locations.

## Binary API

```c
#include <smkv.h>
#include <stdio.h>

int main(void) {
    int error;
    smkv_client *client = smkv_create_v2("{\"endpoint\":\"127.0.0.1:7379\"}", &error);
    if (!client) return 1;
    smkv_result result;
    int code = smkv_call(client, 2, "greeting", 8, "hello", 5,
                         UINT64_MAX, 1, 0, &result);
    smkv_free(result.data);
    if (!code) {
        code = smkv_call(client, 1, "greeting", 8, NULL, 0,
                         UINT64_MAX, 0, 0, &result);
        if (!code) fwrite(result.data, 1, result.length, stdout);
        smkv_free(result.data);
    }
    smkv_destroy(client);
    return code != 0;
}
```

Link with `-I.../include -L.../lib -lsmkv` and the appropriate loader path.
A zero-length successful value differs from `SMKV_NOT_FOUND`. Output buffers must
be released with `smkv_free`, even when empty (NULL is allowed).

`smkv_call` opcodes: 1 get, 2 put, 3 delete, 4 row get, 5 row put, 6 row update,
7 row delete. Raw row operations require wire-encoded keys and values; use the
JSON API below to avoid manual row encoding. Flags: 1 local sync, 2 replica ack,
4 durable replica ack (also set 2). TTL is milliseconds; `UINT64_MAX` means omitted,
zero expires immediately. Omitted TTL preserves expiry on update, clears it on put.
Delete/get must omit TTL. Replica reads are explicitly selected with `replica=1`.

## Configuration, routing, and security

Use either `endpoint` for direct connections or `seeds` for management discovery:

```json
{
  "seeds": ["10.0.0.1:7381", "10.0.0.2:7381"],
  "timeout_ms": 5000,
  "max_key_bytes": 1024,
  "max_value_bytes": 1048576,
  "security": {"ca": "/etc/smkv/ca.pem", "name": "app", "token": "FROM_SECRET_PROVIDER"},
  "management_security": {"ca": "/etc/smkv/ca.pem", "name": "app", "token": "FROM_SECRET_PROVIDER"}
}
```

The token shown is a placeholder: real tokens must match server configuration and
contain 32–256 printable ASCII characters. TLS and auth are independently optional.
Omit `ca` for plaintext, omit `name`/`token` for no auth. TLS validates the supplied
CA and hostname/IP and never downgrades; data and management profiles are independent.

Cluster discovery is lazy on the first operation. CRC32 routes the complete key
(including the table for rows). Only explicit WrongOwner responses trigger retries.
Maps must agree on cluster identity, partition count, and each observed epoch;
stale-only observations are rejected. Discovery probes seeds sequentially with a
500 ms per-seed cap within the total deadline. Epochs above INT64_MAX are rejected.

## JSON convenience ABI

`smkv_request_json(client, request)` returns allocated UTF-8 JSON; free it with
`smkv_free`. Request fields: numeric `op`, hex `key`, optional `value` (hex),
`flags`, `ttl_ms` (decimal string), and `replica` (bool). Rows also use `table`.
Row gets use an optional `columns` array for projection; puts/updates use a
`columns` object of tagged values, and updates may include a `remove` array.

```json
{"op":5,"table":"users","key":"3432","columns":{
  "name":{"t":"s","v":"Ada"},
  "visits":{"t":"i","v":"1"},
  "payload":{"t":"b","v":"00ff"},
  "active":{"t":"bool","v":true},
  "ratio":{"t":"f","v":"000000000000f43f"}
}}
```

`i` uses a signed int64 decimal string; `f` uses eight little-endian IEEE754 bytes
in hex. Responses contain `code`, `unknown`, and successful `value` or `columns`.
Server diagnostics and tokens are deliberately excluded from errors.

## Lifecycle and limits

One handle serializes requests and retains at most one TCP connection. Use a
bounded application pool of handles for concurrency; this initial SDK does not
provide a multi-connection pool. Switching owners reconnects. There is no async API,
scan API, batch API, Windows qualification, or throughput claim yet.

The deadline includes lock wait, discovery, connect, and I/O. Native DNS cannot be
interrupted; use IP addresses for strict bounds. `smkv_close` interrupts active I/O
and rejects queued operations. Stop all callers before `smkv_destroy`; never use a
destroyed pointer. Do not share clients across fork.

Positive error codes match server statuses. Negative codes are defined in
`smkv.h`. `outcome_unknown` means a mutation may have completed; never blindly
retry it. No uncertain mutation is automatically replayed. Successful default
writes do not request per-write fsync.

Run `python3 c-smkv/tests/test_native.py` after building for ABI fault tests and
see [cross-language qualification](sdk-clients.html#initial-release-limits).

## Logical databases

Select the database when constructing the client (or cluster client):

```json
{"endpoint":"127.0.0.1:7379","database":"orders"}
```

Omitting the option uses `default` and preserves existing wire keys. Named
databases require database-aware development servers (cluster protocol 5, or 6
with experimental group sync). Names use 1–64 ASCII letters, digits, `_` or `-`.
The selection scopes raw keys and all table operations, including replica reads.
The complete database-prefixed key is hashed for routing and counts against the
key-size limit. Databases share cluster capacity and durability settings.

Use the matching database-aware native library and, for Java, JNI bridge. These
bindings require the ABI 2 constructor `smkv_create_v2`; an older library fails to
load or construct a client rather than silently selecting the default database.
