Summary
PR #190 adds a GET /docs endpoint that serves a Swagger UI page. The page currently loads Swagger UI assets from a public CDN:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css">
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
Depending on cdn.jsdelivr.net at runtime is undesirable:
- Availability — the docs page breaks whenever the CDN is unreachable (air-gapped / VPC-only deployments, CDN outage, corporate egress filtering).
- Supply chain — a compromised or hijacked CDN response executes arbitrary JS in the browser of anyone viewing the docs. No SRI hash pins the content.
- Reproducibility — the served asset version can drift under the
@5 major tag without any change to our image.
We should vendor the Swagger UI static assets, bake them into the Docker image, and serve them from the Lambda so /docs is fully self-hosted with zero external requests.
Background
- Docs handler:
src/handlers/handler_api.py → get_docs() (returns the HTML string).
- Routing:
src/event_gate_lambda.py ROUTE_MAP, dispatched by exact API Gateway resource field via src/utils/utils.py::dispatch_request.
- Assets already baked into the image are
COPYed in Dockerfile (see COPY api.yaml $LAMBDA_TASK_ROOT/api.yaml).
- Only two files are strictly required from
swagger-ui-dist@5: swagger-ui.css (~145 KB) and swagger-ui-bundle.js (~1.4 MB, self-contained — includes presets). Both are well under the 6 MB API Gateway Lambda-proxy response limit.
- Swagger UI is Apache-2.0 licensed — compatible with this project.
Proposed work
1. Vendor the assets
Add pinned Swagger UI assets to the repo, e.g. src/static/swagger-ui/:
swagger-ui.css
swagger-ui-bundle.js
Pin the exact version (e.g. swagger-ui-dist@5.x.y, not floating @5). Record the version + upstream source + license in a short README/NOTICE next to the files so future upgrades are traceable.
Alternative: download+checksum-verify during the Docker build instead of committing binaries. Committing is simpler, deterministic, and needs no build-time network — recommended unless the team prefers to keep vendored blobs out of git.
2. Serve the assets from the Lambda
Add handler methods that read the vendored files and return them with correct Content-Type (text/css, application/javascript). Register routes in ROUTE_MAP, e.g.:
/docs/swagger-ui.css
/docs/swagger-ui-bundle.js
(or a single /docs/{proxy+} catch-all dispatching by filename — fewer API Gateway resources to declare). Consider a long-lived Cache-Control header since assets are version-pinned.
3. Point the page at the local assets
Update get_docs() HTML to use relative paths instead of the CDN URLs:
<link rel="stylesheet" href="./docs/swagger-ui.css">
<script src="./docs/swagger-ui-bundle.js"></script>
Relative paths keep working across all API Gateway stages / custom domains (same rationale as the existing ./api reference).
4. Bake assets into the image
Add a COPY src/static $LAMBDA_TASK_ROOT/src/static (or the chosen path) to the Dockerfile, and ensure the non-root app user has read access (the existing chown -R 1000:1000 ... ${LAMBDA_TASK_ROOT} already covers files under the task root).
5. Tests + docs
- Unit: new asset routes return 200, correct
Content-Type, non-empty body; /docs HTML references relative ./docs/... paths and no longer contains cdn.jsdelivr.net.
- Integration: extend
EventGateTestClient / tests/integration/test_api_endpoint.py.
api.yaml: document the asset routes (or the {proxy+}).
Deployment note
Like /docs itself (see PR #190), the API Gateway must route the new asset path(s) to the EventGate Lambda (explicit resources or a {proxy+} catch-all). This requires a matching infra change in cps-eventbus-gateway.
Acceptance criteria
Related: PR #190, #191
Summary
PR #190 adds a
GET /docsendpoint that serves a Swagger UI page. The page currently loads Swagger UI assets from a public CDN:Depending on
cdn.jsdelivr.netat runtime is undesirable:@5major tag without any change to our image.We should vendor the Swagger UI static assets, bake them into the Docker image, and serve them from the Lambda so
/docsis fully self-hosted with zero external requests.Background
src/handlers/handler_api.py→get_docs()(returns the HTML string).src/event_gate_lambda.pyROUTE_MAP, dispatched by exact API Gatewayresourcefield viasrc/utils/utils.py::dispatch_request.COPYed inDockerfile(seeCOPY api.yaml $LAMBDA_TASK_ROOT/api.yaml).swagger-ui-dist@5:swagger-ui.css(~145 KB) andswagger-ui-bundle.js(~1.4 MB, self-contained — includes presets). Both are well under the 6 MB API Gateway Lambda-proxy response limit.Proposed work
1. Vendor the assets
Add pinned Swagger UI assets to the repo, e.g.
src/static/swagger-ui/:swagger-ui.cssswagger-ui-bundle.jsPin the exact version (e.g.
swagger-ui-dist@5.x.y, not floating@5). Record the version + upstream source + license in a shortREADME/NOTICEnext to the files so future upgrades are traceable.2. Serve the assets from the Lambda
Add handler methods that read the vendored files and return them with correct
Content-Type(text/css,application/javascript). Register routes inROUTE_MAP, e.g.:/docs/swagger-ui.css/docs/swagger-ui-bundle.js(or a single
/docs/{proxy+}catch-all dispatching by filename — fewer API Gateway resources to declare). Consider a long-livedCache-Controlheader since assets are version-pinned.3. Point the page at the local assets
Update
get_docs()HTML to use relative paths instead of the CDN URLs:Relative paths keep working across all API Gateway stages / custom domains (same rationale as the existing
./apireference).4. Bake assets into the image
Add a
COPY src/static $LAMBDA_TASK_ROOT/src/static(or the chosen path) to theDockerfile, and ensure the non-rootappuser has read access (the existingchown -R 1000:1000 ... ${LAMBDA_TASK_ROOT}already covers files under the task root).5. Tests + docs
Content-Type, non-empty body;/docsHTML references relative./docs/...paths and no longer containscdn.jsdelivr.net.EventGateTestClient/tests/integration/test_api_endpoint.py.api.yaml: document the asset routes (or the{proxy+}).Deployment note
Like
/docsitself (see PR #190), the API Gateway must route the new asset path(s) to the EventGate Lambda (explicit resources or a{proxy+}catch-all). This requires a matching infra change incps-eventbus-gateway.Acceptance criteria
/docsrenders Swagger UI with no requests tocdn.jsdelivr.netor any external host (verifiable via browser network tab / offline).Related: PR #190, #191