| Engine | Type | Best for | Key feature |
|---|---|---|---|
| Lua | Server-side | Dynamic apps, APIs, custom logic | Full Lua scripting with REST API access |
| Native | No-code | Blogs, forums, file sharing, CMS | Pre-built apps, one-click install |
| Static | Static | SPA, landing pages, git deploy | 9-layer pipeline, GitHub integration |
| ID | Name | Engine | Use |
|---|---|---|---|
| 0 | TwigHTML | Any | HTML, CSS, JS, Twig templates — general-purpose page logic |
| 29 | LuaScript | Lua only | Server-side Lua with full platform API access |
| Codes per page | 1,000 |
| Code content size | 5 MB |
| Position range | 1–1,000 (lower = runs first) |
| Permission model | Linux-style perm (0–63). Bits 0–2 = others, bits 3–5 = group. null = unrestricted. Owner + Admin always full access. |
/v1/* endpoints.| auth | HTTP Basic Auth. realm, users, paths. Returns 401. |
| redirects | 301/302/307/308 wildcard redirects with $1,$2 captures. |
| trailing_slash | Silent normalize: keep | add | remove. |
| rewrites | Internal path transform via :name, *, **. No HTTP redirect. |
| proxy | Reverse proxy to external URL. Exact first, then longest prefix. 30s timeout. |
| object | Inline content with :variable interpolation. Status/headers override. |
| map | Path → file reference (md5:fileId, UUID, bare MD5). 3-tier storage. |
| directory_listing | HTML directory index when enabled. |
| 404 | Custom error_page or built-in HTML fallback. |
| Key | Type | Default | Description |
|---|---|---|---|
| git | object | — | {repo, branch, auth_token} — GitHub source |
| spa | bool | false | All unmatched routes → index.html (React/Vue/Svelte) |
| clean_urls | bool | false | /about → /about.html |
| index | string | index.html | Default directory index file |
| strip_prefix | string | — | Strip dir prefix from ZIP paths (e.g. "dist/") |
| template | string | — | ZIP template in md5:fileId format |
| headers | object | — | Path-pattern response headers. Overrides built-in Cache-Control |
| security | object | — | {hsts, x_content_type_options, x_frame_options, referrer_policy, permissions_policy} |
| content_types | object | — | MIME overrides per extension |
| error_pages | object | — | Custom error pages per status code |
| ETag / 304 | CRC32-based ETag on every response |
| Range / 206 | Range: bytes=N-M partial content |
| HEAD support | Full headers without body |
| Cache-Control | HTML: max-age=0, hashed: immutable, other: 86400 |
| Auto MIME | 30+ MIME types auto-detected from extensions |
| Tracing | Add ?__trace=1 to any URL. Fetch at /_wapka/trace?id=X |
framework() | → app — create framework app object |
validator(data, rules) | → validator — validation object |
csrf_token() | → token — generate CSRF token & set cookie |
loadstring(code) | → function — parse Lua string executable |
print(msg) | write to debug buffer (APP_DEBUG only) |
dump(value) | pretty-print value to debug buffer |
app:get("/path", fn) | register GET route |
app:post("/path", fn) | register POST route |
app:put("/path", fn) | register PUT route |
app:patch("/path", fn) | register PATCH route |
app:delete("/path", fn) | register DELETE route |
app:any("/path", fn) | match any HTTP method |
app:group("/prefix", fn) | group routes under prefix |
app:url("name", params) | generate URL from named route |
app:use(middleware) | register global middleware (string | function) |
app:on("event", fn) | register hook: before | after | error |
app:mount("/path", zipKey) | serve ZIP bundle as static files |
app:set_template("n", "code") | define inline Twig template |
app:auto_run(false) | disable auto-run; must call app:run() |
app:run() | start the app manually |
ctx:render("tpl", data) | render Twig template with data |
ctx:json(data) | send JSON response |
ctx:html("str") | send raw HTML response |
ctx:text("str") | send plain text response |
ctx:status(code) | set HTTP status (chainable) |
ctx:ok(data) | pass data through unchanged |
ctx:error("msg", code) | return {_error=true, message, status} |
ctx:redirect("url", code) | HTTP redirect (default 302) |
ctx:set_header("k","v") | set response header (chainable) |
ctx:set_cookie("k","v", opts) | set cookie. opts: {max_age} |
ctx:get_cookie("k") · :has_cookie · :delete_cookie | cookie helpers |
ctx:flash("k", "v") | set/get flash message (survives redirect) |
req.method | "GET", "POST", ... |
req.path · req.uri · req.url | request path / full URI / complete URL |
req.get (alias: query) | parsed query params table |
req.post | parsed POST body (form data) |
req.body | raw request body string |
req.headers | all request headers (lowercased keys) |
req.cookie | request cookies table |
req.files | uploaded file data table |
req.args | raw query string |
req.session | session data (read-only) |
env.siteid · env.sitename | site ID / subdomain name |
env.remote_ip | visitor IP address |
env.is_user · env.is_admin | logged in? / site admin? |
env.timezone | server timezone string |
"cors" | adds CORS headers |
"json" | parse JSON body → ctx.body |
"form" | parse form body → ctx.body |
"auth" | require logged-in user (401) |
"admin" | require site admin (403) |
"csrf" | validate CSRF token on mutations |
v:passes() · v:fails() | check result |
v:errors() | → {field = "message"} |
v:first() | → first error string |
v:valid() | → only validated fields (safe) |
required · string · number · email · boolean · table · min:N · max:N · in:val1,val2 · regex:pattern · nullable
http.get(url, opts) · http.post · http.put | HTTP methods |
http.patch(url, opts) · http.delete · http.head | HTTP methods |
http.request("method", url, opts) | any HTTP method |
opts: {body, headers, timeout, user_agent, verify_ssl} → {status, body, headers} | |
url.encode(str) · url.decode(str) | form-urlencode / decode |
url.rawencode(str) · url.rawdecode(str) | RFC 3986 encode (%20) / decode |
url.parse(url) | → {scheme, host, port, path, query} |
url.build(parts) | build URL from parts table |
url.slug(str) | → "hello-world" |
url.redirect(url) | send 302 redirect immediately |
hash.md5 · sha1 · sha256 · sha512 | hex hash functions |
hash.crc32(str) | → number · fast but not secure |
hash.hmac(key, data) | HMAC-SHA256 → hex |
hash.random(bytes) | → secure random hex (default 32 bytes) |
hash.equals(a, b) | timing-safe comparison |
hash.uuid() | → RFC 4122 UUID v4 string |
str.split(str, sep) · str.trim(str) | split by separator · trim whitespace |
str.contains(str, sub) · starts_with · ends_with | substring checks |
str.limit(str, n, ending) | truncate to n chars (ending default "...") |
str.replace(str, old, new) | find and replace all occurrences |
str.lcfirst · ucfirst · ucwords | case transforms |
| log | |
log.info(msg) · error · warn | always write |
log.debug(msg) · should_debug() | only when APP_DEBUG=true |
| encoder | |
encoder.json(v) · base64 · base36 · hex · rot13 | encode Lua value |
| decoder | |
decoder.json(str) · base64 · base36 · hex · rot13 | decode to Lua value |
eq (default) · neq · gt · gte · lt · lte · in · nin · like · regex · null · contains · exists
Use ["or"] = {{...}, {...}} for OR conditions.
api.dataset.find("col", filter) | → {items, total} |
api.dataset.get(id) | → table | nil |
api.dataset.create("col", data) | → table |
api.dataset.update(id, data, replace) | replace=true for full replace |
api.dataset.delete(id, permanent) | soft (default) or hard |
api.dataset.restore(id) | → table |
api.dataset.collections() | → string[] |
api.dataset.purge_collection("name") | admin only |
api.users.get(id) | → table | nil |
api.users.list(filter) | {type, level, search, page, limit} |
api.users.create(data) | → table |
api.users.update(id, data) | partial update |
api.users.login("user", "pass") | → table |
api.users.online(filter) | → items[] |
api.users.me() | currently logged-in user |
api.users.delete(id) | soft-delete (sets type=4) |
api.users.stats() | → total user count |
| api.sites | |
api.sites.get(id) · api.sites.list(page) | → table | items[] |
api.sites.current() | → {id, name, type, config} |
| api.auth | |
api.auth.session_create(sid, data) · session_check(sid) | OAuth session helpers |
| api.forums | |
api.forums.get(id) · api.forums.list(filter) | {parent, user, search, order, page, limit} |
api.forums.create("name", parentId) · rename(id,"n") · delete(id) | CRUD |
| api.posts | |
api.posts.get(id) · api.posts.list(filter) | {forum_id, user_id, status, search, order, page, limit} |
api.posts.create(data) | {forum_id, title, content, format, status} |
api.posts.update(id, data) · delete(id, hard) · restore(id) | CRUD |
api.messages.get(id) | → table | nil |
api.messages.send(to, "content") | by user ID or username |
api.messages.chat(uid, page, limit) | → messages with user |
api.messages.conversations() · api.messages.unread() | partners list · count |
api.messages.edit(id, "content") · delete(id) | CRUD |
| api.files | |
api.files.get(id) | → {url, cdn_url, thumb_url, ...} |
api.files.list(filter) | {folder_id, search, order, page, limit} |
api.files.upload(data) · import("url", folderId) | multipart · from URL |
api.files.rename(id, "n") · delete(id) | CRUD |
| api.folders | |
api.folders.get(id) · api.folders.list(parentId) | → table | items[] |
api.folders.create("n", parentId) · rename(id,"n") · delete(id) | CRUD |
| api.pages | |
api.pages.get(id) · api.pages.list(filter) | {search, order, page, limit} |
| api.codes | |
api.codes.get(id) · api.codes.list(pageId) | → table | items[] |
api.codes.create(pageId, data) | {content, type, position} |
api.codes.update(id, data) · delete(id) | CRUD |
api.codes.copy(codeId, pageId) · move(codeId, pageId) | copy to page · move to page |
api.codes.up(codeId) · api.codes.down(codeId) | move in execution order |
{{ variable }} | auto-escaped output |
{{ variable|raw }} | unescaped output (trusted content) |
{% set var = value %} | set variable |
{% if condition %} ... {% endif %} | conditional |
{% for item in items %} ... {% endfor %} | loop |
{# comment #} | comment (not rendered) |
if · elseif · else · endif · for · endfor · set · include · extends · block · endblock · macro · endmacro · import · from · spaceless · endspaceless · verbatim · endverbatim · apply · endapply · autoescape · endautoescape · flush
upper · lower · capitalize · title · trim · raw · escape · e · length · reverse · first · last · join · split · sort · date · date_modify · format · replace · number_format · abs · round · ceil · floor · json_encode · url_encode · striptags · nl2br · slice · default · keys · batch · column · filter · map · reduce · find
| Functions | |
range(low, high) · cycle(values, name) · date(format) | helpers |
max(v1, v2, ...) · min(v1, v2, ...) · random(n) | math |
attribute(obj, field) · block(name) | dynamic access |
template_from_string(str) | inline template |
| Loop Variables | |
loop.index · loop.index0 · loop.first · loop.last · loop.length | iteration info |
{% extends "layout" %} | parent template |
{% block name %} ... {% endblock %} | block override |
{% include "partial" %} | include sub-template |
{% include "partial" with {key: value} %} | include with variables |
{% macro name(params) %} ... {% endmacro %} | define reusable macro |
{% from "macros" import name %} | import macros from another template |
{{ csrf_token() }} | generate CSRF token (hidden input value) |
{{ flash("key") }} | get + consume flash message |
{% if has_flash("key") %} ... {% endif %} | check flash without consuming |