feat: build apisix-runtime with ngx_http_ffi_client - #481
Conversation
ai-proxy, ai-proxy-multi and ai-request-rewrite send every outbound LLM request through ngx_http_ffi_client when the runtime carries it, and cost about a third of the outbound CPU time they do on lua-resty-http. The module has to be in the runtime for that to happen. api7/ngx_http_ffi_client is still private and carries no tags, so it is pinned by commit and fetched with NGX_HTTP_FFI_CLIENT_TOKEN. The token reaches the container as a BuildKit secret rather than a build arg, so it stays out of image history, and the fetch runs with the trace off so it stays out of the build log. A build without the token warns and leaves the module out; ai-proxy falls back to lua-resty-http on such a runtime, so both runtimes work. The module compiles against lua-nginx-module's public co-ctx API, which arrived in 0.10.29. OpenResty 1.29.2.4 bundles 0.10.31rc2 and has it.
The script runs under set -x, so assigning NGX_HTTP_FFI_CLIENT_TOKEN and testing it with [ -n ... ] both echoed the token into the build log. Only a derived yes/no now reaches the trace.
The build itself stays lenient so pull requests from forks, which get no secrets, still pass. A release is different: without the token it would produce an official runtime with no ngx_http_ffi_client and say so only in a warning buried in the build log.
📝 WalkthroughWalkthroughThe runtime build now validates and retrieves Changesngx_http_ffi_client runtime integration
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant RuntimeBuild as build-apisix-runtime.sh
participant FFIClient as ngx_http_ffi_client
participant OpenResty
RuntimeBuild->>FFIClient: copy local checkout or shallow clone configured tag
RuntimeBuild->>OpenResty: configure module and install Lua FFI binding
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/package-apisix-runtime-deb-openresty-1.21.yml:
- Around line 38-39: Remove the NGX_HTTP_FFI_CLIENT_TOKEN environment variable
from the OpenResty 1.21 workflow so the openresty/1.21.4 build cannot enable
ngx_http_ffi_client; leave the selected runtime unchanged.
In `@build-apisix-runtime.sh`:
- Around line 42-46: Update the ngx_http_ffi_client_ver validation in
build-apisix-runtime.sh to require exactly a 40-character hexadecimal commit ID,
then verify git -C "$prev_workdir" rev-parse HEAD matches the requested pin
before copying. Reject the local source when the working tree is dirty, ensuring
cp -r "$prev_workdir" only builds the selected commit’s contents.
In `@dockerfiles/Dockerfile.apisix-runtime.deb`:
- Around line 26-28: Separate BuildKit cache entries by introducing a non-secret
module-enabled build argument: update dockerfiles/Dockerfile.apisix-runtime.deb
lines 26-28 and dockerfiles/Dockerfile.apisix-runtime.rpm lines 24-26 to consume
it in their RUN instructions; update Makefile lines 46-50 to derive 0 or 1 from
token presence, and pass that value via --build-arg at lines 107-115 and 129.
Ensure the argument distinguishes token-enabled and token-disabled builds
without exposing the secret.
In `@README.md`:
- Line 21: Update the NGX_HTTP_FFI_CLIENT_TOKEN documentation in README.md to
state that it is optional for non-release builds but required for release
builds, while preserving the existing description of its BuildKit-secret usage.
- Around line 112-117: Update the README paragraph describing
ngx_http_ffi_client and ai-proxy to state the compatibility boundary explicitly:
the module can be included only in OpenResty 1.29.2.4 with ngx_lua 0.10.31rc2,
and is incompatible with OpenResty 1.21.4.4. Clarify that lua-resty-http
fallback applies only when the runtime is built without the module, rather than
claiming both runtime variants work universally.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f6e70f05-9094-4227-b537-141d3205af53
📒 Files selected for processing (10)
.github/workflows/package-apisix-runtime-deb-openresty-1.21.yml.github/workflows/package-apisix-runtime-deb-ubuntu20.04.yml.github/workflows/package-apisix-runtime-rpm-el.yml.github/workflows/package-apisix-runtime-rpm-ubi.yml.github/workflows/release-apisix-runtime.ymlMakefileREADME.mdbuild-apisix-runtime.shdockerfiles/Dockerfile.apisix-runtime.debdockerfiles/Dockerfile.apisix-runtime.rpm
| ngx_http_ffi_client_ver=${ngx_http_ffi_client_ver:-"f13fcfa4e923ad82844bf49d9d3b3d283371ef66"} | ||
| if [[ ! "$ngx_http_ffi_client_ver" =~ ^[A-Za-z0-9._/-]+$ ]]; then | ||
| echo "ERROR: invalid ngx_http_ffi_client_ver: $ngx_http_ffi_client_ver" >&2 | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 5 'ngx_http_ffi_client_ver|ngx_http_ffi_client_dir|prev_workdir|git .*fetch|git .*checkout' \
build-apisix-runtime.sh
rg -n -C 3 'ngx_http_ffi_client_ver\s*=' \
-g '*.sh' -g 'Makefile' -g '*.yml' -g '*.yaml' .Repository: api7/apisix-build-tools
Length of output: 6261
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- build script context ---'
sed -n '1,190p' build-apisix-runtime.sh
printf '%s\n' '--- callers and documentation ---'
rg -n -C 4 'NGX_HTTP_FFI_CLIENT_TOKEN|ngx_http_ffi_client_ver|build-apisix-runtime\.sh|ngx_http_ffi_client' \
-g '!build-apisix-runtime.sh' .
printf '%s\n' '--- repository metadata files ---'
git ls-files | sed -n '1,160p'Repository: api7/apisix-build-tools
Length of output: 16381
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
source = Path("build-apisix-runtime.sh").read_text()
default = re.search(
r'ngx_http_ffi_client_ver=\$\{ngx_http_ffi_client_ver:-"([^"]+)"\}',
source,
)
validation = re.search(
r'if \[\[ ! "\$ngx_http_ffi_client_ver" =~ (.+?) \]\]; then',
source,
)
print("default:", default.group(1) if default else "<missing>")
print("validation:", validation.group(1) if validation else "<missing>")
if validation:
pattern = re.compile(r"^[A-Za-z0-9._/-]+$")
for value in ("main", "refs/heads/main", default.group(1)):
print(f"{value!r} accepted:", bool(pattern.fullmatch(value)))
local_start = source.index('if [ "$repo" == "ngx_http_ffi_client" ]')
local_end = source.index("elif [", local_start)
local_branch = source[local_start:local_end]
print("local-copy branch:")
print(local_branch.strip())
print("checks HEAD:", "rev-parse" in local_branch or "git -C" in local_branch)
print("checks working tree:", "status --porcelain" in local_branch)
PYRepository: api7/apisix-build-tools
Length of output: 461
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
lines = Path("build-apisix-runtime.sh").read_text().splitlines()
start = next(i for i, line in enumerate(lines)
if 'repo" == "ngx_http_ffi_client"' in line)
end = next(i for i in range(start + 1, len(lines))
if lines[i].lstrip().startswith("elif "))
branch = lines[start:end]
print("\n".join(f"{i + 1}: {line}" for i, line in enumerate(branch, start)))
print("checks HEAD:", any("rev-parse" in line or "git -C" in line for line in branch))
print("checks working tree:",
any("status --porcelain" in line for line in branch))
PYRepository: api7/apisix-build-tools
Length of output: 248
🏁 Script executed:
#!/bin/bash
set -euo pipefail
nl -ba build-apisix-runtime.sh | sed -n '148,175p'Repository: api7/apisix-build-tools
Length of output: 201
Enforce the ngx_http_ffi_client commit pin for local builds. Require a full 40-character commit ID, and require git -C "$prev_workdir" rev-parse HEAD to match it before copying. Reject dirty working trees because cp -r "$prev_workdir" can otherwise build source that differs from the selected commit.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@build-apisix-runtime.sh` around lines 42 - 46, Update the
ngx_http_ffi_client_ver validation in build-apisix-runtime.sh to require exactly
a 40-character hexadecimal commit ID, then verify git -C "$prev_workdir"
rev-parse HEAD matches the requested pin before copying. Reject the local source
when the working tree is dirty, ensuring cp -r "$prev_workdir" only builds the
selected commit’s contents.
| RUN --mount=type=secret,id=ngx_http_ffi_client_token \ | ||
| export NGX_HTTP_FFI_CLIENT_TOKEN="$(cat /run/secrets/ngx_http_ffi_client_token 2>/dev/null || true)" \ | ||
| && mv ./utils/build-common.sh ./utils/determine-dist.sh ./ \ |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Separate cache entries for module-enabled and module-disabled builds.
BuildKit does not include secret contents in its cache key. These RUN instructions can reuse a layer built with the opposite token state. A release build can then omit ngx_http_ffi_client, or a no-token build can retain it from cache. (docs.docker.com)
dockerfiles/Dockerfile.apisix-runtime.deb#L26-L28: use a non-secret module-enabled build argument in theRUNinstruction.dockerfiles/Dockerfile.apisix-runtime.rpm#L24-L26: use the same non-secret build argument in theRUNinstruction.Makefile#L46-L50: derive a0or1module-enabled value from token presence.Makefile#L107-L115: pass that value with--build-arg.Makefile#L129-L129: pass that value with--build-arg.
📍 Affects 3 files
dockerfiles/Dockerfile.apisix-runtime.deb#L26-L28(this comment)dockerfiles/Dockerfile.apisix-runtime.rpm#L24-L26Makefile#L46-L50Makefile#L107-L115Makefile#L129-L129
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dockerfiles/Dockerfile.apisix-runtime.deb` around lines 26 - 28, Separate
BuildKit cache entries by introducing a non-secret module-enabled build
argument: update dockerfiles/Dockerfile.apisix-runtime.deb lines 26-28 and
dockerfiles/Dockerfile.apisix-runtime.rpm lines 24-26 to consume it in their RUN
instructions; update Makefile lines 46-50 to derive 0 or 1 from token presence,
and pass that value via --build-arg at lines 107-115 and 129. Ensure the
argument distinguishes token-enabled and token-disabled builds without exposing
the secret.
Source: MCP tools
| | image_base | False | the environment for packaging, if type is `rpm` the default image_base is `centos`, if type is `deb` the default image_base is `ubuntu` | image_base=centos | | ||
| | image_tag | False | the environment for packaging, it's value can be `16.04\|18.04\|20.04\|6\|7\|8`, if type is `rpm` the default image_tag is `7`, if type is `deb` the default image_tag is `20.04` | image_tag=7 | | ||
| | buildx | False | if `True`, use buildx to build docker images, which may speed up GitHub Actions | buildx=True | | ||
| | NGX_HTTP_FFI_CLIENT_TOKEN | False | environment variable holding a token that can read `api7/ngx_http_ffi_client`. It is passed to the runtime build as a BuildKit secret and never as a build arg. Without it the runtime is built without that module | NGX_HTTP_FFI_CLIENT_TOKEN=ghp_xxx make package ... | |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Qualify when this parameter is optional.
The release workflow rejects a missing token before packaging. State that the token is optional for non-release builds but required for release builds.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` at line 21, Update the NGX_HTTP_FFI_CLIENT_TOKEN documentation in
README.md to state that it is optional for non-release builds but required for
release builds, while preserving the existing description of its BuildKit-secret
usage.
| `ngx_http_ffi_client` is the C HTTP client the AI plugins use for outbound LLM | ||
| requests. `api7/ngx_http_ffi_client` is a private repository, so the runtime | ||
| build fetches it only when `NGX_HTTP_FFI_CLIENT_TOKEN` is set, and otherwise | ||
| prints a warning and builds the runtime without it. `ai-proxy` falls back to | ||
| `lua-resty-http` on a runtime that does not carry the module, so both runtimes | ||
| work; only the outbound CPU cost differs. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
State the runtime compatibility boundary.
“Both runtimes work” is too broad. ngx_http_ffi_client targets OpenResty 1.29.2.4 with ngx_lua 0.10.31rc2 and is incompatible with OpenResty 1.21.4.4. Clarify that the fallback applies to a runtime built without the module, and identify which runtime variants can include it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 112 - 117, Update the README paragraph describing
ngx_http_ffi_client and ai-proxy to state the compatibility boundary explicitly:
the module can be included only in OpenResty 1.29.2.4 with ngx_lua 0.10.31rc2,
and is incompatible with OpenResty 1.21.4.4. Clarify that lua-resty-http
fallback applies only when the runtime is built without the module, rather than
claiming both runtime variants work universally.
The AI plugins send every outbound LLM request through ngx_http_ffi_client, so a runtime that lacks it is not a runtime worth shipping. Without NGX_HTTP_FFI_CLIENT_TOKEN the build now stops, before the OpenSSL and OpenResty builds start, rather than warning and producing a runtime with no module. That makes the release workflow's own token check redundant, so it goes away again; --add-module and the Lua install are now unconditional, like every other module here.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
build-apisix-runtime.sh (1)
163-180: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winSensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information
Reachability: Internal · Exploitability: Difficult
Disable redirects for the token-bearing fetch.
Git can reuse
http.extraHeaderon follow-up requests after adopting a redirected URL. This can sendNGX_HTTP_FFI_CLIENT_TOKENto another host or over HTTP. Sethttp.followRedirects=falsefor this fetch.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@build-apisix-runtime.sh` around lines 163 - 180, Update the token-bearing fetch in the ngx_http_ffi_client setup block to disable HTTP redirects by adding the Git configuration setting http.followRedirects=false alongside the existing http.extraheader option. Keep the token handling and fetch behavior unchanged.
🧹 Nitpick comments (1)
build-apisix-runtime.sh (1)
229-229: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winQuote the module path argument.
ShellCheck reports SC2086 for
--add-module=../${ngx_http_ffi_client_dir}. Quote the complete argument so it remains one path if the directory derivation changes.Suggested change
- --add-module=../${ngx_http_ffi_client_dir} \ + --add-module="../${ngx_http_ffi_client_dir}" \🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@build-apisix-runtime.sh` at line 229, Update the --add-module argument in the build configuration command to quote the complete ../${ngx_http_ffi_client_dir} path, preventing word splitting if the derived directory contains whitespace.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@build-apisix-runtime.sh`:
- Around line 98-105: Update the Makefile and runtime Dockerfile comments to
state that NGX_HTTP_FFI_CLIENT_TOKEN is required for fetching
api7/ngx_http_ffi_client, matching the validation in build-apisix-runtime.sh;
remove any wording that describes the token as optional.
---
Outside diff comments:
In `@build-apisix-runtime.sh`:
- Around line 163-180: Update the token-bearing fetch in the ngx_http_ffi_client
setup block to disable HTTP redirects by adding the Git configuration setting
http.followRedirects=false alongside the existing http.extraheader option. Keep
the token handling and fetch behavior unchanged.
---
Nitpick comments:
In `@build-apisix-runtime.sh`:
- Line 229: Update the --add-module argument in the build configuration command
to quote the complete ../${ngx_http_ffi_client_dir} path, preventing word
splitting if the derived directory contains whitespace.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6f795d0b-435c-4ebe-8cea-57c2538c297a
📒 Files selected for processing (3)
.github/workflows/release-apisix-runtime.ymlREADME.mdbuild-apisix-runtime.sh
💤 Files with no reviewable changes (1)
- .github/workflows/release-apisix-runtime.yml
🚧 Files skipped from review as they are similar to previous changes (1)
- README.md
|
|
||
| # fail here rather than after the OpenSSL and OpenResty builds | ||
| if [ "$repo" != ngx_http_ffi_client ] && [ "$ngx_http_ffi_client_have_token" == "no" ]; then | ||
| echo "ERROR: NGX_HTTP_FFI_CLIENT_TOKEN is required to fetch" \ | ||
| "api7/ngx_http_ffi_client, which this runtime must carry." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 5 \
'build-apisix-runtime\.sh|1\.21\.4\.4|1\.29\.2\.4|ngx_http_ffi_client|NGX_HTTP_FFI_CLIENT_TOKEN' \
.github/workflows Makefile dockerfilesRepository: api7/apisix-build-tools
Length of output: 12531
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- build script structure and relevant sections ---'
ast-grep outline build-apisix-runtime.sh
sed -n '1,210p' build-apisix-runtime.sh
sed -n '210,310p' build-apisix-runtime.sh
printf '%s\n' '--- runtime version and module configuration references ---'
rg -n -C 4 \
'OPENRESTY|openresty|1\.21\.4\.4|1\.29\.2\.4|ngx_lua|ffi_client|NGX_HTTP_FFI_CLIENT_TOKEN|build-apisix-runtime' \
Makefile dockerfiles .github/workflows build-apisix-runtime.shRepository: api7/apisix-build-tools
Length of output: 50379
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- exact version declarations and workflow inputs ---'
rg -n -C 8 \
'OPENRESTY_VERSION|OPENRESTY|openresty_version|runtime_version|BUILD_APISIX_RUNTIME_VERSION|1\.21\.4\.4|1\.29\.2\.4|ngx_lua' \
.github/workflows Makefile dockerfiles utils build-apisix-runtime.sh 2>/dev/null || true
printf '%s\n' '--- all module-related script lines ---'
rg -n -C 12 \
'repo=|ngx_http_ffi_client|NGX_HTTP_FFI_CLIENT_TOKEN|add-module|lua-resty|ffi' \
build-apisix-runtime.sh
printf '%s\n' '--- relevant build invocations and secret propagation ---'
rg -n -C 6 \
'build_apisix_runtime|build-apisix-runtime\.sh|ffi_client_secret|--secret|NGX_HTTP_FFI_CLIENT_TOKEN' \
Makefile dockerfiles .github/workflowsRepository: api7/apisix-build-tools
Length of output: 50379
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository refs and 1.21 revision contents ---'
git branch -a --no-color
git show-ref | rg 'openresty/1\.21|refs/remotes|refs/heads' || true
if git rev-parse --verify --quiet 'openresty/1.21.4^{commit}' >/dev/null; then
git show 'openresty/1.21.4:build-apisix-runtime.sh' | sed -n '1,45p;92,185p;210,280p'
fi
printf '%s\n' '--- build wrapper and version environment ---'
rg -n -C 10 \
'build_apisix_runtime|build-apisix-runtime\.sh|OPENRESTY_VERSION|runtime_version|openresty/1\.21' \
utils build-common.sh .github/workflows/package-apisix-runtime-deb-openresty-1.21.yml \
2>/dev/null || true
printf '%s\n' '--- exact 1.21 workflow ---'
cat -n .github/workflows/package-apisix-runtime-deb-openresty-1.21.ymlRepository: api7/apisix-build-tools
Length of output: 12883
🏁 Script executed:
#!/bin/bash
set -euo pipefail
remote='https://github.com/api7/apisix-build-tools.git'
branch='openresty/1.21.4'
base='https://raw.githubusercontent.com/api7/apisix-build-tools/openresty/1.21.4'
printf '%s\n' '--- remote branch resolution ---'
git ls-remote "$remote" "refs/heads/$branch" || true
printf '%s\n' '--- remote 1.21 build script ---'
curl --fail --silent --show-error "$base/build-apisix-runtime.sh" |
nl -ba |
sed -n '1,60p;90,185p;210,280p'
printf '%s\n' '--- remote 1.21 build wrapper and Makefile module handling ---'
curl --fail --silent --show-error "$base/utils/build-common.sh" |
nl -ba | sed -n '85,140p'
curl --fail --silent --show-error "$base/Makefile" |
nl -ba | sed -n '35,55p;95,135p;265,305p'Repository: api7/apisix-build-tools
Length of output: 410
🏁 Script executed:
#!/bin/bash
set -euo pipefail
base='https://raw.githubusercontent.com/api7/apisix-build-tools/openresty/1.21.4'
printf '%s\n' '--- remote 1.21 build script ---'
curl --fail --silent --show-error "$base/build-apisix-runtime.sh" |
sed -n '1,60p;90,185p;210,280p'
printf '%s\n' '--- remote 1.21 build wrapper ---'
curl --fail --silent --show-error "$base/utils/build-common.sh" |
sed -n '85,140p'
printf '%s\n' '--- remote 1.21 Makefile module handling ---'
curl --fail --silent --show-error "$base/Makefile" |
sed -n '35,55p;95,135p;265,305p'Repository: api7/apisix-build-tools
Length of output: 12804
Update the token documentation.
build-apisix-runtime.sh now exits when NGX_HTTP_FFI_CLIENT_TOKEN is absent. Update the Makefile and runtime Dockerfile comments that state the token is optional.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@build-apisix-runtime.sh` around lines 98 - 105, Update the Makefile and
runtime Dockerfile comments to state that NGX_HTTP_FFI_CLIENT_TOKEN is required
for fetching api7/ngx_http_ffi_client, matching the validation in
build-apisix-runtime.sh; remove any wording that describes the token as
optional.
The module was pinned by raw commit because the repository carried no tags. A tag is on the way, so it now follows the same shape as every other module here: a version variable and git clone --depth=1 -b, rather than git init plus a fetch of a bare SHA. A bare SHA is opaque in review and in openresty -V output, and it made "which client is in runtime 1.3.12" hard to answer. ngx_http_ffi_client_ver still overrides, but it now takes a tag or a branch rather than a commit.
membphis
left a comment
There was a problem hiding this comment.
Could we add a reproducible benchmark (or link to one in the paired gateway PRs) comparing ngx_http_ffi_client with lua-resty-http under the same workload? The PR description says the C client reduces outbound CPU time by roughly one third, but the supporting measurements are not included yet.
Please report at least the OpenResty/APISIX versions, hardware, request/response payloads, concurrency, keepalive/TLS settings, warm-up and measurement duration, plus throughput, p50/p95 latency, CPU time/utilization, memory, and error rate. A deterministic local upstream would help isolate client overhead from external LLM/network variance, and both cold-connection and keepalive cases would show where the gain comes from. This would make the motivation and the claimed benefit measurable and easier to validate.
api7/ngx_http_ffi_client is public now, so the token, the BuildKit secret and the conditional that carried it are all gone. What is left is the same three lines every other module here gets: a version variable and a shallow clone of its tag. The Makefile, both runtime Dockerfiles and the packaging workflows are back to exactly what they were before this branch touched them.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@build-apisix-runtime.sh`:
- Around line 39-44: Update the ngx_http_ffi_client_dir construction to replace
slashes in ngx_http_ffi_client_ver with a safe separator, while keeping
ngx_http_ffi_client_ver unchanged for Git checkout operations. Preserve the
existing validation and version-derived directory naming behavior.
In `@README.md`:
- Around line 109-120: Make the documented ngx_http_ffi_client_ver override
functional by defining and forwarding it through the packaging flow, including
build-apisix-runtime.sh and the relevant Makefile target or variable
propagation. Ensure the supplied tag or branch reaches the ngx_http_ffi_client
build; otherwise remove the override example from the README.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: db07473e-d5f7-4394-8a04-4dbc75e6aa0d
📒 Files selected for processing (2)
README.mdbuild-apisix-runtime.sh
| ### ngx_http_ffi_client | ||
|
|
||
| `ngx_http_ffi_client` is the C HTTP client the AI plugins use for outbound LLM | ||
| requests. The version is pinned by `ngx_http_ffi_client_ver` in | ||
| `build-apisix-runtime.sh`, as it is for every other module. Set it to build | ||
| against a different tag or branch: | ||
|
|
||
| ```sh | ||
| ngx_http_ffi_client_ver=v0.2.0 \ | ||
| make package type=deb app=apisix-runtime version=1.0.0 | ||
| ``` | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
files=$(fd -t f . | rg '(^|/)(Makefile|Dockerfile[^/]*|.*\.(mk|yml|yaml))$' || true)
if [ -n "$files" ]; then
while IFS= read -r file; do
rg -n -C 6 \
'ngx_http_ffi_client_ver|build-apisix-runtime\.sh|--build-arg|ARG |ENV ' \
"$file" || true
done <<< "$files"
fiRepository: api7/apisix-build-tools
Length of output: 2067
🏁 Script executed:
set -euo pipefail
for path in Makefile build-apisix-runtime.sh Dockerfile; do
url="https://raw.githubusercontent.com/api7/apisix-build-tools/master/$path"
echo "=== $path ==="
curl -fsSL "$url" 2>/dev/null | rg -n -C 8 \
'ngx_http_ffi_client_ver|build-apisix-runtime\.sh|--build-arg|ARG |ENV ' || true
doneRepository: api7/apisix-build-tools
Length of output: 2038
🌐 Web query:
api7 apisix-build-tools ngx_http_ffi_client_ver build-apisix-runtime.sh Makefile
💡 Result:
The components you listed are part of the core build infrastructure for Apache APISIX, maintained primarily within the api7/apisix-build-tools repository. This ecosystem is designed to automate the complex process of compiling and packaging custom OpenResty distributions specifically optimized for APISIX [1][2]. 1. api7/apisix-build-tools: This is the primary repository containing the scripts and utilities required to package and release APISIX distributions, including the specialized apisix-base and apisix-runtime variants [1][2]. 2. build-apisix-runtime.sh: This is a core shell script in the build-tools repository used to compile the apisix-runtime variant [3]. Unlike the standard OpenResty, this runtime is optimized for production, featuring advanced cryptographic support (such as OpenSSL 3.x and FIPS compliance) and specific compiler flags (cc-opt/ld-opt) to enable APISIX-specific functionality like memory safety and performance tuning [3][2][4]. 3. ngx_http_ffi_client_ver: While you noted this identifier, it appears to be a misinterpretation of FFI (Foreign Function Interface) naming conventions commonly found in OpenResty modules [5][6]. In the context of APISIX and OpenResty, FFI is widely used (via libraries like lua-resty-ffi) to bridge Lua with high-performance languages (e.g., Rust, Go) for gRPC clients, SSL/TLS certificate verification, and other performance-critical tasks [6][7][8]. 4. Makefile: In this build system, the Makefile (and the underlying build scripts) orchestrates the compilation of Nginx and various essential modules, including apisix-nginx-module, lua-var-nginx-module, and wasm-nginx-module [9][3][2]. It handles the configuration of the OpenResty environment, ensuring all dependencies and paths are correctly set for the specific APISIX installation [9][2]. In summary, these tools form the foundation for maintaining the "FFI-heavy" architecture of APISIX, allowing it to leverage high-performance external modules while remaining compatible with the OpenResty/Nginx core [2][10].
Citations:
- 1: https://github.com/api7/apisix-build-tools
- 2: https://deepwiki.com/api7/apisix-build-tools/3-core-build-system
- 3: https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-runtime.sh
- 4: https://apisix.apache.org/docs/apisix/support-fips-in-apisix/
- 5: feature: add FFI interface to verify SSL client certificate openresty/lua-nginx-module#1666
- 6: https://github.com/kingluo/lua-resty-ffi
- 7: http://luajit.io/posts/implement-grpc-client-in-rust-for-openresty/
- 8: https://github.com/kingluo/lua-resty-ffi-grpc
- 9: https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-base.sh
- 10: Thinking about the next 15 years: A discussion on APISIX’s architectural foundations (OpenResty/Nginx/LuaJIT) apache/apisix#13124
🌐 Web query:
site:github.com/api7/apisix-build-tools/blob Makefile ngx_http_ffi_client_ver
💡 Result:
There is no standard or widely documented variable or entity named ngx_http_ffi_client_ver in public Nginx, OpenResty, or Apache APISIX source code or build documentation [1][2][3][4][5][6]. Based on the available technical records, the term likely does not exist in the official Nginx/OpenResty ecosystem. Build scripts for related projects, such as the api7/apisix-build-tools repository, use various version variables to manage dependencies (e.g., ngx_multi_upstream_module_ver, apisix_nginx_module_ver, lua_resty_events_ver) during the build and configuration process [2][4]. It is possible that ngx_http_ffi_client_ver is a custom, project-specific, or typo-derived variable within a private or local build environment, as it does not appear in the authoritative open-source repositories for these technologies [1][2][4].
Citations:
- 1: https://github.com/api7/apisix-build-tools/blob/master/Makefile
- 2: https://github.com/api7/apisix-build-tools/blob/a2e6806e/build-apisix-base.sh
- 3: https://github.com/api7/apisix-build-tools/blob/master/README.md
- 4: https://github.com/api7/apisix-build-tools/blob/a2e6806e/build-apisix-runtime.sh
- 5: https://github.com/api7/apisix-build-tools/blob/master/package-apisix.sh
- 6: https://github.com/api7/apisix-build-tools/blob/a2e6806e/package-apisix.sh
🌐 Web query:
site:github.com/api7/apisix-build-tools "ngx_http_ffi_client_ver"
💡 Result:
The term ngx_http_ffi_client_ver does not appear as a standard public API or documented constant within NGINX or OpenResty. It is likely a misinterpreted or internal-only reference related to custom builds or specific third-party module integrations within the Apache APISIX ecosystem [1][2]. In the context of the apisix-build-tools repository, users often encounter errors related to "client restriction" or requirements to build a custom "APISIX-OpenResty" (often referred to as apisix-base) [2]. This is typically required to support specific features like dynamic SSL client certificate verification, which relies on FFI (Foreign Function Interface) calls introduced in modules like lua-nginx-module to interact with OpenSSL [3][4]. If you are encountering this in a build script or error message, it is likely a result of: 1. Custom C compiler flags (-D...) used during the OpenResty/NGINX build process, which inject custom macros into the binary [5][6][7]. 2. A specific requirement for the APISIX client-control plugin, which depends on features (such as dynamic SSL verification) that are not present in standard, pre-packaged OpenResty distributions and must be enabled by compiling a custom runtime [3][2]. To resolve issues related to missing FFI functionality for APISIX, you generally need to build the apisix-base package using the official build tools provided by the APISIX community, which include the necessary patches and module integrations [1][2].
Citations:
- 1: https://github.com/api7/apisix-build-tools
- 2: How can I build APISIX-OpenRestry deb or docker image #109
- 3: feature: add FFI interface to verify SSL client certificate openresty/lua-nginx-module#1666
- 4: https://github.com/kingluo/lua-resty-ffi
- 5: https://github.com/api7/apisix-build-tools/blob/a2e6806e/build-apisix-base.sh
- 6: https://github.com/api7/apisix-build-tools/blob/a2e6806e/build-apisix-runtime.sh
- 7: [help request] apisix-base rpm not compile with ipv6 module #305
🌐 Web query:
site:github.com/api7/apisix-build-tools "build-apisix-runtime.sh" "make package"
💡 Result:
The command "make package" is used in the context of the apisix-build-tools repository to generate installable packages (deb, rpm, or apk) for various Apache APISIX components, including the apisix-runtime [1][2]. The "build-apisix-runtime.sh" script is a internal component used by the Makefile process, not a command typically invoked directly with "make package" as an argument [3][4]. To package the APISIX runtime, you should use the make command at the root of the apisix-build-tools repository with the appropriate parameters [1][2]. Examples: For an RPM package (CentOS 7): make package type=rpm app=apisix-runtime version=1.0.0 image_base=centos image_tag=7 For a DEB package (Ubuntu 20.04): make package type=deb app=apisix-runtime version=1.0.0 The makefile orchestrates the build process, calling scripts like build-apisix-runtime.sh internally to handle compilation and packaging tasks [4]. Direct execution of build-apisix-runtime.sh is generally for debugging or custom build scenarios and does not support a "make package" subcommand [3].
Citations:
- 1: https://github.com/api7/apisix-build-tools/blob/master/README.md
- 2: https://github.com/api7/apisix-build-tools
- 3: https://github.com/api7/apisix-build-tools/blob/a2e6806e/build-apisix-runtime.sh
- 4: https://github.com/api7/apisix-build-tools/blob/master/Makefile
🌐 Web query:
https://raw.githubusercontent.com/api7/apisix-build-tools/master/Makefile ngx_http_ffi_client_ver
💡 Result:
The term ngx_http_ffi_client_ver does not appear as a standard identifier in the official api7/apisix-build-tools Makefile or its associated build scripts [1][2][3][4]. The apisix-build-tools project uses a set of shell scripts (such as build-apisix-base.sh and build-apisix-runtime.sh) to configure and compile OpenResty for APISIX [1][2][3]. These scripts manage module versions and build flags using variables like APISIX_BASE_VER, APISIX_RUNTIME_VER, and various module-specific version variables (e.g., lua_resty_events_ver) [2][3][5]. It is possible that ngx_http_ffi_client_ver is a misremembered or non-existent identifier, or perhaps a custom variable added in a specific, non-public, or older fork. If you are looking for OpenResty FFI (Foreign Function Interface) related identifiers, the openresty/lua-nginx-module codebase uses various FFI functions prefixed with ngx_http_lua_ffi_ (e.g., ngx_http_lua_ffi_ssl_ciphers or ngx_http_lua_ffi_ssl_get_client_hello_ext_present), but these are part of the Nginx module source code rather than the APISIX build tools' Makefile [6][7][8].
Citations:
- 1: https://github.com/api7/apisix-build-tools
- 2: https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-base.sh
- 3: https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-runtime.sh
- 4: https://github.com/api7/apisix-build-tools/blob/master/Makefile
- 5: https://deepwiki.com/api7/apisix-build-tools/3-core-build-system
- 6: feature: ngx_http_lua_ffi_ssl_get_client_hello_ext_present() openresty/lua-nginx-module#2414
- 7: feature: ngx_http_lua_ffi_ssl_ciphers openresty/lua-nginx-module#1962
- 8: feature: add FFI interface to verify SSL client certificate openresty/lua-nginx-module#1666
🌐 Web query:
https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-runtime.sh ngx_http_ffi_client_ver
💡 Result:
The term ngx_http_ffi_client_ver does not appear as a standard identifier, variable, or API call within the referenced apisix-build-tools scripts or broader OpenResty/APISIX ecosystems [1][2][3]. It is highly likely that this is either a typo or a misremembered variable name, as the build scripts typically define module versions using patterns like ${module_name}ver (e.g., apisix_nginx_module_ver) [1][2]. In the context of OpenResty and APISIX development, FFI (Foreign Function Interface) is commonly used to bridge Lua and C/C++ [4][5]. While there are various FFI-related functions for handling SSL/TLS client certificate verification (often named with prefixes like ngx_http_lua_kong_ffi... or related to the lua-nginx-module FFI interface) [6][7], there is no established technical identifier matching ngx_http_ffi_client_ver [6][4][7]. If you are investigating build-tool configurations for APISIX, you may be thinking of: 1. A specific module version variable defined in the scripts, such as apisix_nginx_module_ver or similar [1][2]. 2. Internal OpenResty FFI APIs used for SSL verification, which typically do not contain "client_ver" in their names [6][7]. If this was intended to refer to a specific software version check, it is recommended to verify the exact spelling against the target build script repository.
Citations:
- 1: https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-runtime.sh
- 2: https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-base.sh
- 3: https://raw.githubusercontent.com/api7/apisix-build-tools/v2.2.1/build-apisix-openresty.sh
- 4: https://github.com/kingluo/lua-resty-ffi
- 5: https://dev.to/kingluo/implement-grpc-client-in-rust-language-for-openrestynginx-4i59
- 6: feature: add FFI interface to verify SSL client certificate openresty/lua-nginx-module#1666
- 7: https://github.com/Kong/lua-kong-nginx-module/blob/master/lualib/resty/kong/tls.lua
🌐 Web query:
https://raw.githubusercontent.com/api7/apisix-build-tools/master/README.md ngx_http_ffi_client_ver
💡 Result:
The term "ngx_http_ffi_client_ver" does not refer to a standard, widely documented feature or configuration parameter in the apisix-build-tools repository or the broader OpenResty/APISIX ecosystem. The apisix-build-tools repository is primarily used for managing the build process, dependencies, and packaging of APISIX and its base OpenResty distributions (apisix-base and apisix-runtime) [1][2][3]. Its configuration files, such as build-apisix-base.sh and build-apisix-runtime.sh, handle version management for components like OpenResty, OpenSSL, and various Nginx modules, but they do not contain a parameter or variable named ngx_http_ffi_client_ver [4][5]. In the context of Nginx and OpenResty, FFI (Foreign Function Interface) is commonly used to call C functions from Lua, often prefixed with ngx_http_lua_ffi_ to denote Lua-Nginx module FFI interfaces [6][7][8]. While various FFI functions exist for SSL/TLS and upstream management, there is no evidence of an interface or variable designated as ngx_http_ffi_client_ver [9][6][7][10][8]. It is possible that this string is a typo or a custom, internal variable name not reflected in public documentation or the main codebase.
Citations:
- 1: https://deepwiki.com/api7/apisix-build-tools/3-core-build-system
- 2: https://github.com/api7/apisix-build-tools/blob/master/README.md
- 3: https://github.com/api7/apisix-build-tools
- 4: https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-base.sh
- 5: https://raw.githubusercontent.com/api7/apisix-build-tools/master/build-apisix-runtime.sh
- 6: feature: add FFI interface to verify SSL client certificate openresty/lua-nginx-module#1666
- 7: feature: add ngx_http_lua_ffi_ssl_client_random. openresty/lua-nginx-module#2284
- 8: feature: ngx_http_lua_ffi_ssl_get_client_hello_ciphers() openresty/lua-nginx-module#2419
- 9: api7/apisix-nginx-module@555c644
- 10: feature: add ssl trusted certificate openresty/lua-nginx-module#2329
🌐 Web query:
site:github.com/api7/apisix-build-tools "v0.1.0" "ffi" "client"
💡 Result:
There is no widely recognized, specific software component identified as a v0.1.0 FFI (Foreign Function Interface) client in authoritative technical documentation or the searched repositories [1][2][3]. While the term FFI is commonly used in contexts involving the APISIX build tools—specifically regarding how various Nginx modules or language-specific bindings interact with C-based components—the version v0.1.0 does not map to a standalone entity labeled as a "ffi client" [4][5][3]. In the scope of the api7/apisix-build-tools repository, v0.1.0 has been associated with specific module releases, such as the wasm-nginx-module [3]. It is possible that "v0.1.0 ffi client" refers to an early-stage or internal library associated with FFI-based integration (such as gRPC or Wasm modules) within the APISIX ecosystem, but no official documentation exists to define it as a singular, public-facing client tool [3][6].
Citations:
- 1: https://github.com/api7/apisix-build-tools
- 2: https://github.com/api7/apisix-build-tools/releases/tag/apisix-runtime%2F1.3.8
- 3: https://github.com/api7/apisix-build-tools/blob/master/CHANGELOG.md
- 4: https://github.com/api7/apisix-build-tools/blob/a2e6806e/build-apisix-base.sh
- 5: https://github.com/api7/apisix-build-tools/blob/a2e6806e/build-apisix-runtime.sh
- 6: [help request] apisix-base rpm not compile with ipv6 module #305
🌐 Web query:
site:github.com/api7/apisix-build-tools "ngx_http_ffi_client" OR "ffi_client"
💡 Result:
The term ngx_http_ffi_client does not refer to a standard or widely recognized component in the mainline Nginx or OpenResty ecosystem. Based on available technical evidence, it is likely a misinterpretation or a highly specific, non-standard naming convention used within internal or experimental modules, possibly related to projects using lua-resty-ffi [1][2]. Contextual findings: 1. lua-resty-ffi: This is a well-known project that provides an efficient, generic API for hybrid programming in OpenResty, allowing integration with languages like Go, Python, Rust, and others [1][2]. It defines various FFI (Foreign Function Interface) APIs for communication between OpenResty (Lua) and external runtime tasks, such as ngx_http_lua_ffi_task_poll and ngx_http_lua_ffi_respond [1]. 2. Nginx Client Modules: Mainline Nginx uses ngx_http_request_t and related structures for internal HTTP handling [3]. While experimental modules or downstream forks (like Angie) may include their own ngx_http_client APIs for managing requests, these are distinct from the FFI-based integration patterns [3]. 3. Apache APISIX: Build tools for Apache APISIX often compile custom OpenResty versions (sometimes referred to as apisix-base or apisix-runtime) that include various third-party modules (e.g., grpc-client-nginx-module) [4][5]. These modules facilitate complex interactions but do not typically expose a component literally named ngx_http_ffi_client [4]. If you are encountering this term in a specific codebase, it is likely a local or library-specific identifier for an FFI-based client implementation [1][6]. Check the specific module documentation or header files within that project for the exact definition.
Citations:
- 1: https://github.com/kingluo/lua-resty-ffi
- 2: https://github.com/Revolyssup/lua-resty-ffi-req
- 3: https://github.com/webserver-llc/angie/blob/371c9294/src/http/ngx_http_client.h
- 4: [help request] apisix-base rpm not compile with ipv6 module #305
- 5: How can I build APISIX-OpenRestry deb or docker image #109
- 6: https://github.com/kingluo/lua-resty-ffi-grpc
Forward ngx_http_ffi_client_ver through packaging.
ngx_http_ffi_client_ver is not defined or consumed by the Makefile or build-apisix-runtime.sh, so this command cannot select v0.2.0. Add support for the variable or remove the example.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 109 - 120, Make the documented
ngx_http_ffi_client_ver override functional by defining and forwarding it
through the packaging flow, including build-apisix-runtime.sh and the relevant
Makefile target or variable propagation. Ensure the supplied tag or branch
reaches the ngx_http_ffi_client build; otherwise remove the override example
from the README.
|
Full container build on a29cb57, run with no credentials in the environment at all: The resulting image: |
What this does
Builds
apisix-runtimewithngx_http_ffi_clientv0.1.0.It is the C HTTP client the AI plugins use for outbound LLM requests. On the paired gateway PRs (apache/apisix#13778 and api7/api7-ee-3-gateway#2075),
ai-proxy,ai-proxy-multiandai-request-rewriteprefer it overlua-resty-http. The module has to be in the runtime for any of that to take effect.It is added the same way as every other module here: a version variable and a shallow clone of its tag.
Performance
Full benchmark record:
benchmark/results-full.md(run 2026-08-04, OpenResty 1.29.2.4, 8-core VM with the target worker pinned to one core, 14 request shapes, 5 repeats each, median QPS).resty.httptrailers28005.246622.054.23x5.77xoneshot30925.8414510.372.13x2.76xhdr4017724.689218.371.92x2.16xpost24173.1612780.461.89x2.24xstream27506.0516236.651.69x2.02xtls18655.4611866.501.57x1.73xshort8721.097152.201.22x1.24xtlsshort932.161149.500.81x0.81xWorth reading the last row rather than only the first: on
tlsshort, a fresh TLS handshake per request, the FFI client is slower, at0.81x. Everything else in the matrix is a win, and the shapes the AI gateway actually runs (stream,post,chunked,readbody, all on pooled connections) sit around1.7xto1.9x, roughly2xon outbound CPU. The fairness audit in that record passed for all 14 shapes, so both clients sent the same request line and the same caller-supplied headers.Verification
--add-moduleagainst thev0.1.0tag: compiles clean, and the module's own Test::Nginx suite passes in full (380 tests across 15 files).make build-apisix-runtime-debend to end. The resulting image carries the module three ways:openresty -Vlists--add-module=../ngx_http_ffi_client-v0.1.0,/usr/local/openresty/lualib/resty/ngx_http_ffi_client.luais installed, and the FFI entry points are in the nginx binary.ai-transport/http.luauses (require->new()->connect->request->read_body->set_keepalive) against a local upstream, and got a live200.Summary by CodeRabbit
New Features
ngx_http_ffi_clientdependency when building the runtime.Documentation