Documentation

HST is a library you link, not a service you call.

HST ships as libhstcore — a stripped shared object with an opaque C ABI. You compile the fixed operator once into an artifact, then open that artifact inside the process that already owns your state and apply sparse deltas in your hot loop. The dense state vector never leaves the process. There is no server, no port, no client library talking over a socket. Run the fit test before you integrate: the seven questions there decide whether your workload has the shape HST needs.

Why there is no hosted API

The transport costs a thousand times more than the work.

This is the central engineering fact about HST, and it decides the entire integration story. The delta kernel is very fast in absolute terms — on the reference NUC11 box, twenty timed apply_delta calls measured 0.002 ms of kernel compute each. The HTTP round trip wrapped around those same calls measured 1.75 ms minimum, 2.25 ms median, 4.55 ms maximum. The hop costs roughly a thousand times the compute it is carrying.

It gets worse with size, because the request shape is wrong in a way no amount of tuning fixes. At N=60,000 a single delta step saves 0.851 ms of compute. Measured p50 transport overhead for that same step: in-process ~0 ms, local IPC over stdio 61 ms, REST over localhost 188 ms. The delta itself is about 1.6 KB, but any call boundary that does not share memory has to ship dense x_prev and y_prev — 1.76 MB — and return a dense y_next at 464 KB. Serializing that request alone costs 32 ms.

So the break-even is explicit: a REST deployment only pays for itself if your full recompute already takes more than ~188 ms, and a local sidecar only if it takes more than ~61 ms. Below those thresholds the network eats the entire win and then some. The Docker/REST service was deprecated for production on 2026-07-06 for exactly this reason. We did not replace it with a leaner protocol, a binary codec, or a gRPC variant — the problem is the boundary, not the encoding. Embedded in-process is the only supported deployment.

1. Compile the operator once

Sparse operator A goes in as COO. The compiler emits a binary artifact holding the tile index. This is offline, done once per operator, and its cost is amortized across every subsequent apply. It takes no license token and is not metered.

hst-compile operator.json op.bin

# operator.json:
# {"operator": {"n": N, "m": M,
#               "rows": [...], "cols": [...],
#               "values": [...]},
#  "compile_options": {"tile_size": 32}}

2. Open the artifact

Include hstcore.h, link libhstcore.so (Linux x86_64) or libhstcore.dylib (macOS arm64). hst_open returns an opaque handle or NULL, writing the reason into your error buffer. A handle is not thread-safe — use one per thread or stream. The second argument is a license token; the community build has the license check compiled out, so pass an empty string.

hst_ctx *hst_open(const char *artifact_path,
                  const char *license_token,
                  char *errbuf, size_t errbuf_len);

hst_ctx *hst_open_batched(const char *artifact_path,
                          const char *license_token,
                          int32_t batch,
                          char *errbuf, size_t errbuf_len);

3. Apply deltas in your loop

Pass the changed column indices and their values. Returns 0 on success, negative on error. Batch width is 1–32; buffers are lane-interleaved as vals[i*batch+b].

int hst_apply_delta(hst_ctx *ctx,
                    const int32_t *cols,
                    const double *vals,
                    int32_t n,
                    double *y_out);

/* zero-copy read of current state;
   valid until the next apply/set_state/close */
const double *hst_state(const hst_ctx *ctx);

The rest of the ABI

Thirteen exported symbols total, ABI node HSTCORE_1.4. The build fails if anything else is exported. hst_recompute_full is the honest baseline you measure against — it does the whole dense recompute, and it is never metered in any build. Prime hst_set_state and hst_set_input together with the same baseline or not at all; hst_recompute_full refuses rather than return a silently wrong vector.

int32_t hst_batch(const hst_ctx *ctx);
int32_t hst_output_dim(const hst_ctx *ctx);
int32_t hst_input_dim(const hst_ctx *ctx);
int hst_set_state(hst_ctx *ctx,
                  const double *y0, int32_t len);
int hst_set_input(hst_ctx *ctx,
                  const double *x0, int32_t len);
int hst_apply_shadow(hst_ctx *ctx,
                     const int32_t *cols,
                     const double *vals,
                     int32_t n, double *y_out);
int hst_recompute_full(hst_ctx *ctx, double *y_out);
void hst_close(hst_ctx *ctx);
const char *hst_version(void);

C integration

Open once, apply many, close.

#include "hstcore.h"

char err[256];
/* "" is the token: community build, no license check. */
hst_ctx *ctx = hst_open("op.bin", "", err, sizeof err);
if (!ctx) { fprintf(stderr, "%s\n", err); return 1; }

int32_t m = hst_output_dim(ctx);
double *y = malloc(m * sizeof(double));

for (;;) {
    /* your system produces the sparse change */
    int32_t n = next_delta(cols, vals);

    int rc = hst_apply_delta(ctx, cols, vals, n, y);
    if (rc != 0)  { /* handle error */ break; }

    consume(y, m);
}

hst_close(ctx);

Build against the shipped shared object directly — there is nothing else to install:

cc -O2 app.c -Iinclude -Llib -lhstcore \
   -Wl,-rpath,'$ORIGIN/../lib' -o app

Python binding

A binding, not an engine: it loads the same shared object into the Python process and computes nothing on its own. Point lib_path at the library and pass an empty token — the community build ignores it.

import hstcore

with hstcore.HSTContext(
        "op.bin", "", lib_path="bin/libhstcore.so") as ctx:
    for cols, vals in stream:
        y = ctx.apply_delta(cols, vals)
        consume(y)

JVM binding

Java calls the same ABI through the JDK 22+ Foreign Function & Memory API — no JNI, no JNA, no native glue to compile. Package com.hornesci.hstcore. bin/ is not on the JVM's default library search path, so name the library explicitly.

import com.hornesci.hstcore.*;

Abi.load("bin/libhstcore.so");
try (Session ctx =
        Session.open("op.bin", "", 16)) {
    while (stream.hasNext()) {
        double[] y = ctx.applyDelta(cols, vals);
        consume(y);
    }
}

Licensing

The community build is Apache-2.0 and the license check is compiled out of it, not set generously: hst_open succeeds on an empty token, nothing expires, no operator is too large, and no usage file is written to your disk. Production use and redistribution are permitted. A separately built, metered library also exists as its own artifact; it is the one a signed token applies to, and you would know because we would have handed you one.

hst_open("op.bin", "", err, sizeof err)

# no token, no quota, no expiry,
# no counter file, nothing to renew

Nothing calls home

There is no activation call, no telemetry beacon, and nothing to allowlist at your egress, in either build. The community build additionally keeps no local state: it writes no usage counter, so there is no file to back up, relocate, or corrupt. It runs disconnected and on an air-gapped network.

hst_recompute_full is never metered
in any build -- it is the reference
the delta path is checked against,
not the product hot path.

What you get

The community build, and what it costs.

Nothing. The community build of the runtime is Apache-2.0 — production use and redistribution permitted, no token, no meter, no expiry, no account to create. It arrives as a tree you unpack, with the library, the operator compiler and the before/after tool in bin/, language bindings in packages/, and a reproduction kit in repro/. It is not published for download yet; ask us and we will send it.

bin/libhstcore.so         # or .dylib
bin/include/hstcore.h
bin/hst-compile.<platform>
bin/hst_compare.<platform>   # before/after
share/sample_op.bin
share/sample_stream.bin
packages/hstcore-{py,java,node,go,rs,dotnet}
packages/{spdelta,bindnum,claimlint}
repro/
install.sh  verify.sh  README.md  LICENSE

hst_compare is the point of the package. It replays a delta stream through both paths — full recompute and delta apply — asserts the two outputs match exactly, and prints the speedup. Run it against the bundled sample first, then against your own artifact and your own stream. Pick the build for your machine; the license argument is optional and an empty string is fine:

./bin/hst_compare.linux-x86_64

./bin/hst_compare.darwin-arm64 \
  --op your_op.bin \
  --stream your_stream.bin \
  --license "" \
  --dump out.tsv

Read the ratio against a competent delta, not against full recompute. Against a from-scratch rebuild almost anything looks spectacular. The number that decides something is HST against the best incremental path you would otherwise write yourself — and where the changing set never moves, HST is expected to lose, because it scans tile-padded entries where a column-exact delta touches only the dirty columns. packages/spdelta is that baseline, shipped in the same tree so you can run it. If you have not run the fit test yet, start there; it is faster to rule a workload out in seven questions than in a build.

Platforms. The library is built for Linux x86_64 and macOS arm64. There is no hstcore.dll — on Windows the bindings install and the tooling runs from Git Bash, and there is no runtime for them to bind to. WSL, Windows on ARM, Linux aarch64 and macOS x86_64 are untested rather than supported.