Domain
Server — HTTP, databases and auth
eTamil runs backend services: an HTTP server with routing, SQLite and PostgreSQL drivers with parameterised queries only, JSON, bcrypt and JWT.
eTamil is a backend language. A program can serve HTTP, talk to a SQL database, hash passwords and issue tokens — without dropping into Rust.
The HTTP server
etamil --server --port 8080 examples/backend/hello_server.qmz
etamil --async --port 8080 examples/backend/hello_server.qmz
--server runs a worker pool. --async runs a tokio accept loop and hands each
request to the blocking pool, so a connection costs a task rather than a thread
and a slow client no longer occupies one of 2 × cores workers for as long as it
takes to send its request. The VM itself stays synchronous — handlers block, on
tokio’s blocking pool, so every database driver keeps working untouched.
Routes
வழி declares a route; பதில் answers.
வழி பெறு, "/kaNakku/:id" {
பதில் 200, "id=" & param_id & " vakY=" & query_params["vakY"];
}
வழி பதி, "/pativu" {
பதில் 201, "got body: " & request_body;
}
வழி பெறு, "/aRikkY.csv" {
பதில் 200, வரிசைகள், {"Content-Type": "text/csv"};
}
A handler reads its request through plain variables, so the same handler is readable in either spelling:
| Variable | Contents |
|---|---|
request_method |
the HTTP method |
request_path |
the path as requested |
request_body |
the raw body |
query_params |
query string, as a record |
headers |
request headers, as a record |
path_params |
matched :name segments, as a record |
param_<name> |
each path parameter, also bound directly |
Response headers are an ordinary record. Without one, the server answers
application/json.
| Flag | Effect |
|---|---|
--vm |
Run on the bytecode VM (default) |
--check |
Lex, parse and type check only, then stop — reports every error and never runs the program |
--server |
Start the synchronous HTTP server |
--async |
Concurrent server: async accept, blocking handlers, Ctrl-C to stop |
--llvm |
LLVM backend (requires --features llvm; Linux/macOS) |
--port <PORT> |
Server port (default 8080) |
--host <HOST> |
Server host (default 127.0.0.1) |
Databases
Queries are always parameterised. There is deliberately no way to splice a value into SQL text from eTamil, so the injection class of bug is not available to write.
தளம்_இணை எசுகியூஎல்லைட், "kaNakku.db";
தளம்_வினா "SELECT peyar, qokY FROM pativukaL WHERE vakY = ?", ["வரவு"], வரவுகள்;
Rows return as an array of records, so a result set iterates like any other table.
| Engine | Status | Notes |
|---|---|---|
| SQLite | Working | Built in, no feature flag. Decimals cross as text, so no precision is lost |
| PostgreSQL | Working | --features postgres; verified against a live server. Money uses native NUMERIC |
| MySQL / MariaDB | Live verified | --features mysql; the live sample passes with ETAMIL_TEST_MYSQL=1 ./scripts/run_examples.sh. Setup is in TESTING.md |
| MongoDB | Working | --features mongodb. It never went through the Database trait, and should not have: a document is a பொருள், so the mapping needed care about numbers (Decimal128, not doubles) rather than invention |
| Redis | Working | Also outside the trait, for the same reason: Redis is a command and a reply, so the host offers exactly one generic ரெடிஸ்_கட்டளை and every command works — including ones invented after it |
cargo build --release --features postgres,mysql
PostgreSQL placeholders are $1, $2, …; SQLite and MySQL use ?.
தளம்_இணை போச்குரசீகுல், "postgres://user:pass@localhost:5432/kaNakku";
தளம்_வினா "SELECT peyar, qokY FROM pativukaL WHERE vakY = $1", ["வரவு"], வரவுகள்;
Both server backends keep money in the database’s own exact decimal type, so a
text column stays text on the way back — where the SQLite backend, which stores
decimals as text, hands back a number. PostgreSQL also folds unquoted identifiers
to lower case: write "qokY" if you want that column name back as you spelled it.
Connections are reused
தளம்_இணை no longer reconnects on every request. It borrows from a process-wide
idle cache, which is the difference between a handler that pays a TCP and TLS
handshake each time and one that does not. Leases are exclusive, so a transaction
still gets a connection to itself and cannot interleave with another request’s
work. ETAMIL_DB_IDLE caps how many stay warm.
Still to do. Transactions as a language construct, and more than one distinct database open at a time — the VM refuses the second rather than guessing which one you meant.
JSON
jEcAZ.qmz is written in eTamil, not in the host. A JSON parser needs to build a
record whose field names come from the data, and the VM already allows that:
பொருள்[சாவி] = மதிப்பு computes the key at runtime.
இறக்கு "nUlakam/jEcAZ.qmz";
ப = மதிப்பு(ஜேசான்_படி(request_body));
அச்சு ப["qokY"] + 1; // a number, not text
பதில் 200, ஜேசான்_ஆக்கு({நிலுவை: 1500});
ஜேசான்_படி returns சரி/தவறு, so malformed input is handled rather than
guessed at. Record fields serialize in sorted order, which makes a response body
stable enough to assert on. \uXXXX escapes are not decoded.
ஜேசான்_உரை is not implemented. It parses, but the VM refuses it. Build the body
with ஜேசான்_ஆக்கு and send it with பதில்.
Calling other services
வலை_பெறு, வலை_பதி and வலை_அனுப்பு make outbound HTTP requests, behind
--features http-client, which is on by default. A non-2xx response comes back as
an ordinary result rather than a failure, so a 404 from a payment gateway is
something you branch on, not something that unwinds your handler.
ப = வலை_பெறு("https://api.example.in/rates");
(சரியா(ப)) எனில் {
அச்சு மதிப்பு(ப).உடல்;
}
Signing and verifying webhooks
கையொப்பம் produces an HMAC-SHA256 signature and கையொப்பம்_சரியா checks one.
The comparison is constant-time, so it does not leak the expected signature one
byte at a time to anything measuring how long the check took.
வழி பதி, "/webhook" {
(கையொப்பம்_சரியா(request_body, headers["X-Signature"], ரகசியம்)) எனில் {
// …handle the event
பதில் 200, "ok";
}
பதில் 401, "bad signature";
}
Bytes, base64 and hex
பைட்டுகள் turns text into bytes and பைட்டுச்_சரம் turns them back. A byte
array is an ordinary array of numbers — deliberately not a new value type, so
every array function in nUlakam/aNi.qmz already works on it.
Encoding lives in nUlakam/kuRiyAkkam.qmz, written in eTamil like the rest:
அறுபத்துநான்கு_ஆக்கு and அறுபத்துநான்கு_படி for base64,
பதினாறு_ஆக்கு and பதினாறு_படி for hex.
Scheduled work
இடைவெளி runs a block on a timer under either server — a reconciliation sweep, a
retry queue, a nightly close.
இடைவெளி 3600 {
அச்சு "hourly reconciliation";
}
The number is the gap between runs, not a fixed period. If one run takes longer than the interval, the next starts late rather than starting on top of the one still going — which for a job that posts ledger entries is the difference between late and wrong.
Authentication
bcrypt and JWT live in the host, because hashing and HMAC-SHA256 need bytes and randomness the language cannot reach:
கடவுச்சொல்_மறை · கடவுச்சொல்_சரியா · சீட்டு_ஆக்கு · சீட்டு_சரிபார்
Set ETAMIL_JWT_SECRET before starting the server. Everything above these four —
who a user is, which route needs which role — stays in eTamil, and a token’s
payload crosses the boundary as JSON text that jEcAZ.qmz reads and writes.
Examples
etamil --server --port 8080 examples/backend/hello_server.qmz
etamil --server --port 8080 examples/backend/user_server.qmz
etamil --server --port 8080 examples/api/vari_cEvY.qmz
etamil --vm examples/db_samples/kaNakku_qaLam.qmz
etamil --server --port 8080 examples/kadai/kadai_cEvY.qmz
examples/kadai/ is the fullest thing in the repository: an eCommerce backend
with a catalogue, per-line GST, atomic orders, a signed payment webhook, and the
same orders posted through to the double-entry ledger.
examples/db_samples/mYcIkul_qaLam.qmz checks what is worth checking on a real
MySQL server — exact DECIMAL sums, an integer key bound from a number, dates as
ISO text, NULL as இன்மை, and an injection payload staying inert. It now
passes against a live server. It still needs a database, so the example runner
skips it unless you opt in:
ETAMIL_TEST_MYSQL=1 ./scripts/run_examples.sh