Distroless builds: http-node-distroless - #457
cosmintanasa47 wants to merge 45 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Several added examples contain confirmed runtime-breaking issues and an insecure Vite host configuration that should be addressed before merge.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR adds a suite of distroless Node.js HTTP server examples (plus end-to-end tests) targeting multiple frameworks/stacks (plain Node HTTP, Express, Prisma, SvelteKit, React Router, SolidStart, Next.js, Vite dev/SSR, Puppeteer PDF generation), using Unikraft Kraftfile specs and distroless-focused Dockerfiles to build minimal runtime images.
Changes:
- Introduces multiple new distroless example directories (runtime + build configs, app sources, and docs).
- Adds end-to-end tests for the newly added examples.
- Adds framework-specific build assets/configuration (e.g., Prisma migrations/seed, SvelteKit scaffolding, React Router SSR scaffolding, Vite SSR server).
File summaries
| File | Description |
|---|---|
| httpserver-prisma-expressjs4.19-node18-distroless/webpack.config.js | Webpack bundling + Prisma asset copying for distroless image |
| httpserver-prisma-expressjs4.19-node18-distroless/test_httpserver-prisma-expressjs4.19-node18-distroless.py | E2E test for Prisma/Express distroless example |
| httpserver-prisma-expressjs4.19-node18-distroless/src/index.js | Express + Prisma REST API server implementation |
| httpserver-prisma-expressjs4.19-node18-distroless/prisma/seed.js | Prisma seed script for demo data |
| httpserver-prisma-expressjs4.19-node18-distroless/prisma/schema.prisma | Prisma schema definition for sqlite demo |
| httpserver-prisma-expressjs4.19-node18-distroless/prisma/migrations/migration_lock.toml | Prisma migration lock metadata |
| httpserver-prisma-expressjs4.19-node18-distroless/prisma/migrations/20240208151224_init/migration.sql | Initial sqlite migration SQL |
| httpserver-prisma-expressjs4.19-node18-distroless/package.json | Build dependencies + Prisma seed config |
| httpserver-prisma-expressjs4.19-node18-distroless/Kraftfile | Unikraft target + command definition |
| httpserver-prisma-expressjs4.19-node18-distroless/Dockerfile | Build (node) + runtime (distroless) image |
| httpserver-prisma-expressjs4.19-node18-distroless/.gitignore | Ignore build artifacts and sqlite db |
| httpserver-prisma-expressjs4.19-node18-distroless/.dockerignore | Reduce Docker build context |
| httpserver-node26-distroless/test_httpserver-node26-distroless.py | E2E test for Node 26 distroless example |
| httpserver-node26-distroless/server.js | Basic Node HTTP “Hello, World!” server |
| httpserver-node26-distroless/README.md | Usage/deploy documentation for node26 example |
| httpserver-node26-distroless/Kraftfile | Unikraft spec + node command |
| httpserver-node26-distroless/Dockerfile | Distroless node26 image definition |
| httpserver-node22-sveltekit-distroless/vite.config.ts | Vite config for SvelteKit example |
| httpserver-node22-sveltekit-distroless/tsconfig.json | TypeScript config for SvelteKit |
| httpserver-node22-sveltekit-distroless/test_httpserver-node22-sveltekit-distroless.py | E2E test for SvelteKit distroless example |
| httpserver-node22-sveltekit-distroless/svelte.config.js | SvelteKit adapter configuration |
| httpserver-node22-sveltekit-distroless/static/robots.txt | Static robots file |
| httpserver-node22-sveltekit-distroless/src/routes/sverdle/reduced-motion.ts | Sverdle reduced-motion store utility |
| httpserver-node22-sveltekit-distroless/src/routes/sverdle/how-to-play/+page.ts | How-to-play page server config |
| httpserver-node22-sveltekit-distroless/src/routes/sverdle/how-to-play/+page.svelte | How-to-play Svelte page |
| httpserver-node22-sveltekit-distroless/src/routes/sverdle/game.ts | Game logic for Sverdle demo |
| httpserver-node22-sveltekit-distroless/src/routes/sverdle/+page.server.ts | Server-side actions/load for Sverdle |
| httpserver-node22-sveltekit-distroless/src/routes/styles.css | Shared styles for SvelteKit app |
| httpserver-node22-sveltekit-distroless/src/routes/Header.svelte | App header component |
| httpserver-node22-sveltekit-distroless/src/routes/Counter.svelte | Demo counter component |
| httpserver-node22-sveltekit-distroless/src/routes/about/+page.ts | About page server config |
| httpserver-node22-sveltekit-distroless/src/routes/about/+page.svelte | About page UI |
| httpserver-node22-sveltekit-distroless/src/routes/+page.ts | Home page server config |
| httpserver-node22-sveltekit-distroless/src/routes/+page.svelte | Home page UI |
| httpserver-node22-sveltekit-distroless/src/routes/+layout.svelte | Layout wrapper |
| httpserver-node22-sveltekit-distroless/src/lib/images/svelte-logo.svg | Svelte logo asset |
| httpserver-node22-sveltekit-distroless/src/lib/images/github.svg | GitHub icon asset |
| httpserver-node22-sveltekit-distroless/src/app.html | SvelteKit app HTML template |
| httpserver-node22-sveltekit-distroless/src/app.d.ts | SvelteKit app types |
| httpserver-node22-sveltekit-distroless/README.sveltekit.md | Upstream scaffold README |
| httpserver-node22-sveltekit-distroless/package.json | SvelteKit dependencies and scripts |
| httpserver-node22-sveltekit-distroless/Kraftfile | Unikraft spec + command for built app |
| httpserver-node22-sveltekit-distroless/Dockerfile | Build + distroless runtime image |
| httpserver-node22-sveltekit-distroless/.npmrc | npm engine strictness |
| httpserver-node22-sveltekit-distroless/.gitignore | Ignore build and tooling artifacts |
| httpserver-node22-sveltekit-distroless/.eslintrc.cjs | ESLint configuration |
| httpserver-node22-sveltekit-distroless/.eslintignore | ESLint ignore patterns |
| httpserver-node22-sveltekit-distroless/.dockerignore | Docker build context trimming |
| httpserver-node22-react-router-distroless/vite.config.ts | Vite config for React Router |
| httpserver-node22-react-router-distroless/tsconfig.json | TypeScript config for React Router |
| httpserver-node22-react-router-distroless/test_httpserver-node22-react-router-distroless.py | E2E test for React Router distroless example |
| httpserver-node22-react-router-distroless/server.js | Express server hosting React Router SSR |
| httpserver-node22-react-router-distroless/README.md | Usage/deploy documentation |
| httpserver-node22-react-router-distroless/react-router.config.ts | React Router SSR config |
| httpserver-node22-react-router-distroless/package.json | React Router dependencies and scripts |
| httpserver-node22-react-router-distroless/Kraftfile | Unikraft spec + runtime command |
| httpserver-node22-react-router-distroless/Dockerfile | Build + distroless runtime image |
| httpserver-node22-react-router-distroless/app/routes/_index.tsx | Example route |
| httpserver-node22-react-router-distroless/app/routes.ts | Flat-routes configuration |
| httpserver-node22-react-router-distroless/app/root.tsx | App document/root component |
| httpserver-node22-react-router-distroless/app/entry.server.tsx | Server entry for SSR streaming |
| httpserver-node22-react-router-distroless/app/entry.client.tsx | Client hydration entry |
| httpserver-node22-react-router-distroless/.gitignore | Ignore build artifacts |
| httpserver-node22-react-router-distroless/.eslintrc.cjs | ESLint rules/config |
| httpserver-node22-react-router-distroless/.dockerignore | Docker build context trimming |
| httpserver-node21-solid-start-distroless/tsconfig.json | TS config for SolidStart |
| httpserver-node21-solid-start-distroless/test_httpserver-node21-solid-start-distroless.py | E2E test for SolidStart distroless example |
| httpserver-node21-solid-start-distroless/src/routes/index.tsx | SolidStart index route |
| httpserver-node21-solid-start-distroless/src/routes/about.tsx | SolidStart about route |
| httpserver-node21-solid-start-distroless/src/routes/[...404].tsx | SolidStart 404 route |
| httpserver-node21-solid-start-distroless/src/global.d.ts | SolidStart env types |
| httpserver-node21-solid-start-distroless/src/entry-server.tsx | SolidStart server entry |
| httpserver-node21-solid-start-distroless/src/entry-client.tsx | SolidStart client entry |
| httpserver-node21-solid-start-distroless/src/components/Counter.tsx | Demo component |
| httpserver-node21-solid-start-distroless/src/components/Counter.css | Demo component styles |
| httpserver-node21-solid-start-distroless/src/app.tsx | SolidStart app root |
| httpserver-node21-solid-start-distroless/src/app.css | App styles |
| httpserver-node21-solid-start-distroless/README.solidstart.md | Upstream scaffold README |
| httpserver-node21-solid-start-distroless/README.md | Usage/deploy documentation |
| httpserver-node21-solid-start-distroless/package.json | SolidStart dependencies/scripts |
| httpserver-node21-solid-start-distroless/Kraftfile | Unikraft spec + runtime command |
| httpserver-node21-solid-start-distroless/Dockerfile | Build + distroless runtime image |
| httpserver-node21-solid-start-distroless/app.config.ts | SolidStart config |
| httpserver-node21-solid-start-distroless/.gitignore | Ignore build/tooling artifacts |
| httpserver-node21-solid-start-distroless/.dockerignore | Docker build context trimming |
| httpserver-node21-nextjs-distroless/tsconfig.json | Next.js TypeScript config |
| httpserver-node21-nextjs-distroless/test_httpserver-node21-nextjs-distroless.py | E2E test for Next.js distroless example |
| httpserver-node21-nextjs-distroless/tailwind.config.ts | Tailwind config |
| httpserver-node21-nextjs-distroless/public/vercel.svg | Static asset |
| httpserver-node21-nextjs-distroless/public/next.svg | Static asset |
| httpserver-node21-nextjs-distroless/postcss.config.js | PostCSS config |
| httpserver-node21-nextjs-distroless/package.json | Next.js dependencies/scripts |
| httpserver-node21-nextjs-distroless/next.config.mjs | Next config (standalone output) |
| httpserver-node21-nextjs-distroless/Kraftfile | Unikraft spec + runtime command |
| httpserver-node21-nextjs-distroless/Dockerfile | Build + distroless runtime image |
| httpserver-node21-nextjs-distroless/app/page.tsx | Next.js app router page |
| httpserver-node21-nextjs-distroless/app/layout.tsx | Next.js root layout |
| httpserver-node21-nextjs-distroless/app/globals.css | Global styles |
| httpserver-node21-nextjs-distroless/.gitignore | Ignore build/tooling artifacts |
| httpserver-node21-nextjs-distroless/.dockerignore | Docker build context trimming |
| httpserver-node-vite-vanilla-distroless/vite.config.js | Vite server config for distroless Vite example |
| httpserver-node-vite-vanilla-distroless/test_httpserver-node-vite-vanilla-distroless.py | E2E test for Vite (vanilla) distroless example |
| httpserver-node-vite-vanilla-distroless/src/style.css | Vite template styling |
| httpserver-node-vite-vanilla-distroless/src/main.js | Vite template entry |
| httpserver-node-vite-vanilla-distroless/src/javascript.svg | Asset |
| httpserver-node-vite-vanilla-distroless/src/counter.js | Template logic |
| httpserver-node-vite-vanilla-distroless/README.md | Usage/deploy documentation |
| httpserver-node-vite-vanilla-distroless/public/vite.svg | Asset |
| httpserver-node-vite-vanilla-distroless/package.json | Vite scripts/dependencies |
| httpserver-node-vite-vanilla-distroless/Kraftfile | Unikraft spec for running Vite server |
| httpserver-node-vite-vanilla-distroless/index.html | Vite template HTML |
| httpserver-node-vite-vanilla-distroless/Dockerfile | Build + distroless runtime image |
| httpserver-node-vite-vanilla-distroless/.gitignore | Ignore build/tooling artifacts |
| httpserver-node-vite-vanilla-distroless/.dockerignore | Docker build context trimming |
| httpserver-node-vite-ssr-vanilla-distroless/test_httpserver-node-vite-ssr-vanilla-distroless.py | E2E test for Vite SSR distroless example |
| httpserver-node-vite-ssr-vanilla-distroless/src/style.css | SSR template styling |
| httpserver-node-vite-ssr-vanilla-distroless/src/javascript.svg | Asset |
| httpserver-node-vite-ssr-vanilla-distroless/src/entry-server.js | SSR render entry |
| httpserver-node-vite-ssr-vanilla-distroless/src/entry-client.js | Client hydration entry |
| httpserver-node-vite-ssr-vanilla-distroless/src/counter.js | Template logic |
| httpserver-node-vite-ssr-vanilla-distroless/server.js | Express SSR server for Vite SSR |
| httpserver-node-vite-ssr-vanilla-distroless/README.md | Usage/deploy documentation |
| httpserver-node-vite-ssr-vanilla-distroless/public/vite.svg | Asset |
| httpserver-node-vite-ssr-vanilla-distroless/package.json | SSR dependencies/scripts |
| httpserver-node-vite-ssr-vanilla-distroless/Kraftfile | Unikraft spec + runtime command |
| httpserver-node-vite-ssr-vanilla-distroless/index.html | SSR template HTML |
| httpserver-node-vite-ssr-vanilla-distroless/Dockerfile | Build + distroless runtime image |
| httpserver-node-vite-ssr-vanilla-distroless/.gitignore | Ignore artifacts |
| httpserver-node-vite-ssr-vanilla-distroless/.dockerignore | Docker build context trimming |
| httpserver-node-express-puppeteer-distroless/wrapper.sh | Wrapper script for runtime environment setup |
| httpserver-node-express-puppeteer-distroless/test_httpserver-node-express-puppeteer-distroless.py | E2E test for Puppeteer distroless example |
| httpserver-node-express-puppeteer-distroless/routes/index.js | Express routes for landing + PDF generation |
| httpserver-node-express-puppeteer-distroless/README.md | Usage/deploy documentation |
| httpserver-node-express-puppeteer-distroless/public/index.html | Landing page UI |
| httpserver-node-express-puppeteer-distroless/public/helpful.html | Extra static page |
| httpserver-node-express-puppeteer-distroless/package.json | Puppeteer app dependencies |
| httpserver-node-express-puppeteer-distroless/LICENSE | Third-party license file |
| httpserver-node-express-puppeteer-distroless/Kraftfile | Unikraft spec + runtime command |
| httpserver-node-express-puppeteer-distroless/example.env | Example env vars |
| httpserver-node-express-puppeteer-distroless/Dockerfile | Build + distroless runtime with browser deps |
| httpserver-node-express-puppeteer-distroless/controller/generatePdf.js | Puppeteer PDF generation logic |
| httpserver-node-express-puppeteer-distroless/bin/www | Express server bootstrap |
| httpserver-node-express-puppeteer-distroless/app.js | Express app setup + middleware |
| httpserver-node-express-puppeteer-distroless/.gitignore | Ignore artifacts |
| httpserver-node-express-puppeteer-distroless/.dockerignore | Docker build context trimming |
| httpserver-nginx-vite-vanilla-distroless/test_httpserver-nginx-vite-vanilla-distroless.py | E2E test for nginx+Vite distroless example |
| httpserver-nginx-vite-vanilla-distroless/src/style.css | Vite template styling |
| httpserver-nginx-vite-vanilla-distroless/src/main.js | Vite template entry |
| httpserver-nginx-vite-vanilla-distroless/src/javascript.svg | Asset |
| httpserver-nginx-vite-vanilla-distroless/src/counter.js | Template logic |
| httpserver-nginx-vite-vanilla-distroless/rootfs/wwwroot/index.html | nginx rootfs default index (packaged content) |
| httpserver-nginx-vite-vanilla-distroless/rootfs/etc/nginx/nginx.conf | nginx runtime configuration |
| httpserver-nginx-vite-vanilla-distroless/public/vite.svg | Asset |
| httpserver-nginx-vite-vanilla-distroless/package.json | Vite build scripts |
| httpserver-nginx-vite-vanilla-distroless/Kraftfile | Unikraft spec + nginx command |
| httpserver-nginx-vite-vanilla-distroless/index.html | Vite template HTML |
| httpserver-nginx-vite-vanilla-distroless/Dockerfile | Build Vite assets + nginx distroless runtime |
| httpserver-nginx-vite-vanilla-distroless/.gitignore | Ignore artifacts |
| httpserver-expressjs4.18-node21-distroless/test_httpserver-expressjs4.18-node21-distroless.py | E2E test for Express distroless example |
| httpserver-expressjs4.18-node21-distroless/README.md | Usage/deploy documentation |
| httpserver-expressjs4.18-node21-distroless/package.json | Express dependency |
| httpserver-expressjs4.18-node21-distroless/Kraftfile | Unikraft spec + node command |
| httpserver-expressjs4.18-node21-distroless/Dockerfile | Build + distroless runtime image |
| httpserver-expressjs4.18-node21-distroless/app/index.js | Express “Hello, World!” implementation |
| httpserver-expressjs4.18-node21-distroless/.gitignore | Ignore artifacts |
| httpserver-expressjs4.18-node21-distroless/.dockerignore | Docker build context trimming |
Review details
Files not reviewed (4)
- httpserver-nginx-vite-vanilla-distroless/package-lock.json: Generated file
- httpserver-node-express-puppeteer-distroless/package-lock.json: Generated file
- httpserver-node-vite-ssr-vanilla-distroless/package-lock.json: Generated file
- httpserver-node-vite-vanilla-distroless/package-lock.json: Generated file
- Files reviewed: 158/188 changed files
- Comments generated: 8
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
74d50e3 to
2b0092e
Compare
|
Applied the last two of Copilot's recommandations: modified the name in package.json and fixed the node26 http server example's README.md. The rest of the suggestions were about modifying the logic of the servers and I prefer not to break anything considering that the examples works when tested manually. |
There was a problem hiding this comment.
🟡 Changes recommended
One or more issues must be addressed before approval.
Get a fresh assessment by requesting another Copilot review.
Review details
Files not reviewed (4)
- httpserver-nginx-vite-vanilla-distroless/package-lock.json: Generated file
- httpserver-node-express-puppeteer-distroless/package-lock.json: Generated file
- httpserver-node-vite-ssr-vanilla-distroless/package-lock.json: Generated file
- httpserver-node-vite-vanilla-distroless/package-lock.json: Generated file
Suppressed comments (6)
httpserver-node-express-puppeteer-distroless/controller/generatePdf.js:13
- If
newPage,goto, orpdfrejects, execution skipsbrowser.close()and the GET handler only returns 500, leaving a Chromium process behind. Repeated failed requests can exhaust the instance's memory/process limits; close the browser in afinallyblock around each render.
httpserver-node-express-puppeteer-distroless/routes/index.js:7 - Express requires
res.sendFileto receive an absolute path unless arootoption is supplied. This relative path throws on every/request, so the landing-page end-to-end test will receive a 500 response. Supply the public directory as therootor use an absolute path.
httpserver-node-express-puppeteer-distroless/routes/index.js:23 - This is an async Express 4 handler with no
try/catchornexterror forwarding. Invalid base64 and browser failures reject the handler promise, so an unhandled request can terminate the process instead of returning an HTTP error. Handle the rejection and return an appropriate 4xx/5xx response.
httpserver-node-vite-vanilla-distroless/vite.config.js:13 - This exposes Vite's development server publicly with
allowedHosts: true, disabling the DNS-rebinding protection and allowing arbitrary Host headers to reach the dev server and its source/module endpoints. Restrict the allowed host(s) or serve a production build instead of disabling the check.
httpserver-node26-distroless/README.md:178 - This documented command uses
/usr/bin/node, but the actual distroless image provides Node at/nodejs/bin/nodeand the Kraftfile uses that path. Following this customization example would fail to start the instance.
httpserver-prisma-expressjs4.19-node18-distroless/src/index.js:74 - When a post is currently published,
!postData.publishedisfalse, so|| undefinedturns the update value intoundefinedand Prisma leaves the field unchanged. The/publish/:idendpoint therefore cannot unpublish a published post; pass the boolean directly.
- Files reviewed: 158/188 changed files
- Comments generated: 12
- Review effort level: Lite
| // See GHSA-vg6x-rcgg-rjx6 for more details[0]. | ||
| // | ||
| // [0]: https://vite.dev/config/server-options.html#server-allowedhosts | ||
| allowedHosts: true |
2b0092e to
5fd2c00
Compare
|
Fixed the last issue found by Copilot. The rest of them are not issues. |
Modify Kraftfile to point to the right path to node. Source file is unmodified.
Slightly modify to fit new distroless version.
Slightly modify to fit new distroless version.
Has only one stage because the source file is copied and that can be made from host. It uses the nodejs26-debian13 distroless image from Google. Nonroot for smaller atack surface.
Slighly modify to fit new distroless version.
Slightly modify to fit new distroless version.
Copy previous Kraftfile. Modify node run command to fit new node22-debian13 image's node executable. Previous: /usr/bin/node Current: /nodejs/bin/node
…rfile base stage - install dependencies build stage - build executable third stage - runs executable Third stage uses gcr.io/distroless/nodejs22-debian13 - distroless image for node. nonroot for better security. It only needs additional svelte dependencies copied from build stage.
Unmodified files from non-distroless (scratch) version.
Unmodified .files from non-distroless (scratch) version.
… file Slightly modify to fit distroless version.
Slightly modify to fit distroless version.
Take non-distroless Kraftfile and modify node's location on node22-debian13 image's file system. Previous: /usr/bin/node Current: /nodejs/bin/node
… Dockerfile base stage: Alpine Node.js base image build: Install deps, copy source, build React app runtime: Install only production deps distroless: Copy build artifacts, node_modules, and server.js to gcr.io/distroless/nodejs22-debian13:nonroot
…files Unmodified files from non-distroless (scratch) version.
…Dockerfile First stage builds and the second runs the server. Node.js 21 lacks official distroless images (only v20/v22). Use cc-debian13 with Debian builder for glibc compatibility. Remove musl libs, add libssl/libcrypto, run as nonroot.
…file Slightly modify to fit new distroless version.
Slightly modify to fit new distroless version.
…iles Unmodified files from non-distroless (scratch) version.
Slightly modify to fit new distroless version.
Slightly modify to fit new distroless version.
…rfile Node.js 21 lacks official distroless images (only v20/v22). Use cc-debian13 with Debian builder for glibc compatibility. Remove musl libs, add libssl/libcrypto, run as nonroot.
Unmodified files from non-distroless (scratch) version.
…file Slightly modified to fit new distroless version.
Slightly modified to fit new distroless version.
…age Dockerfile Didn't copy some C/C++ libraries because ar contained in the cc-distroless image. A cc-distroless version is the only option for a distroless implementation. A node-distroless image for newer versions of node is hard to find. The only one would've been nvitaterna/nodejs-distroless:26 but it has compatibility issues with puppeteer.
…ed files Unmodified files from the non-distroless version.
…ti-stage Dockerfile First stage builds dependencies, the second stage runs the server. It uses bookworm (debian12) node18 image for build stage for maximum compatibility with the node18-debian12 distroless runtime image.
Took the non-distroless version's Kraftfile and modified the node's location for cmd command to fit distroless image's node location.
…test file Slightly modify to fit new distroless version.
…fig.js The previous version had a certain path for openssl's location (alpine type) and the node-debian distroless image has openssl at a different location in the file-system so I modified the hardcoded location for the new version to work.
…odified files Unmodified files from the non-distroless version.
…e Dockerfile Node's odd versions do not have distroless images available so I have built from the cc-debian12 image. It has a similar form as the non-distroless dockerfile for the non-distroless version.
Slightly modify to fit new distroless version.
…t file Sligtly modify to fit new distroless version.
… files Unmodified files from non-distroless version of http server.
…ockerfile Used debian and now alpine image for build stage for compatibility with the runtime stage distroless image. Used cc-debian distroless image and not node because I didn't find one for node23.
… test file Slightly modify to fit new distroless version.
Unmodified files from non-distroless version.
…ge Dockerfile Distroless images for node's odd versions are nowhere to be found so I used a cc-debian distroless image and added node and other dependencies.
…d py test file Slightly modify to fit new distroless version.
…d files Unmodified files from non-distroless version of server.
…Dockerfile Used an nginx distroless image instead of a node one because it incorporates most of the libraries copied in the non-distroless version's dockerfile.
…y test file Slightly modified to fit new distroless version.
…iles Umodified files from non-distroless version.
5fd2c00 to
5ce2504
Compare
|
Now each file is added/modified in only one commit, we do not have an add followed by a modification for any file here. |
Those are the distroless node http servers. Details in the commit messages.