Skip to content

feat: build apisix-runtime with ngx_http_ffi_client - #481

Merged
shreemaan-abhishek merged 6 commits into
masterfrom
feat/apisix-runtime-ngx-http-ffi-client
Aug 6, 2026
Merged

feat: build apisix-runtime with ngx_http_ffi_client#481
shreemaan-abhishek merged 6 commits into
masterfrom
feat/apisix-runtime-ngx-http-ffi-client

Conversation

@shreemaan-abhishek

@shreemaan-abhishek shreemaan-abhishek commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What this does

Builds apisix-runtime with ngx_http_ffi_client v0.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-multi and ai-request-rewrite prefer it over lua-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).

shape C FFI resty.http ratio outbound cost ratio
trailers 28005.24 6622.05 4.23x 5.77x
oneshot 30925.84 14510.37 2.13x 2.76x
hdr40 17724.68 9218.37 1.92x 2.16x
post 24173.16 12780.46 1.89x 2.24x
stream 27506.05 16236.65 1.69x 2.02x
tls 18655.46 11866.50 1.57x 1.73x
short 8721.09 7152.20 1.22x 1.24x
tlsshort 932.16 1149.50 0.81x 0.81x

Worth reading the last row rather than only the first: on tlsshort, a fresh TLS handshake per request, the FFI client is slower, at 0.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 around 1.7x to 1.9x, roughly 2x on 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

  • Built OpenResty 1.29.2.4 with --add-module against the v0.1.0 tag: compiles clean, and the module's own Test::Nginx suite passes in full (380 tests across 15 files).
  • Built the same way against OpenResty 1.21.4.4 to confirm the version floor above: fails on the three missing symbols.
  • Ran make build-apisix-runtime-deb end to end. The resulting image carries the module three ways: openresty -V lists --add-module=../ngx_http_ffi_client-v0.1.0, /usr/local/openresty/lualib/resty/ngx_http_ffi_client.lua is installed, and the FFI entry points are in the nginx binary.
  • Inside that image, drove the exact sequence ai-transport/http.lua uses (require -> new() -> connect -> request -> read_body -> set_keepalive) against a local upstream, and got a live 200.

Summary by CodeRabbit

  • New Features

    • Added support for configuring and packaging the ngx_http_ffi_client dependency when building the runtime.
    • Supports using either a local checkout or a specified tagged version.
    • Added validation and installation of the dependency’s Lua FFI binding.
  • Documentation

    • Documented the dependency, version configuration, and packaging examples.

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.
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The runtime build now validates and retrieves ngx_http_ffi_client, configures it into OpenResty, installs its Lua FFI binding, and documents version overrides for apisix-runtime packaging.

Changes

ngx_http_ffi_client runtime integration

Layer / File(s) Summary
Runtime module build
build-apisix-runtime.sh
The script validates the configured version, uses a local checkout or shallow tagged clone, passes the Lua module directory to the build, configures OpenResty, and installs the Lua FFI binding.
Runtime build documentation
README.md
The README documents ngx_http_ffi_client, ngx_http_ffi_client_ver, and an apisix-runtime packaging example.

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
Loading

Possibly related PRs

Suggested reviewers: jarvis9443

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning Blocking: the PR adds no automated E2E test. Existing CI only installs the package and checks openresty -V; it does not exercise ngx_http_ffi_client against an upstream. Add a CI E2E test that builds and installs the runtime, runs a local upstream, invokes connect/request/read_body/set_keepalive, and asserts status, body, headers, and reuse.
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Check ✅ Passed Changed build paths contain no token or secret handling, logging, persistence, or authorization logic; the module uses a public HTTPS tag, and token/header/secret references are absent.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes adding ngx_http_ffi_client to the apisix-runtime build.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/apisix-runtime-ngx-http-ffi-client

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 95406eb and 1f18875.

📒 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.yml
  • Makefile
  • README.md
  • build-apisix-runtime.sh
  • dockerfiles/Dockerfile.apisix-runtime.deb
  • dockerfiles/Dockerfile.apisix-runtime.rpm

Comment thread .github/workflows/package-apisix-runtime-deb-openresty-1.21.yml Outdated
Comment thread build-apisix-runtime.sh Outdated
Comment on lines +42 to +46
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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)
PY

Repository: 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))
PY

Repository: 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.

Comment on lines +26 to +28
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 ./ \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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 the RUN instruction.
  • dockerfiles/Dockerfile.apisix-runtime.rpm#L24-L26: use the same non-secret build argument in the RUN instruction.
  • Makefile#L46-L50: derive a 0 or 1 module-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-L26
  • Makefile#L46-L50
  • Makefile#L107-L115
  • Makefile#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

Comment thread README.md Outdated
| 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 ... |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

Comment thread README.md Outdated
Comment on lines +112 to +117
`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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Sensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: Internal · Exploitability: Difficult

Disable redirects for the token-bearing fetch.

Git can reuse http.extraHeader on follow-up requests after adopting a redirected URL. This can send NGX_HTTP_FFI_CLIENT_TOKEN to another host or over HTTP. Set http.followRedirects=false for 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 win

Quote 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1f18875 and 49a866f.

📒 Files selected for processing (3)
  • .github/workflows/release-apisix-runtime.yml
  • README.md
  • build-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

Comment thread build-apisix-runtime.sh Outdated
Comment on lines +98 to +105

# 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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 dockerfiles

Repository: 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.sh

Repository: 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/workflows

Repository: 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.yml

Repository: 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 membphis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 49a866f and a29cb57.

📒 Files selected for processing (2)
  • README.md
  • build-apisix-runtime.sh

Comment thread build-apisix-runtime.sh
Comment thread README.md
Comment on lines +109 to +120
### 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
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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"
fi

Repository: 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
done

Repository: 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:


🌐 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:


🌐 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:


🌐 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:


🌐 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:


🌐 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:


🌐 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:


🌐 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:


🌐 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:


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.

@shreemaan-abhishek

Copy link
Copy Markdown
Contributor Author

Full container build on a29cb57, run with no credentials in the environment at all:

+ ngx_http_ffi_client_dir=ngx_http_ffi_client-v0.1.0
+ git clone --depth=1 -b v0.1.0 https://github.com/api7/ngx_http_ffi_client.git ngx_http_ffi_client-v0.1.0
Cloning into 'ngx_http_ffi_client-v0.1.0'...
EXIT=0

The resulting image:

openresty -V   -> --add-module=.../ngx_http_ffi_client-v0.1.0
                  /usr/local/openresty/lualib/resty/ngx_http_ffi_client.lua  (25501 bytes)
                  ngx_http_ffi_client_ffi_request present in the nginx binary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants