From ba12e3c280cd6bbb4a5bddd30f4780d519b5eafe Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 23 Oct 2026 20:25:22 -0600 Subject: [PATCH 1/5] processor_fts: support the portable Lua runtime Signed-off-by: Eduardo Silva --- plugins/processor_fts/fts.h | 13 +++++++++++++ plugins/processor_fts/fts_runtime.c | 27 ++++++++++++++++++++++----- plugins/processor_fts/fts_values.c | 14 +++++++------- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/plugins/processor_fts/fts.h b/plugins/processor_fts/fts.h index 86cc2d7deae..952c37b19af 100644 --- a/plugins/processor_fts/fts.h +++ b/plugins/processor_fts/fts.h @@ -24,6 +24,19 @@ #include #include +/* Typed views keep their parent in a userdata environment/uservalue table. */ +#if LUA_VERSION_NUM >= 502 +#define fts_lua_rawlen lua_rawlen +#define fts_lua_getuservalue lua_getuservalue +#define fts_lua_setuservalue lua_setuservalue +#define fts_lua_register(l, methods) luaL_setfuncs(l, methods, 0) +#else +#define fts_lua_rawlen lua_objlen +#define fts_lua_getuservalue lua_getfenv +#define fts_lua_setuservalue lua_setfenv +#define fts_lua_register(l, methods) luaL_register(l, NULL, methods) +#endif + #define FTS_MAX_DEPTH 64 /* Each signal owns its representation, validation and commit semantics. */ diff --git a/plugins/processor_fts/fts_runtime.c b/plugins/processor_fts/fts_runtime.c index fa574c4afdf..51b3d2033e9 100644 --- a/plugins/processor_fts/fts_runtime.c +++ b/plugins/processor_fts/fts_runtime.c @@ -17,14 +17,17 @@ * limitations under the License. */ +#include +#ifdef FLB_HAVE_LUAJIT #include +#endif #include #include #include #include #include -#ifdef FLB_HAVE_SANITIZE_ADDRESS +#if defined(FLB_HAVE_SANITIZE_ADDRESS) && defined(FLB_HAVE_LUAJIT) #include #endif @@ -41,12 +44,12 @@ */ #define FTS_UNWOUND_STACK_SIZE (256 * 1024) -#ifdef FLB_HAVE_SANITIZE_ADDRESS +#if defined(FLB_HAVE_SANITIZE_ADDRESS) && defined(FLB_HAVE_LUAJIT) __attribute__((noinline)) #endif void fts_sanitizer_forget_unwound_frames(void) { -#ifdef FLB_HAVE_SANITIZE_ADDRESS +#if defined(FLB_HAVE_SANITIZE_ADDRESS) && defined(FLB_HAVE_LUAJIT) char *top; top = (char *) __builtin_frame_address(0); @@ -210,7 +213,11 @@ int fts_load_helpers(lua_State *l, const char *code, size_t size, const char *na } lua_rawgeti(l, LUA_REGISTRYINDEX, ctx->helpers_env_ref); +#if LUA_VERSION_NUM >= 502 + lua_setupvalue(l, -2, 1); +#else lua_setfenv(l, -2); +#endif if (lua_pcall(l, 0, 0, 0) != 0) { fts_sanitizer_forget_unwound_frames(); return -1; @@ -223,13 +230,19 @@ static int initialize_policy(lua_State *l) struct fts_runtime *ctx; const char *blocked[] = {"io", "os", "debug", "jit", "ffi", "package", "coroutine", "module", "load", "loadfile", "loadstring", "dofile", - "newproxy", "xpcall", "getfenv", "setfenv", "print", NULL}; + "newproxy", "xpcall", "getfenv", "setfenv", "print", "warn", NULL}; int i; int ret; int environment; ctx = lua_touserdata(l, 1); +#if LUA_VERSION_NUM >= 502 + lua_pushglobaltable(l); + copy_table(l, -1); + lua_remove(l, -2); +#else copy_table(l, LUA_GLOBALSINDEX); +#endif environment = lua_gettop(l); /* tables used by validation must also be independent of policy globals */ @@ -338,9 +351,13 @@ int fts_runtime_initialize(struct fts_runtime *ctx, struct flb_config *config) } l = ctx->vm->state; +#ifdef FLB_HAVE_LUAJIT ret = luaJIT_setmode(l, 0, LUAJIT_MODE_ENGINE | (ctx->jit ? LUAJIT_MODE_ON : LUAJIT_MODE_OFF)); +#else + ret = 0; +#endif if (ctx->jit && !ret) { - lua_pushliteral(l, "JIT is unavailable in this LuaJIT build"); + lua_pushliteral(l, "JIT is unavailable in this Lua runtime"); return FLB_PROCESSOR_FAILURE; } diff --git a/plugins/processor_fts/fts_values.c b/plugins/processor_fts/fts_values.c index 77701883b1a..e57ffa3d093 100644 --- a/plugins/processor_fts/fts_values.c +++ b/plugins/processor_fts/fts_values.c @@ -69,7 +69,7 @@ static struct fts_value *check_value(lua_State *l, int index) lua_getallocf(l, (void **) &ctx); value = NULL; - if (lua_type(l, index) == LUA_TUSERDATA && lua_objlen(l, index) == sizeof(*value)) { + if (lua_type(l, index) == LUA_TUSERDATA && fts_lua_rawlen(l, index) == sizeof(*value)) { value = lua_touserdata(l, index); } @@ -448,7 +448,7 @@ struct cfl_variant *fts_resolve_value(lua_State *l, int index, int depth) luaL_error(l, "typed value nesting exceeds limit"); } - lua_getfenv(l, index); + fts_lua_getuservalue(l, index); lua_rawgeti(l, -1, 1); parent = fts_resolve_value(l, -1, depth + 1); lua_pop(l, 1); @@ -538,7 +538,7 @@ static void push_view(lua_State *l, int parent, int key) lua_rawseti(l, -2, 1); lua_pushvalue(l, key); lua_rawseti(l, -2, 2); - lua_setfenv(l, -2); + fts_lua_setuservalue(l, -2); } static int value_gc(lua_State *l) @@ -1013,7 +1013,7 @@ static void detach_root(lua_State *l, int index, int depth) luaL_error(l, "typed value nesting exceeds limit"); } - lua_getfenv(l, index); + fts_lua_getuservalue(l, index); lua_rawgeti(l, -1, 1); detach_root(l, lua_gettop(l), depth + 1); lua_pop(l, 2); @@ -1621,7 +1621,7 @@ static int redact(lua_State *l) root_value(l, 1)->depth_changed = FLB_TRUE; } - count = lua_objlen(l, 2); + count = fts_lua_rawlen(l, 2); lua_newtable(l); keys = lua_gettop(l); @@ -2079,9 +2079,9 @@ int fts_register_values(lua_State *l) ctx->value_metatable = lua_topointer(l, -1); lua_pushvalue(l, -1); ctx->value_metatable_ref = luaL_ref(l, LUA_REGISTRYINDEX); - luaL_register(l, NULL, value_methods); + fts_lua_register(l, value_methods); lua_newtable(l); - luaL_register(l, NULL, value_methods + 1); + fts_lua_register(l, value_methods + 1); lua_setfield(l, -2, "__index"); lua_pushliteral(l, "typed value"); lua_setfield(l, -2, "__metatable"); From 675b5846d36aa2b763bac407ca0aede770a2757b Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 23 Oct 2026 20:25:22 -0600 Subject: [PATCH 2/5] build: enable FTS in the browser WebAssembly profile Signed-off-by: Eduardo Silva --- cmake/wasm-browser.cmake | 3 ++- plugins/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/wasm-browser.cmake b/cmake/wasm-browser.cmake index c5d2df00adb..9aad4ede12c 100644 --- a/cmake/wasm-browser.cmake +++ b/cmake/wasm-browser.cmake @@ -68,6 +68,7 @@ set(FLB_WASM_BROWSER_PLUGINS FLB_IN_STORAGE_BACKLOG FLB_PROCESSOR_CONTENT_MODIFIER FLB_PROCESSOR_CUMULATIVE_TO_DELTA + FLB_PROCESSOR_FTS FLB_PROCESSOR_LABELS FLB_PROCESSOR_METRICS_SELECTOR FLB_PROCESSOR_OPENTELEMETRY_ENVELOPE @@ -97,7 +98,7 @@ set(FLB_WASM_BROWSER_PLUGINS FLB_OUT_STDOUT) if(NOT FLB_WASM_LUA) - list(REMOVE_ITEM FLB_WASM_BROWSER_PLUGINS FLB_FILTER_LUA) + list(REMOVE_ITEM FLB_WASM_BROWSER_PLUGINS FLB_FILTER_LUA FLB_PROCESSOR_FTS) endif() # Compile every dependency with the same threading ABI. The embedding API diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 5b70166c948..2a909487746 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -347,7 +347,7 @@ REGISTER_IN_PLUGIN("in_random") # ========== REGISTER_PROCESSOR_PLUGIN("processor_content_modifier") REGISTER_PROCESSOR_PLUGIN("processor_cumulative_to_delta") -if(FLB_LUAJIT AND FLB_YYJSON) +if((FLB_LUAJIT OR FLB_WASM_LUA) AND FLB_YYJSON) REGISTER_PROCESSOR_PLUGIN("processor_fts") endif() REGISTER_PROCESSOR_PLUGIN("processor_labels") From a3ff04efd5587d0bfaf976a0db6df6d8d4e95299 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 23 Oct 2026 20:25:22 -0600 Subject: [PATCH 3/5] tests: internal: support portable Lua in FTS regressions Signed-off-by: Eduardo Silva --- tests/internal/fts.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/internal/fts.c b/tests/internal/fts.c index 383b9f32153..b0fbd7b09e6 100644 --- a/tests/internal/fts.c +++ b/tests/internal/fts.c @@ -1,4 +1,4 @@ -/* Fluent Bit: native log-policy regression tests, Apache-2.0. */ +/* Fluent Bit: log-policy regression tests, Apache-2.0. */ #include #include #include @@ -457,6 +457,7 @@ static void garbage_collector_is_private(void) 0, "drop", 6, 0, 0, NULL, NULL); } +#ifdef FLB_HAVE_LUAJIT static void trusted_policy_jit_compiles(void) { struct flb_config *config; @@ -494,6 +495,28 @@ static void trusted_policy_jit_compiles(void) flb_config_exit(config); } +#else +static void portable_runtime_rejects_jit(void) +{ + struct flb_config *config; + struct flb_processor *processor; + struct flb_processor_unit *unit; + + config = flb_config_init(); + TEST_ASSERT(config != NULL); + processor = flb_processor_create(config, "fts_no_jit_test", NULL, 0); + TEST_ASSERT(processor != NULL); + unit = flb_processor_unit_create(processor, FLB_PROCESSOR_LOGS, "fts"); + TEST_ASSERT(unit != NULL); + TEST_CHECK(flb_processor_unit_set_property_str(unit, "jit", "true") == 0); + TEST_CHECK(flb_processor_unit_set_property_str(unit, "instruction_limit", "0") == 0); + TEST_CHECK(flb_processor_unit_set_property_str(unit, "code", "function process(log) end") == 0); + TEST_CHECK(flb_processor_init(processor) != 0); + flb_processor_destroy(processor); + flb_config_exit(config); +} +#endif + static void native_histogram_buckets_are_preserved(void) { struct flb_config *config; @@ -871,7 +894,7 @@ static void memory_exhaustion_does_not_poison_later_callbacks(void) TEST_CHECK(fts_runtime_call(runtime, NULL, 1) == LUA_ERRMEM); lua_settop(l, 0); TEST_CHECK(fts_runtime_call(runtime, runtime, 1) == 0); - TEST_CHECK(lua_objlen(l, -1) == 300000); + TEST_CHECK(fts_lua_rawlen(l, -1) == 300000); lua_settop(l, 0); flb_processor_destroy(processor); flb_config_exit(config); @@ -1482,7 +1505,11 @@ TEST_LIST = { {"schema_promotion_is_record_local", schema_promotion_is_record_local}, {"reserved_timestamps_are_record_errors", reserved_timestamps_are_record_errors}, {"small_record_batch_uses_wire_budget", small_record_batch_uses_wire_budget}, +#ifdef FLB_HAVE_LUAJIT {"trusted_policy_jit_compiles", trusted_policy_jit_compiles}, +#else + {"portable_runtime_rejects_jit", portable_runtime_rejects_jit}, +#endif {"native_histogram_buckets_are_preserved", native_histogram_buckets_are_preserved}, {"native_metric_scalar_preservation", native_metric_scalar_preservation}, {"kept_native_family_no_merge", kept_native_family_does_not_merge_into_outputs}, From cd0b05953611e80d1ad940b6b5b2b4be43dbadbf Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 23 Oct 2026 20:25:22 -0600 Subject: [PATCH 4/5] tests: validate FTS in browser artifacts Signed-off-by: Eduardo Silva --- tests/wasm/CMakeLists.txt | 13 +++++++++ tests/wasm/browser_smoke.py | 7 ++--- tests/wasm/check_profile.cmake | 9 ++++--- tests/wasm/profile/CMakeLists.txt | 10 ++++++- tests/wasm/sdk_test.js | 44 +++++++++++++++++++++++++++++++ tests/wasm/sdk_test.py | 26 ++++++++++-------- 6 files changed, 91 insertions(+), 18 deletions(-) diff --git a/tests/wasm/CMakeLists.txt b/tests/wasm/CMakeLists.txt index 8c32448219b..f59c31cd92e 100644 --- a/tests/wasm/CMakeLists.txt +++ b/tests/wasm/CMakeLists.txt @@ -37,6 +37,19 @@ if(FLB_WASM_LUA) "-sASSERTIONS=2 -sEXIT_RUNTIME=1") add_test(NAME flb-wasm-lua COMMAND flb-wasm-lua --no-exec) set_tests_properties(flb-wasm-lua PROPERTIES TIMEOUT 30) + + if(FLB_PROCESSOR_FTS) + add_executable(flb-wasm-fts "${PROJECT_SOURCE_DIR}/tests/internal/fts.c") + target_include_directories(flb-wasm-fts PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/tests/internal") + target_compile_definitions(flb-wasm-fts PRIVATE FLB_TEST_INIT_NOP FLB_TEST_FINI_NOP) + target_link_libraries(flb-wasm-fts fluent-bit-static) + set_target_properties(flb-wasm-fts PROPERTIES LINK_FLAGS + "-sASSERTIONS=2 -sEXIT_RUNTIME=1 -sALLOW_MEMORY_GROWTH=1 -sSTACK_SIZE=262144") + add_test(NAME flb-wasm-fts COMMAND flb-wasm-fts --no-exec) + # The large-object regression is substantially slower with WASM ASan. + set_tests_properties(flb-wasm-fts PROPERTIES TIMEOUT 180) + endif() endif() add_test(NAME flb-wasm-build-profile diff --git a/tests/wasm/browser_smoke.py b/tests/wasm/browser_smoke.py index 1c733ee45a4..4e108c042c7 100644 --- a/tests/wasm/browser_smoke.py +++ b/tests/wasm/browser_smoke.py @@ -80,7 +80,8 @@ def main(): parser.add_argument("--build-dir", type=Path, default=DEFAULT_BUILD_DIR) parser.add_argument("--target", choices=("flb-wasm-dependencies", "co-wasm-test", "flb-wasm-pipeline", "flb-wasm-events", "flb-wasm-event-fiber", - "flb-wasm-storage", "flb-wasm-yaml", "flb-wasm-lua", "flb-wasm-http", + "flb-wasm-storage", "flb-wasm-yaml", "flb-wasm-lua", "flb-wasm-fts", + "flb-wasm-http", "flb-wasm-http-lifecycle", "flb-wasm-http-ng", "mk-test-wasm-events"), default="flb-wasm-dependencies") parser.add_argument("--browser", default=shutil.which("chromium") or shutil.which("google-chrome")) @@ -111,9 +112,9 @@ def main(): for phase in phases: page.goto(f"http://127.0.0.1:{server.server_port}/?phase={phase}" if phase else f"http://127.0.0.1:{server.server_port}/" + - ("?arg=--no-exec" if args.target == "flb-wasm-lua" else "")) + ("?arg=--no-exec" if args.target in ("flb-wasm-lua", "flb-wasm-fts") else "")) page.wait_for_function("document.body.dataset.result !== 'pending'", - timeout=45000) + timeout=180000 if args.target == "flb-wasm-fts" else 45000) passed = passed and page.get_attribute("body", "data-result") == "passed" print(page.locator("#output").inner_text()) if not passed: diff --git a/tests/wasm/check_profile.cmake b/tests/wasm/check_profile.cmake index da900fa9ef1..c102bbce507 100644 --- a/tests/wasm/check_profile.cmake +++ b/tests/wasm/check_profile.cmake @@ -31,12 +31,15 @@ function(check_profile name expected_error) message(STATUS "${name}: passed") endfunction() -check_profile(defaults "") +check_profile(defaults "" -DEXPECT_FTS=ON) check_profile(old_sdk "FLB_WASM_BROWSER requires Emscripten 6.0.9" -DEMSCRIPTEN_VERSION=5.0.7) check_profile(unvalidated_sdk "FLB_WASM_BROWSER requires Emscripten 6.0.9" -DEMSCRIPTEN_VERSION=6.0.10) -check_profile(minimal "" -DFLB_MINIMAL=ON) +check_profile(minimal "" -DFLB_MINIMAL=ON -DEXPECT_FTS=OFF) check_profile(disable_filter "" -DFLB_FILTER_GREP=OFF) -check_profile(disable_lua "" -DFLB_WASM_LUA=OFF -DFLB_FILTER_LUA=OFF) +check_profile(disable_lua "" -DFLB_WASM_LUA=OFF -DFLB_FILTER_LUA=OFF -DFLB_PROCESSOR_FTS=OFF -DEXPECT_FTS=OFF) +check_profile(disable_fts "" -DFLB_PROCESSOR_FTS=OFF -DEXPECT_FTS=OFF) +check_profile(reject_fts_without_backend "FLB_PROCESSOR_FTS is not supported by the browser build profile" + -DFLB_WASM_LUA=OFF -DFLB_PROCESSOR_FTS=ON) check_profile(reject_lua_without_backend "FLB_FILTER_LUA is not supported by the browser build profile" -DFLB_WASM_LUA=OFF -DFLB_FILTER_LUA=ON) check_profile(native_defaults "" -DPROFILE_TEST_MODE=native) diff --git a/tests/wasm/profile/CMakeLists.txt b/tests/wasm/profile/CMakeLists.txt index 00a5dbe7f8a..742722cd763 100644 --- a/tests/wasm/profile/CMakeLists.txt +++ b/tests/wasm/profile/CMakeLists.txt @@ -38,7 +38,15 @@ else() if(NOT FLB_MINIMAL AND (NOT FLB_IN_LIB OR NOT FLB_OUT_LIB OR NOT FLB_FILTER_MODIFY)) message(FATAL_ERROR "Local processing plugin defaults were lost") endif() - if(FLB_MINIMAL AND (FLB_IN_LIB OR FLB_OUT_LIB OR FLB_FILTER_MODIFY)) + if(DEFINED EXPECT_FTS) + if((EXPECT_FTS AND NOT FLB_PROCESSOR_FTS) OR (NOT EXPECT_FTS AND FLB_PROCESSOR_FTS)) + message(FATAL_ERROR "FTS browser default or override was lost") + endif() + endif() + if(NOT FLB_WASM_LUA AND FLB_PROCESSOR_FTS) + message(FATAL_ERROR "FTS requires a Lua backend") + endif() + if(FLB_MINIMAL AND (FLB_IN_LIB OR FLB_OUT_LIB OR FLB_FILTER_MODIFY OR FLB_PROCESSOR_FTS)) message(FATAL_ERROR "Minimal mode was ignored") endif() endif() diff --git a/tests/wasm/sdk_test.js b/tests/wasm/sdk_test.js index 166c41d7c0d..1ba942347ca 100644 --- a/tests/wasm/sdk_test.js +++ b/tests/wasm/sdk_test.js @@ -27,6 +27,45 @@ pipeline: match: '*' format: json_lines `; + const checkFts = async fluent => { + await fluent.writeFile('/config/typed.lua', `local fb = require("fluentbit") + assert(os == nil and io == nil and debug == nil and warn == nil) + function process(log) + if log.body:get_string("message") == "fts drop" then return fb.DROP end + local view = log.body:get("nested") + collectgarbage("collect") + view:set("value", fb.int64(42)) + log.body:set("exact", fb.uint64("18446744073709551615"):value()) + log.body:set("fts", true) + return fb.KEEP + end`); + const ftsYaml = yaml.replace(' tag: app', ` tag: app + processors: + logs: + - name: fts + script: /config/typed.lua`); + await fluent.start({yaml: ftsYaml, graceSeconds: 1}); + await fluent.push({input: 'app', records: [ + {message: 'fts drop'}, {message: 'fts browser', nested: {value: 1}}]}); + await waitFor(() => logs.some(line => { + try { + const record = JSON.parse(line); + return record.message === 'fts browser' && record.fts === true && + record.nested.value === 42 && record.exact === '18446744073709551615'; + } + catch (_) { return false; } + })); + await fluent.stop(); + check(!logs.some(line => { + try { return JSON.parse(line).message === 'fts drop'; } + catch (_) { return false; } + }), 'FTS drops records'); + await rejected(() => fluent.start({yaml: ftsYaml.replace('- name: fts', + '- name: fts\n jit: true\n instruction_limit: 0')}), 'E_CONFIG'); + check(logs.some(line => line.includes('JIT is unavailable in this Lua runtime')), + 'FTS reports the unsupported JIT option'); + console.log('Browser FTS processor passed: typed values, nested views, drop and JIT rejection'); + }; check(getBrowserSupport().supported, 'Browser preflight'); const logs = []; const errors = []; @@ -38,6 +77,10 @@ pipeline: progress.push(update); console.log(`SDK startup: ${update.stage} (${update.loadedBytes} bytes)`); }}); + if (globalThis.ftsOnly) { + try { await checkFts(fluent); return {passed: true}; } + finally { await fluent.destroy(); } + } try { check(['loading-runtime', 'downloading-wasm', 'compiling-wasm', 'initializing-workers', 'starting-command-thread'].every(stage => @@ -93,6 +136,7 @@ pipeline: catch (_) { return false; } })); await fluent.stop(); + await checkFts(fluent); await fluent.start({yaml, graceSeconds: 1}); await fluent.stop(); await fluent.syncStorage(); diff --git a/tests/wasm/sdk_test.py b/tests/wasm/sdk_test.py index f823a798b32..20861baf61f 100644 --- a/tests/wasm/sdk_test.py +++ b/tests/wasm/sdk_test.py @@ -155,7 +155,9 @@ def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--build-dir', type=Path, default=DEFAULT_BUILD_DIR) parser.add_argument('--browser', required=True) - parser.add_argument('--pthread-only', action='store_true') + mode = parser.add_mutually_exclusive_group() + mode.add_argument('--pthread-only', action='store_true') + mode.add_argument('--fts-only', action='store_true') args = parser.parse_args() with tempfile.TemporaryDirectory(prefix='flb-sdk-tls-') as directory: cert, key = development_certificate(directory, ['localhost', '127.0.0.1']) @@ -190,18 +192,20 @@ def worker_started(worker): assert not errors, errors print('Pthread bootstrap faults and worker cleanup passed', flush=True) return + page.evaluate('(value) => { globalThis.ftsOnly = value; }', args.fts_only) result = page.evaluate(Path(__file__).with_suffix('.js').read_text()) assert result['passed'] and not errors, errors - server.RequestHandlerClass = functools.partial(StorageFaultHandler, build_dir=args.build_dir) - check_storage_failure(page) - server.RequestHandlerClass = functools.partial(BrowserHandler, build_dir=args.build_dir) - page.route('**/sdk/fluent-bit-runtime.wasm', lambda route: route.abort()) - failure = page.evaluate("""async () => { - const {createFluentBit} = await import('/sdk/fluent-bit.js'); - try { await createFluentBit(); return null; } - catch (error) { return error.code; } - }""") - assert failure in ('E_LOAD', 'E_RUNTIME', 'E_WORKER'), failure + if not args.fts_only: + server.RequestHandlerClass = functools.partial(StorageFaultHandler, build_dir=args.build_dir) + check_storage_failure(page) + server.RequestHandlerClass = functools.partial(BrowserHandler, build_dir=args.build_dir) + page.route('**/sdk/fluent-bit-runtime.wasm', lambda route: route.abort()) + failure = page.evaluate("""async () => { + const {createFluentBit} = await import('/sdk/fluent-bit.js'); + try { await createFluentBit(); return null; } + catch (error) { return error.code; } + }""") + assert failure in ('E_LOAD', 'E_RUNTIME', 'E_WORKER'), failure for _ in range(100): if not workers: break From d63b94c728149462de1719e3703aad846bd8688a Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Fri, 23 Oct 2026 20:25:22 -0600 Subject: [PATCH 5/5] processor_fts: document browser runtime support Signed-off-by: Eduardo Silva --- plugins/processor_fts/README.md | 5 +++++ sdk/browser/README.md | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/plugins/processor_fts/README.md b/plugins/processor_fts/README.md index 33429afc313..28842a56351 100644 --- a/plugins/processor_fts/README.md +++ b/plugins/processor_fts/README.md @@ -7,6 +7,11 @@ attributes are exposed as properties instead of raw metadata. FTS supports logs, metrics, and traces. Its configuration name is `fts`. +The experimental browser WASM profile includes FTS when `FLB_WASM_LUA=ON` +(the default). It uses the portable Lua 5.4 interpreter; `jit: true` is rejected. +Typed values, restricted policy libraries and instruction/memory limits remain +available. Setting `FLB_WASM_LUA=OFF` disables both FTS and the Lua filter. + | Signal | Callback | Called once per | | ------- | ----------------- | --------------- | | Logs | `process(log)` | log record | diff --git a/sdk/browser/README.md b/sdk/browser/README.md index cee78d1b936..56b76a51654 100644 --- a/sdk/browser/README.md +++ b/sdk/browser/README.md @@ -184,6 +184,14 @@ unsanitized browser assets, not a production release or an npm publication. Artifact availability does not imply the subsequent browser tests or the ASan job passed; check the workflow results before choosing a build. +## Typed scripting processor + +The default browser artifact includes the `fts` processor using portable Lua 5.4. +Supply a policy with `writeFile('/config/policy.lua', source)` and reference that +path from the YAML processor's `script` property. `jit: true` is unavailable in +browser builds. See the [FTS guide](https://github.com/fluent/fluent-bit/blob/master/plugins/processor_fts/README.md) for +policies, typed values and resource limits. + ## Release gates Before promoting this experimental API: run ASan lifecycle/network/storage and