All posts

June 10, 2026

Introducing Kestrel

Introducing Kestrel

Intro

Kestrel is a static-typed, compiled language, built for a great developer experience, efficient computation and low memory usage, and ... Kestrel is the first language built for the age of AI, using agentic development.

  • Low memory usage
  • Kestrel skill
  • Context7 documentation

What does kestrel look like?

Principles

Ergonomics First

The common path should be short and readable. Trailing closures, it shorthand, labeled parameters, and guard let keep code concise without sacrificing clarity.

Sugar over Magic

Every convenience desugars to something predictable. some x is .Some(x). null is .None. Custom interpolation is a protocol conformance. You can always peel back a layer and see what's happening.

Everything is a Protocol

Ranges, iteration, string interpolation, pattern matching, comparison — they're all protocols you can conform to. Your types are first-class citizens in every language feature.

Predictable Runtime

No garbage collector, no runtime. Reference counting with deterministic destruction. You know when things are allocated and when they're freed.

Features of Kestrel

Compiled to Native Code

Kestrel compiles directly to native machine code. No runtime, no VM, no interpreter. The result is a single static binary you can deploy anywhere.

Type Inference

Kestrel has a full type inference engine. You get the safety of a strong type system without writing types everywhere.

let name = "Alice"; let scores = [95, 87, 92]; let average = scores.iter().sum() / scores.count;

Generics

Generic functions and types with where clauses and associated type constraints.

func smallest[T](in items: Array[T]) -> T? where T: Comparable { var min: T? = null; for item in items { match min { some m => if item < m { min = some item; }, null => min = some item }; }; min }

Protocols & Extensions

No classes, no inheritance. Composition through protocols and extensions.

protocol Greetable { func name() -> String; } extend Greetable { func greet() -> String { "Hello, \(self.name())!" } } struct User: Greetable { var username: String; func name() -> String { self.username } }

Pattern Matching

Exhaustive match expressions with destructuring, or-patterns, array patterns, and guards.

match response { .Ok(user) => "Welcome, \(user.name)", .Err(.NotFound) => "User not found", .Err(.Forbidden or .Unauthorized) => "Access denied", .Err(_) => "Something went wrong" }

Error Handling

Errors are values, not exceptions. Result and Optional are regular types. try and throws are sugar over the type system.

func loadConfig(path: String) -> Config throws IOError { let data = try File.read(path); let parsed = try Toml().decode(source: data); Config(from: parsed) }

Closures

Trailing closure syntax with implicit it parameter for single-argument closures.

let names = users .iter() .filter { it.isActive } .map { it.name } .collect();

Enums with Data

Algebraic data types. Enums can carry associated values, and the compiler checks you handle every case.

enum Shape { case Circle(radius: Float64) case Rect(width: Float64, height: Float64) case Point } func area(shape: Shape) -> Float64 { match shape { .Circle(r) => 3.14159 * r * r, .Rect(w, h) => w * h, .Point => 0.0 } }

String Interpolation

String interpolation that works with any type conforming to Formattable. Custom types can define their own interpolation behavior.

let msg = "Found \(users.count) users in \(elapsed) ms"; let query: SQL = "SELECT * FROM users WHERE email = \(email)";

Iterators

Lazy, composable iterators. for-in loops, map, filter, reduce, flatMap, zip, and more — all backed by the Iterator protocol.

let total = orders .iter() .filter { it.status == .Shipped } .map { it.total } .sum();

Collections

Array, Dictionary, Set, Deque, and Heap in the standard library. All generic, all iterable.

var scores: [String: Int64] = [:]; scores("Alice") = 95; scores("Bob") = 87; let top = scores.values .iter() .filter { it >= 90 } .collect();

ARC & Copy-on-Write

Reference counting with deterministic destruction — no GC pauses, no unpredictable latency. String and Array use copy-on-write internally, so copies are cheap until you mutate.

let a = "hello"; var b = a; // shares storage, no copy b.append(" world"); // COW: copies on first mutation

C FFI

Call C libraries directly with @extern declarations. Types that cross the FFI boundary conform to FFISafe.

@extern(.C, mangleName: "sqlite3_open") func sqlite3_open(filename: Pointer[UInt8], db: Pointer[RawPointer]) -> Int32;

Benchmarks

Kestrel is a compiled language, and that affords it certain advantages. However, in the current preview, it lacks an optimized build profile, and isn't as fast as it will be. To put numbers on it, I built 3 identical notes-app webservers in Kestrel, Node, and Go, and benchmarked them. These are somewhat complex test beds involving listening for connections, http parsing, reading and writing from a database, rendering HTML, and serializing and deserializing JSON.

Deploy Size

This is where you see Kestrel shine. Because Kestrel doesn't need a runtime, it is able to ship binaries far smaller than Node or Go, even in this early state.

Baseline Memory

Kestrel is even stronger here. The lack of a runtime allows it to run in resource-constrained environments. With memory prices rising, Kestrel lets you do real work on small, cheap machines.

Startup Time

Because Kestrel is purely compiled, it can start up almost instantly for better serverless operations.

GET Throughput

Kestrel 0.16 shows a regression here versus the beta because it measures different things. The beta was measuring cached speed, so raw throughput of requests. 0.16 is able to achieve around 3000 req/s on that. But this test shows something different: how fast it can render HTML, which Kestrel struggles with. Kestrel in its current state does not inline or optimize much. This results in operations being far slower than they will be with the LLVM backend. Secondarily, it's because Kestrel uses reference counted strings. When building the HTML to return, Kestrel has to allocate and deallocate many intermediates. This is more of a standard library issue, and could be fixed in the future with a StaticString struct and using a StringBuilder to create HTML.

POST Throughput

Kestrel is very close under POST. Even without advanced LLVM optimizations, Kestrel can keep up with languages like Node and Go in request processing. Expect to see even more improvements here.

Memory after 1200 Requests

Kestrel's reference-counted (ARC) memory strategy means it wins big on memory usage. Kestrel will allow you to do more with less memory.

Stress Test p99 Latency

I didn't run this benchmark with node, but Kestrel clearly has some catching up to do here.

Kestrel 0.16 BetaKestrel 0.16NodeGo
Deploy Size1.9 MB2.3 MB17.2 MB12.4 MB
Baseline Memory3.0 MB2.7 MB59.8 MB12.2 MB
Startup Time23ms9ms120ms28ms
GET throughput*689 req/s (cached)260 req/s (uncached)3012 req/s3557 req/s
POST throughput*120 req/s1800 req/s2178 req/s1608 req/s
Memory after 1200 reqs23.3 MB3.0 MB83.8 MB19.0 MB
Stress Test p99 Latency**274 ms--13ms

* measured using hey with 1200 requests ** 5 minutes sustained, 50 concurrent connections, on cloud computer with 512 MB RAM and 1 core

Built with Kestrel

Kestrel Wall

Notes App

Full-stack note-taking app with a REST API backend (Perch + Talon-SQLite) and an HTMX frontend. User auth, folders, and CRUD.

Weather Dashboard

Weather forecast app using the Open-Meteo API. City search and 7-day forecasts, built with Perch and HTMX.

Wordle

Wordle clone where all game state lives in the URL — fully stateless server. Built with Perch.

Pokédex

Interactive Gen 1 Pokédex that fetches live data from the PokéAPI. Built with Perch and Swoop.

Counter

Minimal HTMX counter app — increment, decrement, reset. A good starting point for learning Perch.

APOD Viewer

NASA Astronomy Picture of the Day viewer. Fetches daily images using Swoop and serves them with Perch.

Terminal Games

Several terminal games built with a shared TUI library:

  • Snake — classic grid-based snake with score tracking
  • Pong — two-player pong with ANSI graphics
  • Breakout — terminal breakout game

SDL Games

Graphical games using SDL2 bindings:

  • Game of Life — Conway's Game of Life with interactive pattern stamping
  • Pong — SDL2 pong with real-time 2D rendering

Libraries

Perch — Web Server

A web framework with routing, middleware, and typed context.

var app = App[Database](db); app.route(get: "/users/:id") { (req, db) in let id = req.param("id"); let user = try db.query[User]("SELECT * FROM users WHERE id = ?", [id]); return Response.ok(JsonBody(user)); }; let _ = app.listen(port: 8080);

Swoop — HTTP Client

HTTP client with a fluent, immutable API and TLS support.

let api = Swoop(baseUrl: "https://api.example.com") .header("Authorization", "Bearer \(token)"); let users = try api.fetch("/users"); let result = try api.post("/users", JsonBody(newUser));

Talon-SQLite — Database

SQLite bindings with typed queries and transactions.

let db = try Database(path: "app.db"); try db.execute("CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT)"); try db.execute("INSERT INTO notes (body) VALUES (?)", ["Hello"]); let notes = try db.query[Note]("SELECT * FROM notes");

Quill — Serialization

Format-agnostic serialization framework. Conform to Serialize once, get JSON, TOML, and more.

let user = User(name: "Alice", age: 30); let json = try Json().encode(value: user.toValue()); let toml = try Toml().encode(value: user.toValue());

Quill-JSON

JSON encoder and decoder for the Quill framework.

let parsed = try Json().decode(source: "{\"name\": \"Alice\"}"); let encoded = try Json().encode(value: parsed);

Quill-TOML

TOML encoder and decoder for the Quill framework.

let config = try Toml().decode(source: "name = \"my-app\"\nport = 8080");

Plume — Templating

Lightweight string templating with HTML escaping.

var t = Template(); t.put("name", "Alice"); t.setInt("count", 42); let html = t.render("<p>Hello {name}, you have {count} items</p>");

HTML Builder — Type-Safe HTML

Compose HTML as values with trailing closures — auto-escaped, no string concatenation.

let doc = div([cls("card")]) { h1 { text("Notes") } + ul { li { text("Buy milk") } + li { text("Ship 0.16") } } }; let html = doc.render();

Clutch — CLI Parser

CLI argument parsing with subcommands, flags, and options.

let app = Command("myapp", about: "My CLI tool", version: "1.0.0") .argument(flag: "verbose", short: "v", about: "Enable verbose output") .argument("output", short: "o", about: "Output file path") .argument(positional: "input", about: "Input file"); let matches = try app.parse(from: getArgv()); let verbose = matches.hasFlag("verbose");

Datetime — Dates & Times

Calendar dates, wall-clock times, durations, and IANA time zones with formatting.

let launch = try ZonedDateTime(year: 2026, month: 7, day: 4, in: TimeZone("America/New_York")!, hour: 9, minute: 30); let fmt: Format = "\(.ShortWeekday) \(.Hour12):\(.Minute) \(.AmPm) \(.TimeZoneName)"; println(launch.formatted(as: fmt)); let review = launch.adding(months: 1, days: 10); println("Elapsed: \(launch.duration(to: review).humanString())");

Crypto — Hashing

Pure-Kestrel cryptographic hashes — SHA-256, SHA-512, MD5, and BLAKE2b — with a streaming API.

let hex = SHA256.hash(data).hexString; var hasher = SHA256(); hasher.update(part1); hasher.update(part2); let output = hasher.finalize().hexString;

UUID — Unique Identifiers

RFC 9562 UUIDs with version-4 random generation, formatting, and parsing.

let id = UUID.v4(); let text = "\(id)"; let parsed = UUID(from: "550e8400-e29b-41d4-a716-446655440000");

SDL — Graphics & Games

SDL2 bindings for windows, 2D rendering, and keyboard input — the backbone of the SDL games above.

var app = SDLApp(title: "Pong", width: 640, height: 480); while running { while let some event = app.pollEvent() { match event { .Quit => running = false, .KeyDown(.Space) => ball.launch(), _ => {} }; }; app.render { (r) in r.clear(color: .black()); r.fill(rect: paddle, color: .white()); }; app.delay(ms: Milliseconds(value: 16)); };

Tooling

Flock — Package Manager

Manages dependencies, builds, and publishing.

[package] name = "my-app" version = "0.1.0" [dependencies] kestrel/perch = "0.1.0" kestrel/talon-sqlite = "0.1.0" flock build flock run flock check

Jessup — Toolchain Manager

Installs and manages Kestrel compiler versions.

jessup install preview jessup default preview jessup list

LSP

What's Next

LLVM Backend

Kestrel currently compiles through Cranelift, which prioritizes fast compile times over runtime performance. An LLVM backend will bring real optimizations — inlining, loop unrolling, vectorization, and platform-specific tuning. This is where the throughput benchmarks will improve dramatically.

Existential Types

Opaque types (some Protocol) hide the concrete type from the caller. Existential types (any Protocol) go the other direction — they let you store different concrete types behind the same interface. This unlocks heterogeneous collections and dynamic dispatch.

// opaque — caller doesn't know the type, but it's fixed func makeShape() -> some Shape { ... } // existential — can hold any Shape at runtime let shapes: [any Shape] = [Circle(radius: 5), Rect(width: 3, height: 4)];

Escaping Closures

Currently, closures in Kestrel cannot outlive the scope they're created in. Escaping closures will allow closures to be stored, returned, and passed to callbacks — enabling patterns like event handlers, async completion blocks, and builder APIs.

Auto-Derived Protocols

Manually implementing Equatable, Serialize, and Formattable for every struct is tedious when the implementation is mechanical. Derives will let the compiler generate conformances automatically.

@derive(Equatable, Serialize, Formattable) struct User { var name: String; var age: Int64; }

Getting Started

  • How to install
  • Video on building an app with kestrel
  • Joining the community

Community

twitter.com/kestrellang wall.kestrel-lang.com star us on github join the mailing list