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 Beta | Kestrel 0.16 | Node | Go | |
|---|---|---|---|---|
| Deploy Size | 1.9 MB | 2.3 MB | 17.2 MB | 12.4 MB |
| Baseline Memory | 3.0 MB | 2.7 MB | 59.8 MB | 12.2 MB |
| Startup Time | 23ms | 9ms | 120ms | 28ms |
| GET throughput* | 689 req/s (cached) | 260 req/s (uncached) | 3012 req/s | 3557 req/s |
| POST throughput* | 120 req/s | 1800 req/s | 2178 req/s | 1608 req/s |
| Memory after 1200 reqs | 23.3 MB | 3.0 MB | 83.8 MB | 19.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