NordicKV v3.8.2-cluster · swedenpnoo.xyz
Node: sto-relay-01 SYNCED
repl-ping 1.2 ms
--:--:-- UTC

Connection Strings & REST / gRPC Specification

endpoint swedenpnoo.xyz · dual-stack: gRPC over HTTP/2 (TLS 1.3) and REST/JSON over HTTP/1.1+2 · bearer + mTLS

primary endpoint
swedenpnoo.xyz
A · 185.42.118.7 · anycast
ports
443gRPC / REST (TLS 1.3)
9143native wire (mTLS)
9187metrics / Prometheus
ca material
nordickv-root-ca.pem
expires 2029-06-30 · RSA-4096
probe result
1.2 ms · 200 OK
h2 stream · 0 retries

Connection Strings

copy-paste ready · secrets are masked by default, click a blurred value to reveal it individually

click to reveal
primary tls uri
tls://swedenpnoo.xyz:443
native wire uri (mTLS, client cert required)
nkv+tls://nkv_app••••••••@swedenpnoo.xyz:9143/nkv-prod-eu?sslmode=verify-full&pool_max_conns=64
grpc channel target
grpcs://swedenpnoo.xyz:443
method: nkv.cluster.v3.ClusterService/StreamWAL
metadata: authorization=Bearer <token> · x-nkv-consistency=strong
rest base url
https://swedenpnoo.xyz/v3
in-cluster service dns
nkv+tls://sto-relay-01.nkv-prod-eu.svc.cluster.local:9143/nkv-prod-eu
# peers: hel-follower-02.nkv-prod-eu…  fra-follower-03.nkv-prod-eu…

tls ca certificate

nordickv-root-ca.pem · valid 2026-06-30 → 2029-06-30
-----BEGIN CERTIFICATE-----
MIIF1jCCA76gAwIBAgIQGmZk3Kp1sVw9TbQxRl7eMjANBgkqhkiG9w0BAQwFADCB
jzELMAkGA1UEBhMCU0UxEjAQBgNVBAgMCVN0b2NraG9sbTEQMA4GA1UEBwwHU29s
bmExFTATBgNVBAoMDE5vcmRpY0tWIEFCMRQwEgYDVQQLDAtDbHVzdGVyIENBMRYw
FAYDVQQDDA1Ob3JkaWNLViBSb290MRUwEwYDVQQFEww1NTY2Nzc4ODk5MDAwHhcN
MjYwNjMwMDAwMDAwWhcNMjkwNjMwMjM1OTU5WjCBjzELMAkGA1UEBhMCU0UxEjAQ
BgNVBAgMCVN0b2NraG9sbTEQMA4GA1UEBwwHU29sbmExFTATBgNVBAoMDE5vcmRp
Y0tWIEFCMRQwEgYDVQQLDAtDbHVzdGVyIENBMRYwFAYDVQQDDA1Ob3JkaWNLViBS
b290MRUwEwYDVQQFEww1NTY2Nzc4ODk5MDB2MB4GA1UdHgEB/wQUMBKgEDAOggxr
bm9kZS5zd2VkZW4wge0GA1UdIAGGBAgwgYUwgYIGCiSsOAkBAgEBAQEwgXMwLgYI
KwYBBQUHAgEWImh0dHBzOi8vc3dlZGVucG5vby54eXovY2EtY3BzLmh0bWwwQQYI
KwYBBQUHAgIwNRoDTktWDAtOb3JkaWNLViBDQTAkFyBodHRwczovL3N3ZWRlbnBu
b28ueHl6L2NhLWNwcy5odG1sMA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwgbQGA1UdHwSBrDCBqTBGoESgQoZAaHR0cDovL2Ny
bC5zd2VkZW5wbm9vLnh5ei9ub3JkaWNrdi1yb290LWNhLmNybDBfoF2gW4ZZbGRh
cDovL2xkYXAuc3dlZGVucG5vby54eXovY249Tm9yZGljS1YrUm9vdCxjbj1QQSwS
ZGM9c3dlZGVucG5vbyxkYz14eXo/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDA6
BggrBgEFBQcBAQQuMCwwKgYIKwYBBQUHMAGGHmh0dHA6Ly9vY3NwLnN3ZWRlbnBu
b28ueHl6L29jc3AwHQYDVR0OBBYEFJ0nq3Zg1WqYyFmvTr8pQ2x0ZzR/MB8GA1Ud
IwQYMBaAFJ0nq3Zg1WqYyFmvTr8pQ2x0ZzR/MC4GA1UdEQQnMCWCI3N3ZWRlbnBu
b28ueHl6gg5zdG8tcmVsYXktMDEubm9kZTANBgkqhkiG9w0BAQwFAAOCAgEAhT2f
-----END CERTIFICATE-----

verify with: openssl x509 -in nordickv-root-ca.pem -noout -fingerprint -sha256

Bearer Tokens

scope-bound · rotate every 30 d · revoked tokens propagate in < 400 ms

read-write token ACTIVE
Bearer nkv_rw_••••••••••••••••••••••••••••
exp 2026-05-12T00:00:00Z
read-only token ACTIVE
Bearer nkv_ro_••••••••••••••••••••••••••••
exp 2026-07-04T00:00:00Z
admin token ROTATION DUE
Bearer nkv_ad_••••••••••••••••••••••••••••
exp 2026-05-03T00:00:00Z

Client Pool Defaults

max connections
64 / process
min idle
4
connect timeout
2500 ms
idle lifetime
300 s
statement timeout
15 s
keepalive
30 s / 10 s
retry budget
3 attempts · jitter
consistency
strong (leader lease)

Client Examples

connection-pooled clients · plain monospaced rendering, no syntax highlighting applied

connect_pool.py · asyncpg pool + circuit breaker
"""
NordicKV v3.8.2-cluster — asynchronous connection-pool example.
Target node : sto-relay-01 [SYNCED]
Endpoint    : tls://swedenpnoo.xyz:443
"""

import asyncio
import os
import ssl
import logging
from contextlib import asynccontextmanager

import asyncpg

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("nordickv")

CA_PATH = os.environ.get("NKV_CA_PATH", "/etc/nkv/nordickv-root-ca.pem")
DSN = "nkv+tls://nkv_app:{password}@swedenpnoo.xyz:9143/nkv-prod-eu"
PASSWORD = os.environ["NKV_PASSWORD"]

POOL: asyncpg.Pool | None = None


def build_ssl_context() -> ssl.SSLContext:
    ctx = ssl.create_default_context(cafile=CA_PATH)
    ctx.minimum_version = ssl.TLSVersion.TLSv1_3
    ctx.check_hostname = True
    ctx.verify_mode = ssl.CERT_REQUIRED
    # mutual TLS: the cluster enforces peer verification
    ctx.load_cert_chain(
        certfile=os.environ.get("NKV_CLIENT_CERT", "/etc/nkv/client.crt"),
        keyfile=os.environ.get("NKV_CLIENT_KEY", "/etc/nkv/client.key"),
    )
    return ctx


async def init_connection(conn: asyncpg.Connection) -> None:
    """Session-level tuning applied to every pooled connection."""
    await conn.execute("SET nkv.consistency = 'strong'")
    await conn.execute("SET nkv.read_preference = 'leader'")
    await conn.execute("SET statement_timeout = '15s'")
    log.info("pooled connection ready: %s", conn.get_server_pid())


@asynccontextmanager
async def cluster_pool():
    global POOL
    if POOL is None:
        POOL = await asyncpg.create_pool(
            dsn=DSN.format(password=PASSWORD),
            ssl=build_ssl_context(),
            min_size=4,
            max_size=64,
            max_inactive_connection_lifetime=300.0,
            command_timeout=15.0,
            timeout=2.5,
            init=init_connection,
            server_settings={"application_name": "nkv-client/1.4.0"},
        )
    try:
        yield POOL
    finally:
        # keep the pool warm between requests; close on shutdown
        pass


async def write_with_retry(key: str, payload: bytes, attempts: int = 3) -> str:
    async with cluster_pool() as pool:
        last_error: Exception | None = None
        for attempt in range(1, attempts + 1):
            try:
                async with pool.acquire() as conn:
                    txn_id = await conn.fetchval(
                        """
                        SET NVK_OP = 'SET'
                        RETURNING txn_id
                        """,
                        key,
                        payload,
                    )
                    log.info("committed txn=%s key=%s bytes=%d", txn_id, key, len(payload))
                    return txn_id
            except (asyncpg.PostgresConnectionError, asyncio.TimeoutError) as exc:
                last_error = exc
                backoff = min(0.05 * (2 ** attempt) + 0.02 * attempt, 2.0)
                log.warning("attempt %d/%d failed (%s) — retrying in %.3fs",
                            attempt, attempts, exc.__class__.__name__, backoff)
                await asyncio.sleep(backoff)
        raise RuntimeError(f"write failed after {attempts} attempts") from last_error


async def merge_upsert(counter_key: str, delta: int) -> None:
    async with cluster_pool() as pool:
        async with pool.acquire() as conn:
            await conn.execute(
                "MERGE INTO telemetry_counters (k, v) VALUES ($1, $2) "
                "ON CONFLICT (k) DO UPDATE SET v = telemetry_counters.v + EXCLUDED.v",
                counter_key,
                delta,
            )


async def main() -> None:
    await write_with_retry("user:sessions:2f81bd0c", b'{"uid":4711,"ttl":3600}')
    await merge_upsert("telemetry:counters:page-views", 1)
    async with cluster_pool() as pool:
        rows = await pool.fetch("SELECT k, v, written_at FROM kv.hot_cache LIMIT 5")
        for row in rows:
            print(f"{row['k']:<48} {row['v']!r:<32} {row['written_at']}")


if __name__ == "__main__":
    asyncio.run(main())

REST Endpoint Reference

base: https://swedenpnoo.xyz/v3
method path auth description rate limit

gRPC Service Surface

proto package nkv.cluster.v3 · proto3 · retries via service config

service ClusterService {
  rpc Get            (GetRequest)          returns (GetResponse);
  rpc Put            (PutRequest)          returns (PutResponse);
  rpc Merge          (MergeRequest)        returns (MergeResponse);
  rpc BatchPut       (stream PutRequest)   returns (BatchPutResponse);
  rpc StreamWAL      (StreamWALRequest)    returns (stream WALRecord);
  rpc ResyncVerify   (ResyncVerifyRequest) returns (ResyncVerifyResponse);
  rpc ListShards     (ListShardsRequest)   returns (ListShardsResponse);
  rpc ListSnapshots  (ListSnapshotsRequest)returns (ListSnapshotsResponse);
  rpc Health         (HealthRequest)       returns (HealthResponse);
}
max message
16 MiB
deadline default
15 s
keepalive
30 s ping / 10 s timeout
lb policy
round_robin (per shard)

Error Model

NKV_QUORUM_UNAVAILABLE
HTTP 503 · gRPC UNAVAILABLE · fewer than 2 replicas reachable
NKV_LAG_EXCEEDED
HTTP 409 · gRPC FAILED_PRECONDITION · strong read on a lagging follower
NKV_TOKEN_SCOPE
HTTP 403 · gRPC PERMISSION_DENIED · token lacks the required scope
NKV_CHECKSUM_MISMATCH
HTTP 422 · gRPC DATA_LOSS · snapshot or WAL slice failed sha256 verification