lt-database-api#

CI

A small, security-first HTTP API that sits in front of PostgreSQL, MySQL / MariaDB or MongoDB and gives trusted clients one simple, engine-independent way to:

  • describe a schema declaratively (tables, columns, types, lengths, defaults, indexes) and have the API create it and keep it in sync on every later run, and
  • read, write, update and delete data through a small, injection-proof query language.

It was written for game-server bundles (the Replay.RE BlackICE sandbox can only talk to the outside world over HTTP and cannot open database connections), but nothing in it is game specific: any server-side program that can send an HTTP POST and compute an HMAC can use it.

 your program ──HTTPS + signed JSON──▶  lt-database-api  ──▶  PostgreSQL | MySQL | MongoDB
 (trusted server)                       (this project)

Why this exists#

Problem What this project does
The sandbox has no database driver, only HTTP GET/POST One POST endpoint per operation, JSON in and out
You do not want three code paths for three databases One schema format and one query format, translated per engine
Exposing a database-backed API is risky Every request is HMAC-signed, replay-protected, authorized per key and per namespace, and every response is signed too
Many small writes slow a game tick down Optional write-behind mode: journaled, validated, applied in batches at an interval or fixed times of day
Schemas drift when the program changes schema/sync diffs the declared schema against the last applied one and applies the difference, safely

Quick start#

Docker (development)#

git clone https://github.com/LandminenTester/lt-database-api
cd lt-database-api
cp .env.example .env              # then edit it and replace the example secret
docker compose --profile postgres up --build
curl http://localhost:8080/v1/health

--profile can be postgres, mysql or mongodb. The compose file allows plain HTTP on purpose so you can try things locally. Do not expose that port to a network. For production see docs/DEPLOYMENT.md.

Native (Windows or Linux)#

Download the archive for your system from the releases page (single self-contained executable, no .NET installation needed), or build it yourself:

dotnet publish src/Ldb.Api -c Release -r linux-x64 --self-contained -p:PublishSingleFile=true -o out
# or: -r win-x64

Configure it with environment variables or an appsettings.Production.json next to the executable, then start ./Ldb.Api. It can also run as a Windows service or a systemd unit, see docs/DEPLOYMENT.md.

Try a request#

tools/ldb_client.py is a dependency-free reference client (Python 3.8+). It signs requests exactly like the bundle does:

export LDB_URL=http://localhost:8080 LDB_KEY_ID=main LDB_SECRET='the-secret-from-your-env-file'
python tools/ldb_client.py schema/sync '{"namespace":"demo","schema":{"tables":[{"name":"players","primaryKey":["id"],"columns":[
  {"name":"id","type":"int64","autoIncrement":true},{"name":"name","type":"string","length":32,"unique":true},{"name":"score","type":"int32","default":0}]}]}}'
python tools/ldb_client.py data/insert '{"namespace":"demo","table":"players","rows":[{"name":"alice","score":10}]}'
python tools/ldb_client.py data/find   '{"namespace":"demo","table":"players","filter":{"field":"score","op":"gte","value":5}}'

Configuration at a glance#

Everything lives under the Ldb section. Use appsettings.json / appsettings.Production.json or environment variables with the prefix LDB_ and __ as separator (LDB_Ldb__Database__Provider=mysql). Full reference: docs/DEPLOYMENT.md.

{
  "Ldb": {
    "Database": { "Provider": "postgres", "DmlConnectionString": "...", "DdlConnectionString": "..." },
    "Security": {
      "Keys": [ {
        "KeyId": "main",
        "Current": "env:LDB_KEY_MAIN",        // secrets are references, never plain values
        "AllowedNamespaces": [ "mygame" ],
        "AllowedCidrs": [ "203.0.113.10/32" ]  // the game server's address
      } ]
    },
    "Schema": { "DestructiveMode": "Apply" }   // Apply | Quarantine | Deny
  }
}

Documentation#

Browse the public BlackICE resource library for guides and references.

Document Contents
docs/API.md Every endpoint, request and response shape, the filter language, error codes
docs/WRITE-BEHIND.md The optional write-behind mode: queue writes and apply them at an interval or at fixed times
docs/SCHEMA.md Portable column types, how they map to each engine, how schema sync decides and what it will not do
docs/SECURITY.md The signing protocol, threat model, hardening checklist, database privileges, key rotation
docs/DEPLOYMENT.md Docker, Windows service, systemd, reverse proxy with TLS, configuration reference, backups, monitoring
docs/ARCHITECTURE.md How the code is organised and how to add a database engine
docs/BUNDLE-INTEGRATION.md Writing your own client; using it from a Replay.RE BlackICE bundle (companion bundle: lt_database)
docs/BUILD.md Build, test, package, release and maintain the API
docs/openapi.yaml OpenAPI 3.1 description of the endpoints

Security in one paragraph#

Requests carry a key id, a timestamp, a one-time nonce and an HMAC-SHA256 over the exact payload bytes; the API answers with an HMAC over the status, the request nonce and the response body. Secrets are 32+ random bytes referenced from the environment or a file, compared in constant time, and can be rotated without downtime. Keys are limited to namespaces, operations and (optionally) source networks, with per-key rate limits and a lock-out for repeated failures. The API never accepts raw SQL or raw Mongo operators, binds every value as a parameter, quotes every identifier and validates both against the stored schema. Plain HTTP is refused unless explicitly allowed. Details and the honest limits of this design are in docs/SECURITY.md; please report vulnerabilities as described in SECURITY.md.

Project status#

Tested engines: PostgreSQL 17, MySQL 8.4 and MongoDB 8. The same scenarios run unchanged against all three in containers (dotnet test; the engine tests are skipped automatically if no container runtime is available, and run on every push in CI). Older PostgreSQL (14+), MySQL 8.0+, MariaDB and MongoDB 6+ use the same features and are expected to work but are not part of the automated matrix yet.

License#

MIT, see LICENSE.

Type to search the documentation.