Minimal runtime dependencies · Single binary

A scripting language built for the web.

More time to build, everything in one place. LOOK is a scripting language that carries the database, sessions, WebSocket, email and more in its core — so you spend your time building features, not wiring tools together. No packages to install, no dependency tree to resolve: one static file, and a complete workspace for the developer.

1 static binary 247 built-in functions 32 modules Apache-2.0
app.lk
// Fetch a user by ID — embedded database, zero setup
route("GET", "/users/:id", function($req) {
    $id   = int(request::param("id"))
    $user = db::query(app::db(), "SELECT * FROM users WHERE id = ?", [$id])

    if ($user == null) {
        return response::error(404, "not found")
    }
    return response::json($user)
})
10,558/s
requests/sec — c=200, 0 errors
3–5×
less RAM used
1
static binary · glibc ≥ 2.28
C++23
dual engine: bytecode VM + tree-walk

Why did web development get this complicated?

A simple API pulls in dozens of packages, a dependency tree, a separate database driver, yet another service for real time... LOOK collapses that stack into a single file.

The traditional web stack

  • Install a runtime, a version manager, open a package manager
  • Hundreds of transitive dependencies — each its own security surface
  • A separate driver for the DB, a separate service for real time
  • Install a framework, configure, update, break, reinstall
  • Deploy = container + orchestration + "works on my machine"

With LOOK, one file

  • No dependency manager — the core library lives inside the binary
  • No transitive dependencies → no supply-chain risk
  • MySQL/PG/SQLite/Redis + WebSocket + SSE embedded in the language
  • No framework to install — routing is part of the language
  • Deploy = copy the file, run it. One binary, three modes.

LOOK is open source (Apache-2.0) and development happens entirely in the open on GitHub — read the source, open an issue, contribute.

Everything is inside. The box isn't empty.

Most languages say "batteries included," then leave you alone with a package manager. LOOK actually includes them.

Embedded databases

MySQL, MariaDB, PostgreSQL, SQLite and Redis (RESP2) — all in the core with a hand-written wire protocol. No libpq/hiredis.

Dual engine, for speed

The default bytecode VM is ~41× faster than the tree-walking interpreter on the CLI. On the web, a route-level safety-net fallback.

Real-time, built in

WebSocket and Server-Sent Events are embedded in the language — broadcast with ws:: / sse:: in one line.

Embedded SMTP + IMAP

No external service needed to send email. A built-in SMTP server with STARTTLS, plus an IMAP server.

Safe defaults

An undefined variable is an error (strict). Type-strict ==. 64-bit integers — phone numbers/IDs don't silently corrupt.

One portable file

A static binary built on AlmaLinux 8 runs unchanged on Ubuntu, Debian, Rocky, RHEL 8·9·10+. Or just docker run codlook/look.

One binary, a full runtime.

Just start writing — nothing to install, no new ecosystem to learn.

Core

Routing in the language

route("GET","/product/:id", fn) — REST, WS, SSE share one syntax. URL parameters are parsed automatically.

Syntax

Short lambda — fn + arrow

fn($x) => $x * 2 — an expression-bodied arrow function with implicit return. Classic function works too.

Syntax

app:: service registry

app::set("db", $conn) once → routes reach it with app::db() without capturing. The explicit-scope philosophy is preserved.

Core

MySQL · SQLite · PostgreSQL

The wire protocol, directly. One API: db::query / exec / col. Transactions: db::begin / commit / rollback.

Concurrency

parallel() + channel()

LOOK's task model. Fan-out, pipeline, producer-consumer — over channels. No blocking, no callbacks.

Real-time

WebSocket

route("WS","/chat", fn) — RFC 6455, SHA-1+Base64 handshake. ws::broadcast() to every client.

Real-time

SSE + timer::

route("SSE","/events", fn) — Server-Sent Events. Periodic push: timer::every(3000, fn).

Background

jobs:: — job queue

SQLite-backed durable queue. jobs::push / worker / run. Delayed jobs, retries — survives a process restart.

Queue

queue:: — FIFO queue

In-memory, thread-safe, named queue. queue::push / pop / peek / size.

Cache

cache:: — in-process cache

TTL-aware, thread-safe singleton. Shared across all workers. cache::get / set / has / flush.

Template

template:: — view engine

Layout inheritance, partial include, {#each} loop, {#if} condition. Server-rendered — no runtime.

VM

Bytecode VM

AST → register bytecode → switch dispatch. The default engine everywhere — ~41× faster than tree-walking on the CLI.

Security

crypto:: + auth:: + JWT

sha256, hmac_sha256, rs256_sign/verify, base64url, UUID v4, timing-safe constant_compare — no external deps. Passwords: auth::hash (PBKDF2). JWT builds on this.

Security

Rate limiter — token bucket

Two layers: per-IP burst + global cap. LOOK_RATE_LIMIT_* — your code doesn't change, it's at the runtime level.

Security

DKIM + SMTP hardening

Outbound email is signed RSA-SHA256 automatically. Port 25 relay is off — authenticated SMTP only (587/465).

Mail server

Embedded SMTP + IMAP

Built-in MTA (25/587) + IMAP4rev1 (143/993). SMTP → Maildir, IMAP → Thunderbird/Roundcube. No Dovecot/Postfix needed.

Error handling

error:: — typed errors

Throw with throw; error::new($type,$msg) for structured errors. try/catch + error::is / code / message.

Module

http:: — outbound HTTP client

GET/POST/PUT/PATCH/DELETE. JSON body, custom headers, timeout, TLS included. Fan-out API calls with parallel().

Ecosystem

Package + module system

lk install github.com/... — JWT, payments and more. No registry, no config. Packages →

Developer

test + REPL + lk install

lk test — assertions, before/after_each. lk repl — interactive. Install packages from GitHub.

Validation

validator:: — input validation

Email, number, integer, required field, min/max rules. Secure user input in a route with a single call.

All 32 modules · 247 functions → Docs

When should you reach for LOOK?

It's not a framework — it's a language. If one of the needs below is yours, LOOK fits.

🌐

Simple hosting, real-time power

You want the drop-a-file, shared-hosting / Plesk simplicity — and also WebSocket, SSE and concurrency. Drop the file, run it, and you get real-time connections and parallel() on top.

🚀

REST + DB without a framework

Routing, DB (MySQL/SQLite/PostgreSQL), validation, cache, JWT — all in the language. No dependency manager, no package tree — one binary.

📦

Single-file deploy

Copy one file to a VPS, run it with systemd/Plesk. No separate runtime, no package manager. 28 MB RAM, flat across 1M requests.

✉️

Building a mail system

Built-in SMTP + IMAP server — no Postfix/Dovecot. Build a secure mail system, e-commerce notifications or a blog entirely in LOOK. How? ↓

Write a route, return JSON. That's it.

One-line endpoints with arrow functions, or a full function body. Request data via request::, response via response:: — no middleware layer.

  • Path parameters: /users/:id
  • Parameterized SQL — injection-safe
  • No semicolons, no ceremony
routes.lk
// One line — arrow function
route("GET", "/", fn($req) => response::json(["hello" => "world"]))

// Submit a form — POST body
route("POST", "/signup", function($req) {
    $body = request::json()
    if (!validator::email($body["email"])) {
        return response::error(400, "invalid email")
    }
    db::exec(app::db(), "INSERT INTO users(email) VALUES(?)", [$body["email"]])
    return response::json(["ok" => true], 201)
})
chat.lk
// Real-time chat — 5 lines
route("WS", "/chat", function($conn) {
    ws::on($conn, "message", function($m) {
        ws::broadcast($m)          // send to everyone
    })
})

// Live event stream (SSE)
route("SSE", "/prices", function($conn) {
    sse::send($conn, json::encode($tick))
})

Real-time, without a library.

No Socket.io, no separate service. WebSocket and SSE are part of the language. ws::broadcast() reaches every connected client in one call.

  • ws::on / send / broadcast / clients
  • sse::send / on / close
  • Timers: timer::every / after

What frameworks do, the language itself does.

A template engine, background jobs, a queue, Redis, even AI — normally each needs a separate library/service. We built LOOK to make the developer's job easier; it's all in the core.

template::

A template engine — in the language.

Which language ships a template engine in the box? LOOK has layout inheritance, partial includes, {#each} / {#if} — server-rendered, no runtime. Automatic XSS protection via template::escape.

products.lk
// page.html: {#each products as p}<li>{$p.name}</li>{/each}
$html = template::render("page.html", [
    "title"    => "Products",
    "products" => db::query(app::db(), "SELECT * FROM products", [])
])
return response::html($html)
jobs.lk
// Don't make the request wait — enqueue, return instantly
jobs::push("mail", ["to" => $email, "subject" => "Welcome"])
return response::json(["ok" => true])

// Separate worker — durable (SQLite), survives a restart
jobs::worker("mail", function($job) {
    mail::send($job["to"], $job["subject"], $body)
})
jobs::run()
jobs:: · queue::

Background jobs, without losing them.

Don't make slow work (mail, reports, webhooks) block the request — enqueue it and let a worker chew on it in the background. jobs:: is a SQLite-backed durable queue: delayed jobs, retries, dead-letter. queue:: is a fast in-memory FIFO between requests.

parallel() + channel()

Concurrency at the language level.

Run 3 DB queries at once in a single request — total time equals the slowest query, not the sum of all three. Go's channel model, in LOOK syntax: parallel() starts a task, channel() carries the result back. There's blocking (natural backpressure), no callback hell.

Every HTTP request already runs on its own worker thread — --workers N, no code changes. An isolated interpreter copy + a connection pool.

dashboard.lk
// 3 queries AT ONCE — time = the slowest query
$result = channel(3)
parallel(function() use ($result, $db) {
    $n = db::query($db, "SELECT count(*) n FROM companies", [])
    send($result, $n[0]["n"])
})
parallel(function() use ($result, $db) {
    $n = db::query($db, "SELECT count(*) n FROM orders", [])
    send($result, $n[0]["n"])
})
$companies = receive($result)
$orders    = receive($result)
cache:: + Redis

Connecting to Redis = one env var.

No client to install, no driver to pick. Set LOOK_REDIS_URLcache:: speaks RESP2 itself. Leave it unset and it runs in-memory. The code stays the same.

cache.lk
# env: LOOK_REDIS_URL=redis://127.0.0.1:6379/0
cache::set("stock:42", $stock, 300)   // 300s TTL
$stock = cache::get("stock:42")
ai — official module

The Anthropic API in two lines.

The official ai module is built on the core http::stream. Ask Claude a question, stream it token by token — the key lives in a single env var.

ai.lk
use ai   // lk module install github.com/codlook/look-modules/ai

$reply = ai_chat([
  ["role" => "user", "content" => "Describe LOOK in one sentence"]
], ["model" => "claude-sonnet-5"])

print($reply["content"])

How deep does it go?

Every component of LOOK is written from scratch in C++23 — no drivers, no ORM, no middleware.

Parser

Lexer · Parser · AST

Precedence-aware parser, full operator set, string interpolation, error reporting with line/column positions.

VM

Bytecode VM

AST → 3-address register bytecode → switch dispatch. 41× faster than tree-walking on the CLI. Safety net: tree-walk fallback.

Concurrency

ThreadPool + ConnPool

--workers N, a per-request interpreter copy, a per-DSN connection pool. Hot reload: zero downtime.

Networking

HTTP/1.1 · WebSocket · SSE

epoll (Linux), IOCP (Windows). RFC 6455 WS, an SSE frame codec. Optional fiber runtime (Go netpoller model).

DB

MySQL · SQLite · PostgreSQL

Wire protocol from scratch. No driver. One db:: API, different DSNs — only the connection string changes.

Deploy

CGI · FastCGI · HTTP

Apache/nginx FastCGI, standalone HTTP, CGI fallback. One binary, three modes.

Real numbers, a real server.

Measured with ab on a live AlmaLinux 8 · MariaDB server (64 workers, direct port).

10,558
req/sec
HTTP + VM router
c=200, 0 errors
1M
requests
Endurance run
c=10,000, 0 errors
41–51×
faster
Bytecode VM speedup
CLI: 3652 ms → 72 ms
28
MB
Memory — flat
didn't grow across 1.75M requests

So why is it this frugal with RAM?

The classic model spawns a separate process per request/worker — each tens of MB. LOOK is one process + N worker threads: memory is shared, and each request only takes a lightweight isolated interpreter copy. The result: a ~8 MB idle baseline (~8.5 MB holding 200 open keepalive connections), and under sustained high-throughput load it holds flat at ~28 MB across 1.75M requests — leak-free, verified with valgrind/heaptrack. With --workers 2 it runs stably without OOM even on a 1 CPU / 1 GB RAM VPS.

All tests measured with ab on a live AlmaLinux 8 server (running apps: test.codlook.com). Throughput is flat from c=1,000 to c=20,000 (~7.5–10k) — the bottleneck is CPU; kernel walls (backlog, syn_backlog) were cleared with tuning. The TLS chain (nginx→FCGI) does ~1,100–1,240 req/sec; the difference is double TLS termination, not LOOK.

Proven resilience.

Every line is tested — it passes fuzzing, ThreadSanitizer and data-integrity tests.

16,000+
Fuzzing rounds
Random/malformed HTTP bombardment under ASan + UBSan — 0 crashes, 0 UB.
0 races
ThreadSanitizer clean
Concurrent-dispatch data-race scan. The one race found was fixed.
20,000
Cross-leak test
Every request a unique token + DB round-trip. 0 data leaks.
Zero
Supply-chain risk
MySQL/PG/SQLite/Redis wire protocols from scratch in C++23. No third-party packages.
4 rounds
Manual security audit
Protocol parsers, VM arithmetic and network paths reviewed line by line.
ConcernLOOK's protection
SQL injectiondb::query($conn, sql, [params]) — automatic parameterized ?, driver-correct escaping
DoS — huge bodyExceeding LOOK_MAX_BODY_SIZE (10 MB) → 413; unbounded-body memory exhaustion is prevented
Request smugglingContent-Length + Transfer-Encoding together → 400 (RFC 7230 §3.3.3)
Session security128-bit /dev/urandom token; cookie HttpOnly + Secure + SameSite
Password storageauth::hash / verify — PBKDF2-SHA256, in the core
Upload securityMagic-byte type validation (blocks extension spoofing) + SHA-256
WebSocket maskingAn unmasked client frame → closed with 1002 (RFC 6455 §5.1)
Parser stack overflowDeeply nested ((…)) → expression/statement depth guard (150) — segfault prevented
Arithmetic UB64-bit integers; signed-overflow/shift/unary-minus UB eliminated — UBSan clean. BIGINT/ID/phone round-trips exactly

So how did we solve these?

Security isn't a layer bolted on afterward — because we wrote the protocols ourselves, we control every byte.

The protocols are ours, from scratch

We wrote the MySQL, PostgreSQL, SQLite, Redis, HTTP, SMTP and IMAP wire protocols ourselves in C++23. No third-party driver → no hidden vulnerability in an unknown dependency. A malformed server response can't crash a worker — every number conversion and allocation is bounded.

Sanitizers are mandatory in CI

Before every build, CI runs 16,000+ rounds of malformed-HTTP fuzzing under AddressSanitizer + UndefinedBehaviorSanitizer and a ThreadSanitizer data-race scan (security.yml). The one race found was fixed. Memory is flat across 1.75M requests — no leaks.

4 rounds of manual audit

Beyond fuzzing: protocol parsers, VM arithmetic and network paths were reviewed line by line. Every hardening point found — an unguarded number conversion, an unbounded allocation, signed-overflow UB — was closed and pinned to a test. An external independent audit also caught real CRITICAL findings, all resolved.

Spotlight

Embedded Mail Server — SMTP + IMAP

Most scripting languages send mail; LOOK serves it. A built-in SMTP (RFC 5321) and IMAP4rev1 (RFC 3501 + IDLE) server — no Postfix/Dovecot. You can build a secure mail system end to end entirely in LOOK.

# Receives SMTP → Maildir → serves IMAP (one command) SMTP :25/:587 ─deliver─▶ <LOOK_MAIL_DIR>/<user>/inbox ◀─reads─ IMAP :143/:993 lk --mode http --port 7400 # HTTP + SMTP + IMAP + IMAPS together
STARTTLS + IMAPS (993) SEARCH · FETCH · APPEND IDLE — live push PBKDF2 authentication ASan+UBSan fuzz: 0 UB

Thunderbird, Roundcube, Apple Mail and mobile clients connect directly — list, read, compose, search, get live notifications. All over TLS.

32 modules, 247 functions.

From text to cryptography, from queues to templates — the core library comes ready.

db::
MySQL · PG · SQLite · query, transaction
request::
get, post, json, param, header, ip
response::
json, error, redirect, html
string::
split, trim, replace, contains… (24)
array::
map, filter, reduce, sort… (20)
math::
sqrt, pow, round, random… (14)
crypto::
sha256, hmac_sha256, rs256, base64url, uuid
auth::
hash, verify — PBKDF2 passwords
session::
start, get, set, destroy
http::
get, post, stream — external APIs
mail::
send — embedded SMTP
ws::
WebSocket connections
sse::
Server-Sent Events
cache::
in-memory cache
queue::
job queue
jobs::
background jobs, worker
validator::
email, numeric, required, min, max
See all modules →

Up and running in seconds.

The official Docker image and VS Code extension are live — or install the static binary on your server.

One portable binary for glibc ≥ 2.28: AlmaLinux/Rocky/RHEL 8·9·10, Ubuntu 18.10→24.04, Debian 10+ — future releases work automatically too. CentOS 7 / RHEL 7 (glibc 2.17, EOL) is not supported → move to AlmaLinux 8.

🐳 Docker — no compilation ● live on Docker Hub

The fastest path from a clean machine. The official image is live on Docker Hub — the first run pulls it automatically (~43 MB).

1

Run your app — the ./app folder is mounted into the container

$docker run -p 7400:7400 -v "$PWD/app:/app" codlook/look
2

Drop in ./app/index.lk, open http://localhost:7400/ in the browser

Image page: hub.docker.com/r/codlook/look · Build from source: docker build -f cpp/docker/Dockerfile.production -t look .

🐧 Linux server

The same package for Ubuntu/Debian and AlmaLinux/Rocky/RHEL — one portable binary.

1

Download and unpack the package from GitHub Releases

$unzip look-lang-linux-*.zip -d look && cd look
2

One command — installs the binary, creates a sample app + systemd service, starts it

$sudo bash install.sh
3

Verify

$curl http://127.0.0.1:9000/  ·  systemctl status look

Pick a custom port with LOOK_PORT=8080 sudo bash install.sh. The service is managed by systemd — systemctl restart look.

🔌 Plesk extension

Install from the panel, attach LOOK to domains in one click — a full workspace with an in-browser code editor, live monitor and log viewer.

1

Open the Plesk Terminal: Server Management → Terminal (or ssh root@server_ip)

2

Download and install the ZIP — at the end you'll see "The extension was successfully installed."

wget -O /tmp/look-lang-plesk-1.0.0.zip "https://github.com/codlook/look/releases/download/v1.0/look-lang-plesk-1.0.0.zip" plesk bin extension --install /tmp/look-lang-plesk-1.0.0.zip
3

Finish the install — one root command (required on new servers)

$plesk php /usr/local/psa/admin/plib/modules/look-lang/scripts/post-install.php
4

Open the panel: Extensions → My Extensions → LOOK Language → Open

5

Applications → Add New Domain → pick the domain (script path and port fill in automatically) → Add & Start. It shows Running in the list within seconds — the systemd service + Apache proxy are set up automatically.

6

Edit index.lk in the browser with Edit CodeSave & Redeploy — the file is written and the service restarts.

Step 3 is required: Plesk doesn't run the extension's install hook; this command installs the binary to /opt/look + grants sudo rights. Skip it and enabling a domain fails with sudo: a password is required. Re-running is safe; it's idempotent. The workspace: a live system dashboard, an in-browser code editor, a per-domain monitor (CPU/RAM/PID/connections) and a journalctl log viewer.

🪟 Windows

A native MSVC build with Schannel TLS — zero external DLLs. LOOK's HTTP server is built in, so no Apache or extra web server is needed.

1

Download look-lang-windows-1.0.0.zip from GitHub Releases and extract it

>Expand-Archive look-lang-windows-1.0.0.zip -DestinationPath look
2

Run the built-in HTTP server — it serves index.lk from the current folder

>cd look; .\lk-fcgi.exe --mode http --port 8080
3

Open http://localhost:8080. Or run a script directly: lk.exe app.lk

The package is lk.exe (CLI / REPL) + lk-fcgi.exe (HTTP / FastCGI server) + README — plain binaries, no installer, no Apache.

🧩 VS Code extension ● live on the Marketplace

Live error checking as you write .lk, autocompletion for 247 functions, hover signatures — the official extension.

1

Install "LOOK Language" from the VS Code Marketplace — or via the command line:

$code --install-extension codlook.look-lang
2

Open a .lk file — errors are underlined in red as you type

Marketplace: marketplace.visualstudio.com/…/codlook.look-lang · If lk isn't on PATH, Settings → look.binaryPath.

VS Code extension

See your error as you type.

The official LOOK extension: live error checking (parse + undefined function), autocompletion for 247 built-in functions, hover signatures, and Run / Service / REPL shortcuts. Live on the VS Code Marketplace.

demo.lk — LOOK Language
$name = "Ada"
print("Hello, " . $name)

mathh::sqrt(16)   ⌇ Undefined function: mathh::sqrt
//        ▲ underlined in red as you type

When the core isn't enough, the ecosystem.

No registry, no config — straight from GitHub. Two separate official repos: modules (pure LOOK) and packages (integrations).

look-modules — ai · jwt
$lk module install github.com/codlook/look-modules/ai
look-packages — iyzico · firebase
$lk install github.com/codlook/look-packages/iyzico

Three rules, never broken.

Every new feature has to serve these three principles — if it doesn't, it doesn't get into the language.

01

Drop it, run it

Drop the file on the server, let the server route it — done. Nothing to install or compile. Shared hosting, VPS, Plesk — the same workflow.

02

Explicit scope

Clean scope, explicit dependencies, concurrency built into the language. use() declares exactly what comes in. No hidden globals, no surprises.

03

No framework to install

Routing, DB, auth, cache, jobs, WebSocket, SSE, templates — all in the language. No package manager to open, no dependency hell.

When you're ready, one command.

Start writing for the web without setup fatigue. Download LOOK, run it, deploy it.