ktm v0.28.3

Releases

What changed in each version of ktm. Binaries for Linux x86_64, Linux arm64 and macOS are attached to each release on GitHub.

v0.28.3 (2026-10-16)

A program that clears the history before it ends leaves nothing for memsafe to report.

Fixes

v0.28.2 (2026-10-16)

A prompt of several lines for term.readLine.

Fixes

v0.28.1 (2026-10-16)

Two fixes found by a program built on term.readLine.

Fixes

v0.28.0 (2026-10-16)

Reading what people type, with line editing and a history, as GNU readline does.

term: reading lines

v0.27.1 (2026-10-16)

Range for over a map, closable sidebars and chapter descriptions on the docs site, and a steadier test.

Fixes

v0.27.0 (2026-10-16)

A new standard library module for the output of command-line programs, and two Quick Guides.

term: terminal output

Documentation

v0.26.0 (2026-10-16)

SQLite with arguments and structs, go-to-definition into packages, man pages, and Quick Guides.

SQLite: queries with arguments, rows into structs

LSP

Documentation

v0.25.0 (2026-10-15)

Two changes to the language that make a program that compiled before fail to: both say what to write instead.

Changes you may notice

v0.24.0 (2026-10-15)

Threads without C, a clearer name for ad, and an LSP that imports for you.

Changes you may notice

The thread module

LSP

Fixes

v0.23.2 (2026-10-15)

Fixes, most of them to the compiler. Two make programs that were wrong fail loudly: a printf-style format whose values don't match it no longer compiles, and an integer division by zero on arm64 crashes as it does on x86_64.

v0.23.1 (2026-10-15)

Two fixes to prebuilt packages (v0.22.0), found building ktm-pg v0.2.0 and ktm-jwt v0.2.0 from GitHub. With v0.22.0 or v0.23.0, KTM_PREBUILD=0 (or -jit) avoids both.

v0.23.0 (2026-10-15)

The cryptography that signed and encrypted tokens need, and new versions of two packages.

v0.22.0 (2026-10-15)

Conditions without parentheses, thread-local globals, crash reports that follow packages and inlined calls, JWTs, and packages compiled once.

Changes you may notice: threadlocal is a keyword now, so a variable can't be called that.

v0.21.0 (2026-10-14)

Everything a REST API needs, and a guide that builds one: HTTP programming takes a bookmarks service from ten lines to PostgreSQL, accounts, tests and production.

v0.20.1 (2026-10-14)

A fix for the release downloads.

v0.20.0 (2026-10-14)

Packages, as Go has them, and the libraries a web service needs: TLS, HTTPS, an HTTP client, cryptography and password hashing, URLs, UUIDs, base64, command-line flags and settings from the environment.

v0.19.0 (2026-10-14)

HTTP servers: net.http, Go's net/http and ServeMux for ktm, about 3 times as fast.

v0.18.0 (2026-10-14)

Memory that frees itself where it should, clearer -Memsafe reports, switch on strings, and a round of stdlib additions.

Changes you may notice

v0.17.2 (2026-10-13)

One logging fix.

v0.17.1 (2026-10-13)

Program arguments, and a flaky test.

v0.17.0 (2026-10-13)

C's stdio in libc, and logging to stdout.

Changes you may notice

v0.16.0 (2026-10-13)

A default logger, so a program logs from anywhere without passing a Logger around.

v0.15.2 (2026-10-13)

One fix for the language server.

v0.15.1 (2026-10-13)

Two fixes for compile errors.

v0.15.0 (2026-10-13)

Logging, and functions that know where they were called from.

v0.14.0 (2026-10-13)

JSON and YAML without reflection. Mark a struct @json or @yaml and the compiler writes its encoder and decoder.

Changes you may notice

v0.13.3 (2026-10-13)

Fixes for struct values passed by value, and clearer errors.

v0.13.2 (2026-10-13)

A vec.Vec is a JSON array.

v0.13.1 (2026-10-13)

A clean build and a tidier library reference.

v0.13.0 (2026-10-13)

Ahead of Go on every load. On Linux the coroutine loop now runs on io_uring, and a coroutine-per-connection server written the plain way (coro.serve, coro.read, coro.write) does more work per CPU-second than Go 1.27's goroutine-per-connection server in every scenario of the echo benchmark, with less memory (server on 2 cores, client on 6, so the client is never the limit):

ScenarioktmGoper server CPU-second
64 B request/response, 64 clients368k req/s168k req/s+45%
64 B request/response, 1024 clients379k req/s133k req/s+54%
4 KiB request/response, 64 clients267k req/s157k req/s+45%
64 KiB streams, 16 connections7.5 GB/s6.6 GB/s+12%
64 KiB streams, 64 connections7.4 GB/s6.5 GB/s+9%

Coroutines and networking

Changes you may notice

v0.12.0 (2026-10-12)

Memory. A coroutine-per-connection server now uses less memory than Go's goroutine-per-connection server at every load of the echo benchmark: with 1024 clients, 10.7 MB against Go's 11.6 MB (ktm v0.11.0 took 72 MB), and it keeps v0.11.0's speed (request/response 3% to 35% ahead of Go, a quarter of Go's p99 latency at 64 clients).

Big stack frames

Coroutines

Changes you may notice

v0.11.0 (2026-10-12)

Networking that is fast by default. A TCP server written the plain way, a coroutine per connection with coro.serve, now uses every core and the kernel's event queue, and beats Go 1.27's goroutine-per-connection server on request/response traffic (an echo benchmark on one Linux machine, both servers untuned):

64-byte request/responsektmGo
16 clients325k req/s281k req/s
64 clients379k req/s, p99 0.54 ms282k req/s, p99 2.63 ms
256 clients365k req/s294k req/s
1024 clients333k req/s321k req/s

With 4 KiB messages ktm serves 32% more requests with a fifth of Go's p99. A single bulk stream runs 22% faster; with 16 and 64 streams at once ktm and Go are about even (within 3% either way, run to run).

Coroutines and networking

Changes you may notice

v0.10.1 (2026-10-12)

Editor support, fixed and made rich: the language server understands the whole current language and gains the features editors expect, and the Vim syntax is rewritten for today's ktm. Also: directories and file operations in fs.

The language server

Vim and Neovim

The standard library

v0.10.0 (2026-10-12)

The standard library's containers, rebuilt on generics. vec.Vec<T>, map.Map<K, V>, map.Tree<K, V>, set.Set<T> and list.List<T> hold any type with no void * and no casts, are values (made with mod.new<T>(), freed with free(), so ad works), and replace IntVec, PtrVec, FloatVec, IntMap, StrMap, RBTree, IntSet and the void * list, which are gone. New: module sort and json.decode<T>.

import json, list, map, set, vec;

struct Point { i64 x; i64 y; };

i64 byX(Point *a, Point *b) { return a.x - b.x; }

void main()
{
  ad vec.Vec pts = vec.new();
  Point a = {3, 1}, b = {1, 2};
  pts.push(a);
  pts.push(b);
  pts.sort(&byX);
  for (auto p : pts) { printf("(%d,%d) ", p.x, p.y); }
  printf("\n");

  ad map.Map counts = map.new();
  u8[] words[] = {"to", "be", "or", "not", "to", "be"};
  for (auto w : words) { counts.set(w, counts.getOr(w, 0) + 1); }
  i64 it = 0, n;
  u8[] word;
  while (counts.next(&it, &word, &n)) { printf("%s=%d ", word, n); }
  printf("\n");

  ad set.Set seen = set.new();
  printf("%d %d\n", seen.add(4), seen.add(4));

  ad list.List jobs = list.new();
  auto build = jobs.append("build");
  jobs.append("test");
  jobs.remove(build);
  for (auto j = jobs.head; j; j = j.next) { printf("%s\n", j.value); }

  Point p = json.decode("{\"x\": 5, \"y\": 6}");
  printf("%d %d\n", p.x, p.y);
}
(1,2) (3,1) 
to=2 be=2 or=1 not=1 
1 0
test
5 6

The standard library

The language

Changes you may notice

Fixes

v0.9.0 (2026-10-12)

Generics and type aliases. A struct, its methods and a function can take type parameters, and each use with other types is compiled for those types: the same code you would write by hand, with no void * and no casts.

import mem;

struct Stack { T *items; i64 len; i64 cap; };

void Stack.push(T x)
{
  if (self.len == self.cap) {
    self.cap = self.cap ? self.cap * 2 : 4;
    self.items = mem.realloc(self.items, sizeof(T) * self.cap);
  }
  self.items[self.len++] = x;
}

T Stack.pop() { return self.items[--self.len]; }
void Stack.free() { mem.free(self.items); }

T max(T a, T b) { return a > b ? a : b; }

type Ints = Stack;

void main()
{
  ad Ints nums = {null, 0, 0};
  nums.push(1);
  nums.push(2);
  ad Stack words = {null, 0, 0};
  words.push("hello");
  printf("%d %s %d %.1f\n", nums.pop(), words.pop(), max(3, 7), max(1.5, 0.5));
}

The language

Fixes

v0.8.0 (2026-10-12)

Autodefer, one-instruction bit operations, pipelines and process groups in proc, and UDP with binary addresses in net.

import math, str;

void main()
{
  ad String text = str.from("one two three");
  ad u8[][] words = str.split(text, " ");     // freed at the end of main
  u32 h = 0x811C9DC5;
  for (auto w : words) { h = math.rotl((u32)(h ^ w.len), 5); }
  printf("%d words, hash %x\n", words.len, h);
}

The language

The standard library

Fixes

v0.7.0 (2026-10-12)

Code generation: the compiler inlines small functions, and a new module atomic shares counters and flags between threads without a mutex. Locals are now always at their type's alignment, which atomics and C code need.

import atomic, libc;

i64 gHits = 0;

i64 next(i64 i) { return i + 1; }   // small: compiled into its callers

void *worker(void *unused)
{
  for (i64 i = 0; i < 100000; i = next(i)) { atomic.add(&gHits, 1); }
  return null;
}

void main()
{
  libc.pthread_t *th[4];
  for (i64 i = 0; i < 4; i++) { libc.pthread_create(&th[i], null, &worker, null); }
  for (i64 i = 0; i < 4; i++) { libc.pthread_join(th[i]); }
  printf("%d hits\n", atomic.load(&gHits));
}

The compiler

The standard library

Changes you may notice

Fixes

v0.6.0 (2026-10-12)

Function overloading, and the library pieces the example projects still wrote by hand: hashing, case-insensitive and URL/HTML text helpers, portable file metadata, and waiting for a child process inside the coroutine loop.

import hash, str;

void send(i64 n) { printf("number %d\n", n); }
void send(u8[] text) { printf("text %.*s\n", (i32)text.len, text.ptr); }

void main()
{
  send(42);
  send("hello");

  u8 digest[32];
  hash.sha256("abc", digest);
  String hex = hash.hex(digest);
  defer hex.free();
  String q = str.urlEncode("a b&c");
  defer q.free();
  printf("%.8s %s %d\n", hex.cstr(), q.cstr(), str.equalsFold("GET", "get"));
}

The language

The standard library

Changes you may notice

v0.5.1 (2026-10-12)

Two fixes, and a clean-out of the old bug queue (every other entry in it was already fixed, or no longer applies to the frozen syntax).

Fixes

v0.5.0 (2026-10-12)

A systems library: what the example servers and tools used to write by hand. A coroutine loop for servers, a byte buffer, monotonic clocks and HTTP dates, the OS calls shells and daemons need, and small helpers. Nothing that compiled before changes meaning.

import bytes, coro, date, libc, net;

void *client(void *arg)
{// reads 4-byte big-endian lengths and their messages, answers with the time
  i32 fd = (i32)(i64)arg;
  bytes.Buffer in = bytes.bufferNew();
  defer in.free();
  while (coro.waitIdle(fd, libc.POLLIN, 5000) > 0 && in.readFrom(fd) > 0) {
    while (in.len >= 4 && in.len >= 4 + bytes.be32(in.data())) {
      in.consume(4 + bytes.be32(in.data()));
      String now = date.httpDate(date.unixNow());
      net.send(fd, now);
      now.free();
    }
  }
  coro.close(fd);
  return null;
}

void *acceptor(void *arg)
{
  i32 lfd = (i32)(i64)arg, fd;
  while ((fd = coro.accept(lfd)) != -1) { coro.go(&client, (void *)(i64)fd); }
  return null;
}

void main()
{
  coro.go(&acceptor, (void *)(i64)net.listen(9000, "*", 64));
  coro.run();
}

The standard library

Fixes

v0.4.0 (2026-10-12)

Reflection. A program can ask the compiler about its types at run time, and the json module uses that to turn any struct into JSON and back without hand-written code. Plus a JSON writer, enum names at run time, and typed comparators and callbacks.

import json, mem, reflect;

enum Role { guest, admin };
struct User { i64 id; String name; Role role; u8[][] tags; };

void main()
{
    User u;
    mem.set(&u, 0, sizeof(User));
    json.decode("{\"id\": 7, \"name\": \"Ada\", \"role\": \"admin\", \"tags\": [\"math\"]}",
                reflect.of(u));
    defer json.free(reflect.of(u));
    u.id++;
    String text = json.encode(reflect.of(u));
    defer text.free();
    printf("%s\n", text);   // {"id":8,"name":"Ada","role":"admin","tags":["math"]}
}

The language

The standard library

v0.3.1 (2026-10-12)

The rest of the library on u8[] and String, and the compiler catching more mistakes it used to let through. Code that uses proc, csv or sql needs updating, and a few things that compiled before (and did the wrong thing) are errors now.

Changes you may notice

New

Fixes

v0.3.0 (2026-10-11)

Safe strings and slices. ktm gets a slice type that knows its length, a native String, bounds checks that throw, and a standard library that works with both. The old u8 * buffer functions are gone or moved to a C-string module, so code written for v0.2 needs updating where it uses them.

import fs, str;

i32 main() {
    String text = fs.readAll("notes.txt");
    defer text.free();

    u8[][] lines = text.split("\n");
    defer lines.free();
    for (auto line : lines) {
        u8[] t = line.trim();
        if (t.startsWith("#")) {
            continue;
        }
        printf("%s (%d bytes)\n", t, t.len);
    }

    String out = str.format("%d lines", lines.len);
    defer out.free();
    out.append("!");
    printf("%s\n", out);
    return 0;
}

The language

The standard library

Watch out

Fixes

Known issue

v0.2.1 (2026-10-11)

A cleanup release: the macOS JIT fork crash, module libraries with run-time initialisers, readable names in every message, and a set of smaller fixes.

Fixes

Changes you may notice

v0.2.0 (2026-10-11)

A new language. ktm now has its own C-like syntax, modules, methods and a standard library organised into modules. The old HolyC syntax is gone: every program, test, doc page and example in the repository was converted once with an internal tool, which was then deleted. Code written for v0.1.0 does not compile with v0.2.0.



enum Kind { circle, rect };

struct Shape { Kind kind; f64 w, h; };

f64 Shape.area() {
    switch (self.kind) {
        case Kind.circle: return 3.14159 * self.w * self.w;
        case Kind.rect:   return self.w * self.h;
    }
    throw(1, "unknown kind %d", self.kind);
    return 0;
}

i32 main(i32 argc, u8 **argv) {
    defer printf("bye\n");
    Shape s = {Kind.rect, 2, 3};
    printf("%.1f\n", s.area());
    return 0;
}

The language

The standard library

Removed

Fixes

Known issue

On macOS, a child forked by a program run with -jit can crash right after fork(), rarely and under load. Compiled programs are fine; see Known issues for the workaround.

v0.1.0 (2026-10-10)

Renamed from hcc v0.19.3. The language, the standard library and their behaviour are unchanged; only names are new.

Changes you may notice