Skip to content

About

A Lambda native gateway with cross-cutting concerns implemented in Java

Resources

Stars

3 stars

Watchers

13 watching

Forks

Repository files navigation

Build and package

Use Maven with JDK 25 (the Java version declared in pom.xml):

mvn clean verify

The executable shaded JAR is target/lambda-native-<version>.jar, with the version taken from pom.xml. Customers can use this JAR with a compatible GraalVM Native Image installation to build their own native binary.

To build the Linux native binary and AWS Lambda custom-runtime ZIP, install Docker and zip, then run:

bash native-image-linux.sh

The script runs mvn clean install, reads the JAR name and Java version from Maven, and uses ghcr.io/graalvm/native-image-community:<maven.compiler.target>. Set GRAALVM_IMAGE to select a pinned image compatible with that Java version. Docker builds for its host architecture; use a host matching your Lambda architecture and verify the resulting binary against your chosen Lambda runtime. The ZIP is lambda-native-custom-runtime.zip and contains executable lambda-native and bootstrap files. HTTP and HTTPS protocols are enabled. Build or packaging failures stop the script with a nonzero exit status.

Run the native-build script regression tests separately with Python 3 and zip:

python3 tests/test_native_image_script.py

These tests stub Maven and Docker to check packaging and failure handling. They are not run by Maven. The source-build CI workflow runs them separately from the real native smoke.

Test with Main

We have provided a Main class in the src folder for testing all the middleware handlers with a sample request. You can run the Main class within your IDE with debug mode to see how each middleware handler works.

Before running the Main class, you need to disable the DynamoDbCacheManager as it depends on the AWS environment. To do that you need to common out the service.yml section in the values.yml

# ------------< Service Config >-------------
service.singletons:
#  - com.networknt.cache.CacheManager:
#      - com.networknt.aws.lambda.cache.DynamoDbCacheManager
# -------------------------------------------

If you want to enable the JWT verification, you need to make sure the token use in the Main class is matching the jwk configuration in the values.yml

# client.yml
client.tokenKeyServerUrl: https://networknt.oktapreview.com
client.tokenKeyUri: /oauth2/aus66u5cybTrCsbZs1d6/v1/keys

If everything works fine, you should see the following error that indicate all middleware handlers are passed and the AWS Lambda invocation is failed. This is because we are testing without AWS environment.

software.amazon.awssdk.services.lambda.model.ResourceNotFoundException: Function not found: arn:aws:lambda:ca-central-1:964637446810:function:PetstoreNativeLambdaProxyFunction (Service: Lambda, Status Code: 404, Request ID: daefb25c-6d31-4efa-b662-69bc2f0fc87d)
	at software.amazon.awssdk.core.internal.http.CombinedResponseHandler.handleErrorResponse(CombinedResponseHandler.java:125)
	at software.amazon.awssdk.core.internal.http.CombinedResponseHandler.handleResponse(CombinedResponseHandler.java:82)
	at software.amazon.awssdk.core.internal.http.CombinedResponseHandler.handle(CombinedResponseHandler.java:60)
	at software.amazon.awssdk.core.internal.http.CombinedResponseHandler.handle(CombinedResponseHandler.java:41)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.HandleResponseStage.execute(HandleResponseStage.java:50)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.HandleResponseStage.execute(HandleResponseStage.java:38)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptTimeoutTrackingStage.execute(ApiCallAttemptTimeoutTrackingStage.java:72)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptTimeoutTrackingStage.execute(ApiCallAttemptTimeoutTrackingStage.java:42)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.TimeoutExceptionHandlingStage.execute(TimeoutExceptionHandlingStage.java:78)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.TimeoutExceptionHandlingStage.execute(TimeoutExceptionHandlingStage.java:40)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptMetricCollectionStage.execute(ApiCallAttemptMetricCollectionStage.java:55)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptMetricCollectionStage.execute(ApiCallAttemptMetricCollectionStage.java:39)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:81)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:36)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:56)
	at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:36)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.executeWithTimer(ApiCallTimeoutTrackingStage.java:80)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:60)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:42)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:50)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:32)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:37)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:26)
	at software.amazon.awssdk.core.internal.http.AmazonSyncHttpClient$RequestExecutionBuilderImpl.execute(AmazonSyncHttpClient.java:224)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.invoke(BaseSyncClientHandler.java:103)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.doExecute(BaseSyncClientHandler.java:173)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.lambda$execute$1(BaseSyncClientHandler.java:80)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.measureApiCallSuccess(BaseSyncClientHandler.java:182)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.execute(BaseSyncClientHandler.java:74)
	at software.amazon.awssdk.core.client.handler.SdkSyncClientHandler.execute(SdkSyncClientHandler.java:45)
	at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.execute(AwsSyncClientHandler.java:53)
	at software.amazon.awssdk.services.lambda.DefaultLambdaClient.invoke(DefaultLambdaClient.java:2792)
	at com.networknt.aws.lambda.handler.middleware.proxy.LambdaProxyMiddleware.invokeFunction(LambdaProxyMiddleware.java:104)
	at com.networknt.aws.lambda.handler.middleware.proxy.LambdaProxyMiddleware.execute(LambdaProxyMiddleware.java:66)
	at com.networknt.aws.lambda.handler.middleware.MiddlewareRunnable.run(MiddlewareRunnable.java:23)
	at java.base/java.lang.Thread.run(Thread.java:829)
	at com.networknt.aws.lambda.handler.chain.ChainLinkWorker.run(ChainLinkWorker.java:21)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)

Lambda Native Proxy

Middleware Handlers

Jackson 3 migration

This source line uses Jackson 3.2.3 and json-schema-validator 3.0.8. Build the Jackson 3 light-4j, http-client, REST and AWS Lambda prerequisites first; older snapshot binaries are not compatible. Jackson annotations retain their com.fasterxml.jackson.annotation package.

The runtime uses the event-only LambdaRuntimeMappers.mapper() API from custom-runtime, built from JacksonDefaults with its permissive unknown-property override. Mapper instances are immutable. Java time and Optional support are built in. Classic SnakeYAML compatibility pins are retained until the stack-wide cleanup.

Local qualification includes the full JVM suite, sanitized event/validation parity against the Jackson 2 baseline, and an offline GraalVM 25 native-image build. A native-image build does not establish deployed AWS behavior or complete reflection coverage for every middleware/configuration. The separate conformance worktree is deferred.

Native qualification now invokes the executable against a synthetic loopback Lambda Runtime API:

MAVEN_REPO_LOCAL=/path/to/isolated/m2 bash native-image-linux.sh
python3 tests/native_runtime_smoke.py target/custom-runtime/lambda-native

The smoke check binds API Gateway events with an unknown future field and runs specification, validator and health handlers. It checks valid referenced query/header/body schemas, invalid query/header/body values, an unknown path, a method mismatch, and builder-based error serialization. The OpenAPI document and handler/validator settings are controlled fixtures. The status catalog and injection settings are copied from the packaged application's config/status.yml and config/config.yml; their contents are not duplicated in the fixture. By default the script requires exactly one shaded target/lambda-native-*.jar (excluding sources and Javadoc JARs); use --application-jar /path/to/runtime.jar to select one explicitly.

It uses no AWS endpoint or credentials. -H:+AddAllCharsets is required by SnakeYAML Engine's UTF-32 input detection. The check does not qualify every middleware chain, all default-resource packaging, or deployed AWS behavior.

The native reflection file currently includes version-specific JSON overlay sideband accessors (MapOverlay$1.getOverlay() and ListOverlay$1.getOverlay()) and the OpenAPI path cache's Caffeine factories (SSMS.FACTORY and PSMS()), covered by this smoke. These remain application registrations until their owning libraries ship equivalent native metadata. Migrate native consumers together and remove these entries only after updated source pins and native smoke results establish equivalent coverage; moving them is separate from this mapper change.

Reproducible Jackson 3 source build

.github/workflows/jackson3-source-build.yml builds the prerequisite source stack in an isolated Maven repository, reuses independently qualified full-core test evidence, and runs the prerequisite and full consumer suites. It pins reviewed policy f0d2100f972eec765b2b276e7ace38922394f0a6 and candidate bc3e4b5e2719d705d58a2edeabeb9bb5009c7401; yaml-rule/http-client are selected from that verified evidence. The complete prerequisite closure is pinned in ci/jackson3-source-refs.json, initialized from the successful source-build manifest, with reviewed updates expressed as full SHAs; no default-branch or migration-branch lookup is used. Explicit source_refs replay overrides are merged into that map and remain subject to core qualification. The workflow uploads source-refs.json, source-commits.txt, core-qualification.json and Surefire reports. Core registration and workflow-token acceptance are merged in light-4j #2825 and #2826.

The optional source_refs workflow-dispatch input replays recorded full SHAs. GitHub enables dispatch only after the workflow exists on the default branch; before merge, reproduce from the recorded source checkouts locally. A green local suite is separate from a successful remote source-build run.

Exact-source CI qualification

Consumer CI requires retained successful full-reactor evidence for the actual checked-out light-4j SHA and the same yaml-rule/http-client source commits. Core PR CI explicitly checks out the PR head, not its merge ref. Replay dispatches cannot qualify consumers. The attempt-specific evidence artifact records the actual checkout, prerequisite SHAs and test totals. A later pending, failed or cancelled run does not invalidate earlier valid evidence. All API pages and retained attempts are considered; missing or expired evidence blocks reuse. core-qualification.json records the accepted run and artifact.

The full dependency ledger is audited across all groups. Every NetworkNT release and snapshot must be source-installed, apart from the explicitly SHA-256-pinned Jackson 3 validator release. Known Jackson 2 yaml-rule and chaos-monkey releases are rejected even with a local-install marker. Jackson 2 dependencies in other groups are reported; ready migrations reject them except shared annotations.

Updating Jackson 3 sibling source pins after merge

The source manifest deliberately pins reviewed commits, including sibling PR sources. It does not follow branch heads. Later CI-only or documentation commits in a sibling do not require repeatedly chasing its head.

Before merging this dependent PR, merge networknt/light-aws-lambda#168. Resolve each actual merge commit through GitHub (gh pr view <number> --repo networknt/<repo> --json mergeCommit); do not substitute the old PR head or a moving branch. For codegen, merge and requalify the Lambda native dependency after AWS first.

Fetch each sibling repository’s master and verify the selected commit is its ancestor with git merge-base --is-ancestor <merge-sha> origin/master. Replace the corresponding values in ci/jackson3-source-refs.json with those full 40-character merge SHAs in a reviewed dependent change. Keep the remaining closure pins and the qualified core/policy SHAs unchanged. Run fresh source-build CI on that dependent commit and review its resolved-source artifact before merging. A squash or merge commit must be qualified even when its runtime tree matches the earlier pin.

For later sibling updates after this PR merges, use the same manifest-update PR and fresh dependent CI procedure. Never silently advance pins or infer qualification from the sibling’s own green build.

Basic Auth credential hardening

See A20/A25 assessment and credential ownership for the scoped Basic Auth hardening and remaining memory/logging limits.

About

A Lambda native gateway with cross-cutting concerns implemented in Java

Resources

Stars

3 stars

Watchers

13 watching

Forks

Releases

Packages

Used by

Contributors

Languages