From a761611ad0895eac2ea537b6f90a0ac4d7ac3d3c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 25 Mar 2026 06:17:14 +0000
Subject: [PATCH 1/4] Initial plan
From 4c36af011424999add1718696e309970e89991f1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 25 Mar 2026 06:22:14 +0000
Subject: [PATCH 2/4] Add debug/administration website with Lua terminal and
log viewer
- web/nginx.conf: OpenResty config with API endpoints for Lua code
execution, log retrieval, log clearing, and server info
- web/index.html: Single-page debug console UI with Lua terminal
(code editor + output) and server log viewer with auto-refresh
Co-authored-by: paintdream <7030141+paintdream@users.noreply.github.com>
Agent-Logs-Url: https://github.com/paintdream/ngx_lua_cpp/sessions/f44b2ed1-ba42-480f-9e4a-7035bf3c50ab
---
web/index.html | 674 +++++++++++++++++++++++++++++++++++++++++++++++++
web/nginx.conf | 249 ++++++++++++++++++
2 files changed, 923 insertions(+)
create mode 100644 web/index.html
create mode 100644 web/nginx.conf
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..b01aecb
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,674 @@
+
+
+
+
+
+ngx_lua_cpp Debug Console
+
+
+
+
+
+
+
+ ⚙
+ ngx_lua_cpp Debug Console
+
+
+ Connecting...
+
+
+
+
+
+
+
+
+
+
▶ Lua Terminal
+
+
+
+
+
+
+
+
+
+ Ctrl+Enter to run · Ctrl+L to clear output · Tab to indent
+
+
+
+
Output
+
+
+
+ Output will appear here...
+
+
+
+
+
+
+
+
📜 Server Logs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading logs...
+
+
+
+
+
+
+
+
diff --git a/web/nginx.conf b/web/nginx.conf
new file mode 100644
index 0000000..f21a54d
--- /dev/null
+++ b/web/nginx.conf
@@ -0,0 +1,249 @@
+# ngx_lua_cpp Debug/Administration Server
+# OpenResty nginx configuration
+#
+# Usage:
+# 1. Adjust the paths below to match your environment:
+# - Replace [[ngx_lua_cpp source directory]] with the path to this repository
+# - Replace [[ngx_lua_cpp binary directory]] with the path to the built library
+# 2. Start OpenResty:
+# openresty -p `pwd` -c nginx.conf
+# 3. Open http://localhost:8080 in your browser
+
+worker_processes 1;
+error_log logs/error.log info;
+pid logs/nginx.pid;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ include mime.types;
+ default_type application/octet-stream;
+
+ access_log logs/access.log;
+
+ sendfile on;
+ keepalive_timeout 65;
+
+ # Shared dictionary for storing execution history
+ lua_shared_dict exec_history 1m;
+
+ init_worker_by_lua_block {
+ -- Adjust these paths to your environment
+ -- package.path = package.path .. ";[[ngx_lua_cpp source directory]]/demo/?.lua"
+ -- package.cpath = package.cpath .. ";[[ngx_lua_cpp binary directory]]/?.dll;[[ngx_lua_cpp binary directory]]/lib?.so;"
+ -- require("init_ngx_lua_cpp")
+ }
+
+ server {
+ listen 8080;
+ server_name localhost;
+
+ # Serve static files (index.html, etc.)
+ location / {
+ root html;
+ index index.html;
+ }
+
+ # API: Execute Lua code
+ location /api/exec {
+ default_type application/json;
+
+ content_by_lua_block {
+ ngx.req.read_body()
+ local body = ngx.req.get_body_data()
+ if not body then
+ ngx.status = 400
+ ngx.say('{"ok":false,"error":"Empty request body"}')
+ return
+ end
+
+ local cjson = require("cjson.safe")
+ local data = cjson.decode(body)
+ if not data or not data.code then
+ ngx.status = 400
+ ngx.say('{"ok":false,"error":"Invalid JSON or missing \\"code\\" field"}')
+ return
+ end
+
+ local code = data.code
+
+ -- Capture output by overriding ngx.say/ngx.print within the chunk
+ local output_parts = {}
+ local env = setmetatable({
+ print = function(...)
+ local args = {...}
+ local parts = {}
+ for i = 1, select("#", ...) do
+ parts[#parts + 1] = tostring(args[i])
+ end
+ output_parts[#output_parts + 1] = table.concat(parts, "\t")
+ end,
+ ngx = ngx,
+ require = require,
+ tostring = tostring,
+ tonumber = tonumber,
+ type = type,
+ pairs = pairs,
+ ipairs = ipairs,
+ pcall = pcall,
+ xpcall = xpcall,
+ error = error,
+ assert = assert,
+ select = select,
+ unpack = unpack,
+ table = table,
+ string = string,
+ math = math,
+ os = os,
+ io = io,
+ coroutine = coroutine,
+ collectgarbage = collectgarbage,
+ rawget = rawget,
+ rawset = rawset,
+ setmetatable = setmetatable,
+ getmetatable = getmetatable,
+ }, {__index = _G})
+
+ local fn, err = loadstring(code)
+ if not fn then
+ ngx.say(cjson.encode({ok = false, error = "Compile error: " .. err}))
+ return
+ end
+
+ setfenv(fn, env)
+
+ local ok, result = pcall(fn)
+ local output = table.concat(output_parts, "\n")
+
+ if ok then
+ local res = {ok = true, output = output}
+ if result ~= nil then
+ res.result = tostring(result)
+ end
+ ngx.say(cjson.encode(res))
+ else
+ ngx.say(cjson.encode({ok = false, error = tostring(result), output = output}))
+ end
+ }
+ }
+
+ # API: Retrieve server logs
+ location /api/logs {
+ default_type application/json;
+
+ content_by_lua_block {
+ local cjson = require("cjson.safe")
+ local args = ngx.req.get_uri_args()
+ local log_type = args.type or "error"
+ local lines = tonumber(args.lines) or 100
+
+ local log_file
+ if log_type == "access" then
+ log_file = ngx.config.prefix() .. "logs/access.log"
+ else
+ log_file = ngx.config.prefix() .. "logs/error.log"
+ end
+
+ -- Read log file directly (no shell commands)
+ local f = io.open(log_file, "r")
+ if not f then
+ ngx.say(cjson.encode({ok = false, error = "Failed to open log file"}))
+ return
+ end
+
+ -- Read all lines and keep only the last N
+ local all_lines = {}
+ for line in f:lines() do
+ all_lines[#all_lines + 1] = line
+ end
+ f:close()
+
+ local start = math.max(1, #all_lines - math.min(lines, 10000) + 1)
+ local tail_lines = {}
+ for i = start, #all_lines do
+ tail_lines[#tail_lines + 1] = all_lines[i]
+ end
+ local content = table.concat(tail_lines, "\n")
+
+ ngx.say(cjson.encode({
+ ok = true,
+ log_type = log_type,
+ file = log_file,
+ content = content
+ }))
+ }
+ }
+
+ # API: Clear logs
+ location /api/logs/clear {
+ default_type application/json;
+
+ content_by_lua_block {
+ local cjson = require("cjson.safe")
+ local args = ngx.req.get_uri_args()
+ local log_type = args.type or "error"
+
+ local log_file
+ if log_type == "access" then
+ log_file = ngx.config.prefix() .. "logs/access.log"
+ else
+ log_file = ngx.config.prefix() .. "logs/error.log"
+ end
+
+ local ok, err = pcall(function()
+ local f = io.open(log_file, "w")
+ if f then
+ f:close()
+ end
+ -- Signal nginx to reopen log files
+ local pid_file = ngx.config.prefix() .. "logs/nginx.pid"
+ local pf = io.open(pid_file, "r")
+ if pf then
+ local pid = pf:read("*l")
+ pf:close()
+ if pid then
+ os.execute("kill -USR1 " .. tonumber(pid))
+ end
+ end
+ end)
+
+ if ok then
+ ngx.say(cjson.encode({ok = true, message = log_type .. " log cleared"}))
+ else
+ ngx.say(cjson.encode({ok = false, error = tostring(err)}))
+ end
+ }
+ }
+
+ # API: Server info
+ location /api/info {
+ default_type application/json;
+
+ content_by_lua_block {
+ local cjson = require("cjson.safe")
+
+ local info = {
+ ok = true,
+ ngx_lua_version = ngx.config.ngx_lua_version,
+ nginx_version = ngx.config.nginx_version,
+ worker_pid = ngx.worker.pid(),
+ worker_count = ngx.worker.count(),
+ prefix = ngx.config.prefix(),
+ }
+
+ -- Try to load ngx_lua_cpp info
+ local inst_ok, inst = pcall(require, "init_ngx_lua_cpp")
+ if inst_ok and inst then
+ info.ngx_lua_cpp = true
+ info.ngx_lua_cpp_running = inst:is_running()
+ else
+ info.ngx_lua_cpp = false
+ end
+
+ ngx.say(cjson.encode(info))
+ }
+ }
+ }
+}
From aecb33340240f607c65434fd36bd07c4090fa212 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 26 Mar 2026 05:51:46 +0000
Subject: [PATCH 3/4] Initial plan
From 90fba81ef68a102d9b1547b599d5522ca626268c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 26 Mar 2026 05:58:07 +0000
Subject: [PATCH 4/4] Add doc/design.md: comprehensive design document for
ngx_lua_cpp
Co-authored-by: paintdream <7030141+paintdream@users.noreply.github.com>
Agent-Logs-Url: https://github.com/paintdream/ngx_lua_cpp/sessions/60dd2890-9b07-4c4a-a307-1ed8baef7e37
---
doc/design.md | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 291 insertions(+)
create mode 100644 doc/design.md
diff --git a/doc/design.md b/doc/design.md
new file mode 100644
index 0000000..3ad4c57
--- /dev/null
+++ b/doc/design.md
@@ -0,0 +1,291 @@
+# ngx_lua_cpp Design Document
+
+## 1. Overview
+
+**ngx_lua_cpp** is a C++ extension framework for OpenResty/Nginx that lets developers write high-performance C++ logic — including C++20 coroutines — and expose it to Lua scripts running inside OpenResty's `ngx_lua` module. It is built on the [iris](https://github.com/paintdream/iris) header-only concurrency library and is distributed under the MIT License.
+
+### Goals
+
+- Enable C++ extensions for OpenResty without requiring any nginx or OpenResty header files at build time.
+- Provide seamless Lua ↔ C++ type conversion and function binding via iris.
+- Support C++20 coroutines that integrate transparently with OpenResty's Lua coroutine scheduler.
+- Operate as a self-contained shared library (`.so` / `.dll`) loaded at runtime through LuaJIT's `require`.
+
+## 2. Repository Structure
+
+```
+ngx_lua_cpp/
+├── CMakeLists.txt # Build configuration (CMake, C++20)
+├── LICENSE # MIT License
+├── README.md # User-facing documentation
+├── cmake/
+│ └── FindLuaJIT.cmake # CMake module to locate LuaJIT
+├── demo/
+│ └── init_ngx_lua_cpp.lua # Lua bootstrap script for nginx workers
+├── doc/
+│ └── design.md # This document
+├── src/
+│ ├── ngx_lua_cpp.h # Public header — exported types and macros
+│ ├── ngx_lua_cpp.cpp # Core implementation
+│ └── iris/ # iris library (header-only, vendored)
+│ ├── iris_common.h # Common utilities, allocators, helpers
+│ ├── iris_common.inl # Inline implementations for iris_common.h
+│ ├── iris_lua.h # Lua binding framework (iris_lua_t)
+│ ├── iris_dispatcher.h # Thread pool and warp/strand abstractions
+│ └── iris_coroutine.h # C++20 coroutine integration
+└── web/
+ ├── nginx.conf # Example nginx configuration with debug APIs
+ └── index.html # Browser-based debug console UI
+```
+
+## 3. Architecture
+
+The diagram below shows how the main components interact at runtime inside an OpenResty worker process:
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ OpenResty Worker Process │
+│ │
+│ ┌──────────────┐ require("ngx_lua_cpp") ┌──────────────┐ │
+│ │ ngx_lua / │ ──────────────────────────► │ libngx_lua_ │ │
+│ │ LuaJIT VM │ ◄────────────────────────── │ cpp.so │ │
+│ │ │ Lua ↔ C++ calls │ │ │
+│ └──────┬───────┘ └──────┬───────┘ │
+│ │ │ │
+│ │ ngx.sleep / yield / resume │ │
+│ ▼ ▼ │
+│ ┌──────────────┐ hook process_events() ┌──────────────┐ │
+│ │ nginx event │ ◄─────────────────────────│ ngx_hooker_t │ │
+│ │ loop │ ─────────────────────────►│ (singleton) │ │
+│ └──────────────┘ notify() └──────┬───────┘ │
+│ │ │
+│ ┌───────▼──────┐ │
+│ │ ngx_lua_ │ │
+│ │ cpp_t │ │
+│ │ ┌──────────┐ │ │
+│ │ │async_ │ │ │
+│ │ │worker │ │ │
+│ │ │(thread │ │ │
+│ │ │pool) │ │ │
+│ │ └──────────┘ │ │
+│ │ ┌──────────┐ │ │
+│ │ │main_warp │ │ │
+│ │ │(strand) │ │ │
+│ │ └──────────┘ │ │
+│ └──────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### Runtime Flow
+
+1. **Loading** — The nginx `init_worker_by_lua_block` directive executes `require("ngx_lua_cpp")`, which calls `luaopen_ngx_lua_cpp`. This registers the `ngx_lua_cpp_t` type with iris_lua and constructs the global `ngx_hooker_t` singleton.
+2. **Initialization** — Lua code calls `lib.new()` to create an `ngx_lua_cpp_t` instance and `inst:start(N)` to spin up N worker threads.
+3. **Request handling** — Inside `content_by_lua_block`, Lua calls C++ methods (e.g., `inst:sleep(40)`). If the method is a coroutine, the framework yields the current ngx_lua coroutine, dispatches work to the thread pool, and resumes the coroutine with the result once complete.
+4. **Event loop integration** — `ngx_hooker_t` hooks `ngx_event_actions.process_events` so that every event-loop iteration drains completed async tasks back to the main thread via `main_warp->poll()`.
+
+## 4. Key Components
+
+### 4.1 `ngx_lua_cpp_t` (src/ngx_lua_cpp.h, src/ngx_lua_cpp.cpp)
+
+The primary class exposed to Lua. Each instance owns:
+
+| Member | Type | Purpose |
+|---|---|---|
+| `async_worker` | `shared_ptr>` | Thread pool for background work |
+| `main_warp` | `unique_ptr` | Strand that serializes work back to the nginx main thread |
+| `main_warp_guard` | `unique_ptr` | Keeps `main_warp` in the preempted (accepting tasks) state |
+| `main_thread_index` | `size_t` | Index of the pseudo-thread representing the nginx main thread in the async worker |
+
+**Lua API** (registered in `lua_registar`):
+
+| Lua method | C++ method | Description |
+|---|---|---|
+| `lib.new()` | `place_new_object` | Creates a new `ngx_lua_cpp_t` instance |
+| `inst:start(n)` | `start(size_t)` | Starts the thread pool with `n` worker threads |
+| `inst:stop()` | `stop()` | Stops the thread pool and drains pending tasks |
+| `inst:is_running()` | `is_running()` | Returns `true` if the thread pool is active |
+| `inst:get_hardware_concurrency()` | `get_hardware_concurrency()` | Returns `std::thread::hardware_concurrency()` |
+| `inst:sleep(ms)` | `sleep(size_t)` | Example coroutine: sleeps on a worker thread, returns the value |
+| `inst:__async_worker__(ptr)` | `__async_worker__(void*)` | Introspection: get/set the internal async worker (for advanced use) |
+
+### 4.2 `ngx_hooker_t` (src/ngx_lua_cpp.cpp)
+
+A process-wide singleton (`get_instance()`) responsible for integrating with the nginx event loop. It is constructed on first access (typically when `luaopen_ngx_lua_cpp` is called).
+
+**Initialization steps:**
+
+1. Opens the host process handle (`dlopen(NULL)` / `GetModuleHandle(NULL)`).
+2. Resolves nginx and ngx_lua symbols by name using `dlsym` / `GetProcAddress`:
+ - `ngx_event_actions` — the global event actions table
+ - `ngx_posted_delayed_events` — the delayed events queue
+ - `ngx_http_lua_module` / `ngx_stream_lua_module` — module descriptors
+ - `ngx_http_lua_get_co_ctx` / `ngx_stream_lua_get_co_ctx` — coroutine context accessors
+3. Replaces `ngx_event_actions.process_events` with its own `proxy_ngx_process_events`, saving the original pointer.
+
+**Event loop hook** (`process_events`):
+
+On each nginx event loop iteration:
+1. Removes any placeholder sleep events that were inserted by `ngx_lua_cpp_yield` (to trick ngx_lua into yielding).
+2. Calls `process_events()` on every registered `ngx_lua_cpp_t` instance, which polls the main warp for completed tasks.
+3. Calls the original `process_events` to perform normal nginx event processing.
+
+**Yield/Resume mechanism:**
+
+- **`ngx_lua_cpp_yield`** — Called when a C++ coroutine needs the Lua side to yield. It invokes `ngx.sleep(0)` internally to put the Lua coroutine into a sleeping state, making it compatible with ngx_lua's scheduler.
+- **`ngx_lua_cpp_resume`** — Called when a C++ coroutine completes. It inserts the coroutine context into `ngx_posted_delayed_events` so nginx will wake and resume the Lua coroutine on the next event loop iteration. Return values are stored in the Lua registry keyed by the `lua_State*` pointer.
+
+**Auto-discovery of `co_ctx` event queue offset:**
+
+The offset of the `sleep.event_queue` field within `ngx_http_lua_co_ctx_t` (and the stream equivalent) is not known at compile time. The hooker discovers it at runtime by:
+1. Calling `ngx.sleep(0)` once on the real yield function.
+2. Observing which entry appears at the tail of `ngx_posted_delayed_events`.
+3. Computing the byte offset from the `co_ctx` pointer to that queue entry.
+
+This approach avoids any dependency on nginx internal header files.
+
+### 4.3 `ngx_warp_t` (src/ngx_lua_cpp.h)
+
+A specialization of `iris_warp_t` configured for the nginx environment:
+
+```cpp
+struct ngx_warp_t : iris_warp_t, false, ngx_warp_t> { ... };
+```
+
+- The `strand = false` template parameter means it operates in warp (non-strand) mode — routines are dispatched but not strictly serialized.
+- `flush_warp()` calls `ngx_hooker_t::notify()`, which triggers `ngx_event_actions.notify()` to wake the nginx event loop so it will process the newly queued tasks promptly.
+- The enter/leave/suspend/resume warp hooks are no-ops because the nginx main thread context does not need additional locking.
+
+### 4.4 Coroutine Return Value Wrapping (src/ngx_lua_cpp.h)
+
+For C++ coroutines that return a value (i.e., `iris_coroutine_t` where `T` is non-void), the framework wraps the Lua-side coroutine function so that:
+
+1. When the C++ coroutine yields, the Lua coroutine also yields (via the ngx.sleep trick).
+2. When the C++ coroutine completes with a return value, the value is stored in the Lua registry.
+3. When the Lua coroutine is resumed, a wrapper function (`get_coroutine_returns`) retrieves the stored values from the registry and returns them as Lua return values.
+
+This is implemented through the `iris_lua_traits_t` specialization for coroutine function pointers and the `ngx_iris_wrap_coroutine_with_returns_key` registry entry.
+
+### 4.5 iris Library (src/iris/)
+
+The [iris](https://github.com/paintdream/iris) library is a header-only C++ concurrency framework providing:
+
+| Header | Purpose |
+|---|---|
+| `iris_common.h` / `.inl` | Utilities: block allocators, binary search helpers, atomic guards, debug macros |
+| `iris_lua.h` | Full-featured Lua binding: automatic type marshalling, metatables, ref counting, coroutine integration, error handling |
+| `iris_dispatcher.h` | `iris_async_worker_t` (thread pool) and `iris_warp_t` (strand/warp task serialization) |
+| `iris_coroutine.h` | `iris_coroutine_t` — C++20 coroutine wrapper with `co_await` support for warp switching (`iris_switch`) |
+
+## 5. Nginx Integration Details
+
+### 5.1 No Header Dependency
+
+ngx_lua_cpp deliberately avoids including any nginx or OpenResty headers. Instead, `ngx_lua_cpp.cpp` contains minimal forward declarations of the nginx structures it needs:
+
+- `ngx_http_request_t` (first three fields only: `signature`, `connection`, `ctx`)
+- `ngx_stream_session_t` (enough fields to reach `ctx`)
+- `ngx_stream_lua_request_t` (first two fields: `connection`, `session`)
+- `ngx_module_t` (first field: `ctx_index`)
+- `ngx_event_actions_t` (full function pointer table)
+- `ngx_queue_t` (doubly-linked list node)
+
+If a custom nginx build changes the layout of these structures, the forward declarations can be updated without pulling in the full nginx source tree.
+
+### 5.2 Symbol Resolution
+
+All nginx/ngx_lua symbols are resolved at runtime via `dlsym` (Linux) or `GetProcAddress` (Windows). This means:
+
+- The shared library has no link-time dependency on nginx.
+- It works with any nginx binary that exports the required symbols.
+- The library can detect HTTP vs. stream context at runtime by checking `ngx.config.subsystem`.
+
+### 5.3 HTTP and Stream Support
+
+The framework supports both `ngx_http_lua` and `ngx_stream_lua` subsystems. At registration time (`ngx_hooker_t::registar`), it queries `ngx.config.subsystem` and stores a boolean flag in the Lua registry. This flag is checked on every yield/resume to determine which set of context accessors and yield functions to use.
+
+## 6. Threading Model
+
+```
+ ┌──────────────────────────┐
+ │ nginx main thread │
+ │ │
+ │ ┌─────────────────────┐ │
+ │ │ main_warp (strand) │ │
+ │ │ polled each event │ │
+ │ │ loop iteration │ │
+ │ └─────────────────────┘ │
+ └────────────┬───────────────┘
+ │
+ ┌────────────┼───────────────┐
+ │ │ │
+ ┌─────▼────┐ ┌────▼─────┐ ┌──────▼───┐
+ │ worker 0 │ │ worker 1 │ │ worker N │
+ │ (thread) │ │ (thread) │ │ (thread) │
+ └───────────┘ └──────────┘ └──────────┘
+```
+
+- **Main thread** — The nginx event loop thread. `main_warp` queues are polled here during `process_events`. All Lua interactions happen on this thread.
+- **Worker threads** — Managed by `iris_async_worker_t`. C++ coroutines use `co_await iris_switch(nullptr)` to move execution off the main thread, perform blocking work, then `co_await iris_switch(current)` to return results to the main warp.
+- **Priority tasks** — Tasks marked as priority are redirected to `main_warp` via the `set_priority_task_handler` callback, ensuring they execute on the main thread.
+
+## 7. Build System
+
+The project uses **CMake** (minimum version 3.12) with C++20 enabled:
+
+```
+cmake -B build .
+cmake --build build
+```
+
+**Key build details:**
+
+- **Output**: Shared library (`libngx_lua_cpp.so` on Linux, `ngx_lua_cpp.dll` on Windows).
+- **Lua dependency**: LuaJIT by default; can switch to Lua 5.1–5.4 via the `USE_LUA_VERSION` cache variable.
+- **LuaJIT discovery**: Uses `cmake/FindLuaJIT.cmake`. Set the `LUA_DIR` environment variable if LuaJIT is not found automatically (typically points to OpenResty's installation directory).
+- **Platform support**: Linux (GCC 11+, Clang 14+), Windows (Visual Studio 2019+). Non-MSVC builds add `-fPIC`.
+- **Link dependencies** (non-MSVC): `m`, `dl`, `stdc++`, `pthread`, and the Lua library.
+
+## 8. Demo and Debug Console
+
+### 8.1 Lua Bootstrap (`demo/init_ngx_lua_cpp.lua`)
+
+A minimal Lua module loaded once per worker via `init_worker_by_lua_block`. It creates a singleton `ngx_lua_cpp_t` instance and starts 4 worker threads:
+
+```lua
+local lib = require("ngx_lua_cpp")
+local inst = lib.new()
+inst:start(4)
+return inst
+```
+
+Subsequent `require("init_ngx_lua_cpp")` calls in request handlers return the cached instance.
+
+### 8.2 Web Debug Console (`web/`)
+
+> **Security Warning:** The debug console exposes an endpoint that executes arbitrary Lua code on the server. It must **never** be deployed in production. Restrict access via firewall rules, bind to `localhost` only, and enable authentication if used in any shared environment.
+
+A self-contained debug interface consisting of:
+
+- **`nginx.conf`** — Configures an HTTP server on port 8080 with API endpoints:
+ - `POST /api/exec` — Execute arbitrary Lua code in a sandboxed environment
+ - `GET /api/logs` — Retrieve nginx error/access log tails
+ - `POST /api/logs/clear` — Truncate log files
+ - `GET /api/info` — Server status and ngx_lua_cpp state
+- **`index.html`** — A single-page application providing a Lua terminal and log viewer with syntax highlighting and keyboard shortcuts.
+
+## 9. Extending ngx_lua_cpp
+
+To add a new C++ function accessible from Lua:
+
+1. **Declare** the method in `ngx_lua_cpp_t` (in `ngx_lua_cpp.h`).
+2. **Implement** it in `ngx_lua_cpp.cpp`. For async operations, return `iris_coroutine_t` and use `co_await iris_switch` to move between warps.
+3. **Register** it in `ngx_lua_cpp_t::lua_registar`:
+ ```cpp
+ lua.set_current<&ngx_lua_cpp_t::my_function>("my_function");
+ ```
+4. **Call** from Lua:
+ ```lua
+ local result = inst:my_function(args)
+ ```
+
+The iris_lua binding system handles automatic type marshalling between Lua and C++, including support for integers, floats, strings, booleans, tables, and userdata.