Audit — August 2026

Table of contents

  1. Part 1 — Audit Findings
    1. 1.1 CI/CD is dead (P0)
    2. 1.2 Configuration defects (P0)
    3. 1.3 Security (P0)
    4. 1.4 Dependency & image currency (P1)
    5. 1.5 README claims the code does not support (P0 — credibility)
    6. 1.6 Documentation & links (P1)
    7. 1.7 Open-source health (P1)
    8. 1.8 Code quality & testing (P2)
  2. What Happened Next

This is the audit that drove the 2026 modernization pass, published as-is. It is here for two reasons: so contributors can see what state the repository was in and why the changes were made, and so the next audit has a baseline to compare against.

Everything below describes the repository as it was on 2026-08-20, before the pass. Findings marked P0 and P1 have since been addressed — see the changelog. The remediation plan itself lives in .claude/plans/2026-modernization.md.

Every version number, HTTP status code and workflow conclusion cited was checked live against the GitHub API and the npm registry on that date, not recalled.


Part 1 — Audit Findings

Severity: P0 = broken or misleading for users today · P1 = currency/credibility · P2 = polish.

1.1 CI/CD is dead (P0)

Every workflow run since 2025-12-06 shows cancelled. Root cause: all three build workflows declare runs-on: Ubuntu-20.04, a runner GitHub retired in 2025.

File Problem
.github/workflows/angular-build-and-push.yml Ubuntu-20.04 (retired) · actions/checkout@v2 · setup-qemu-action@v1 · setup-buildx-action@v1 · login-action@v1 · build-push-action@v2
.github/workflows/express-build-and-push.yml identical set
.github/workflows/nginx-build-and-push.yml identical set
.github/workflows/jekyll-gh-pages.yml triggers on branches: [ main ]default branch is master, so the docs site has never auto-deployed on push; only workflow_dispatch works

Knock-on effects:

  • The three README badges are the first thing on the landing page and they are red.
  • Docker Hub images (nitin27may/mean-angular, mean-expressjs, mean-nginx) last pushed 2025-04-08 — 16 months stale, Angular 19-era. docker-compose.hub.yml pulls :latest, so the “fastest start” path serves software two major Angular versions behind the source tree.
  • All three Hub repos have only a latest tag. No semver, nothing pinnable.
  • Run history shows a Mongo Build workflow that no longer exists in the tree — orphaned.
  • No PR-triggered CI at all. Nothing builds, lints, or tests a pull request. For a repo with 71 forks, an incoming PR gets zero automated feedback.
  • No dependabot.yml, despite a dependencies label already existing in the repo.

1.2 Configuration defects (P0)

These are copy/paste bugs that ship in the documented quick-start path.

Location Defect
docker-compose.yml, docker-compose.nginx.yml, docker-compose.hub.yml EXPRESS_PORT=${MONGO_DB_DATABASE} — the port variable is assigned the database name. Present in all three files.
docker-compose.hub.yml MONGO_DB_PARAMETERS=${MONGO_DB_PORT} — auth params become 27017, so the connection string loses ?authSource=admin and Mongo auth breaks on the Hub path.
api/src/server.ts Reads process.env.PORT. Compose passes EXPRESS_PORT. EXPRESS_PORT is never read by anything.
.env.example Ships BASE_HREF=/contacts/ with a comment explaining it. Nothing consumes it — frontend/Dockerfile dropped the BASE_HREF arg; only the orphaned root dockerfile still has it. Setting it silently does nothing.
docker-compose*.yml Uses legacy links: instead of depends_on: + networks. Deprecated in Compose v2.
all compose files MongoDB 27017 published to the host including in the nginx “production” mode — which directly contradicts docs/index.md’s claim: “Enhanced Security: Internal services remain isolated from direct external access.”
all compose files image: mongo:latest — unpinned, and the README/environment.ts both claim “MongoDB 7.0”. Whatever users get, it is not 7.0.
docker-compose.yml NODE_ENV=dev set on the Angular container, which is an nginx container. Meaningless.
api/docker-compose.yml Orphan file. Obsolete version: '3.8' key, the entire api service commented out, mounts ./init-db and ./mongoneither path exists under api/. Pure confusion for anyone exploring the folder.
root dockerfile (lowercase) Orphan. Referenced by no compose file. Uses node:20-alpine (Node 20 went EOL April 2026), the old dist/contacts/browser SSR-era layout, and contains smart quotes ('builder') in comments.

1.3 Security (P0)

Issue Detail
Credential logging api/src/server.ts:33 does console.log(MONGODB_URI)prints the Mongo password to stdout on every boot. api/src/config/database.ts:7 logs it a second time.
Silent insecure JWT fallback api/src/config/env.tssecret: process.env.SECRET \|\| 'default-secret-for-jwt'. Combined with the documented cp .env.example .env quick-start (which ships SECRET=your-jwt-secret-key), every deployment that follows the README runs on a publicly-known signing key. No fail-fast.
Token in query string api/src/middlewares/auth.middleware.ts accepts req.query.token. JWTs leak into access logs, browser history, and Referer headers.
Wide-open CORS app.use(cors()) with no origin allowlist.
No hardening middleware No helmet, no rate limiting on /api/users/authenticate, no request validation.
Containers run as root No USER directive in api/Dockerfile, frontend/Dockerfile, loadbalancer/Dockerfile, or the root dockerfile. Container-hardening basics that a reference repo should be modelling.
No health checks No HEALTHCHECK in any Dockerfile; no healthcheck:/depends_on: condition: in any compose file. Express starts before Mongo is ready and just logs a connection error rather than retrying.
No resource limits manifest/*.yaml K8s deployments have no resources.requests/limits.

1.4 Dependency & image currency (P1)

Checked against the npm registry on 2026-08-20.

Frontend (frontend/package.json)

Package Current Latest Note
@angular/* 21.0.3 22.1.3 one major behind
@angular/cli 21.0.2 22.1.5  
@ng-bootstrap/ng-bootstrap 20.0.0-rc.0 21.0.0 a release candidate is pinned as a production dep — this is almost certainly why frontend/Dockerfile needs --legacy-peer-deps
ngx-toastr 19.1.0 20.0.5  
@fortawesome/fontawesome-free 6.5.2 7.3.1 major behind
@types/node 22.10.2 26.2.0  
bootstrap 5.3.2 5.3.8  
prettier 3.3.0 3.9.6  
prettier-plugin-organize-imports / -tailwindcss 3.2.4 / 0.6.1 the Tailwind plugin is installed in a Bootstrap project. Dead dependency.
express, @angular/ssr, @angular/platform-server present dead weight — see §1.5

Backend (api/package.json)

Package Current Latest Note
express 4.21.2 5.2.1 major behind — and the frontend already carries Express 5, so the repo ships both majors
mongoose 8.10.0 9.9.3 major behind
dotenv 16.3.1 17.4.2 major behind
@types/express 4.17.25 must move with Express 5
jest not installed "test": "jest"npm test fails immediately
eslint / @typescript-eslint/* not installed "lint": "eslint . --ext .ts"npm run lint fails immediately. api/.eslintrc is also the legacy format; ESLint 9 requires flat config (eslint.config.js)

Do not blindly take typescript@7.0.2 — Angular 22 pins a supported TS range. Track Angular’s constraint, not latest.

Base images

Image Current Should be
api/Dockerfile, frontend/Dockerfile node:22-alpine node:24-alpine (Node 24 is Active LTS; 22 is in maintenance)
root dockerfile node:20-alpine Node 20 is EOL — delete the file
loadbalancer/Dockerfile nginx (unpinned, not alpine) nginx:1.29-alpine
frontend/Dockerfile stage 2 nginx:alpine pin the minor
all compose mongo:latest mongo:8.0

Syntax/tooling drift

  • FROM node:22-alpine as builder — lowercase as triggers a BuildKit StageNameCasing warning. Use AS.
  • RUN npm ci --production in api/Dockerfile — deprecated flag; use npm ci --omit=dev.
  • frontend/Dockerfile uses npm install --legacy-peer-deps instead of npm ci, so the committed package-lock.json is not honoured and builds are not reproducible.
  • api/tsconfig.json: target: "es2016", moduleResolution: "node" — ancient for a Node 22/24 runtime.

1.5 README claims the code does not support (P0 — credibility)

This is the most damaging category. Each of these is something a visitor can disprove in under a minute.

Claim Where Reality
“Angular SSR” GitHub repo description, docs/index.md frontend/angular.json has no server, ssr, or prerender config whatsoever. frontend/Dockerfile builds browser-only and serves via nginx. server.ts, main.server.ts, app.config.server.ts are dead files. "serve:ssr:contacts": "node dist/contacts/server/server.mjs" points at an artifact that is never produced. provideClientHydration() in app.config.ts hydrates nothing.
“Angular 19” docs/index.md “What You’ll Learn” Repo is on 21. The README says 21, the docs site says 19.
“MongoDB 7.0” README Tech Stack, environment.ts Compose pulls mongo:latest (8.x).
“Express.js 4.21” environment.ts True for the API, but the frontend ships Express 5 — two majors in one repo.
“TypeScript throughout the entire stack” README Overview api/tsconfig.json has strict: false and noImplicitAny: false. Frontend is littered with any (allContacts = signal<any[]>([]), contactToDelete: any, data.sort((a: any, b: any) => ...)).
“Enhanced Security: internal services remain isolated” docs/index.md 27017:27017 is published to the host in the nginx production compose file.
“Roadmap 2026: Q1 Testing & Quality, Q2 UI Modernization” README + docs/roadmap.md It is August 2026 with zero commits since December 2025. Q1 and Q2 shipped nothing. A dated roadmap that has visibly lapsed is worse than no roadmap.
  • Broken on the live docs site (https://nitinksingh.com/mean-docker/, which the nitin27may.github.io URL 301s to):
    • architecture.html404, but docs/index.md links to it. docs/architecture.md sets has_children: true + permalink: /docs/architecture, so the file exists but not at the linked URL.
    • ../api/README.html404 (linked from docs/index.md)
    • ../frontend/README.html404 (linked from docs/index.md)
    • ../loadbalancer/README.html404 (linked from docs/index.md)
    • Cause: the Jekyll build uses source: ./docs, so nothing outside docs/ is ever published. These four links can never resolve as written.
  • CONTRIBUTING.md links docs/local-devlopment.html — a .html link inside a GitHub-rendered markdown file. Broken on GitHub. Should be .md.
  • docs/local-devlopment.mdfilename is misspelled (“devlopment”). Renaming breaks inbound links; needs a Jekyll redirect_from.
  • README’s “View Demo” link points to http://localhost — the second link on the landing page and it is meaningless to a visitor. Should point to the docs site or be removed.
  • docs/_config.yml footer: Copyright © 2025.
  • Repo homepage is set to nitin27may.github.io/mean-docker/ but that 301s to nitinksingh.com/mean-docker/. Set the canonical directly.
  • has_wiki: true — an empty wiki tab competing with the docs site.

On README length: at 8.3 KB the README is not too long — the ratio to the docs site is about right. The problems are structural, not volumetric: heavy raw-HTML <table>/<td> blocks that render inconsistently outside github.com and extract poorly for the AI coding assistants that increasingly surface repos like this one, plus a “Getting Started” that omits the docker-compose.hub.yml fast path entirely. Trim the HTML tables, not the content.

1.7 Open-source health (P1)

GitHub community profile score: 71%.

Present Missing
LICENSE (MIT), README.md, CONTRIBUTING.md, .github/ISSUE_TEMPLATE/{bug_report,feature_request}.md, .github/FUNDING.yml, 10 labels, Discussions on SECURITY.md, CODE_OF_CONDUCT.md, .github/PULL_REQUEST_TEMPLATE.md, CODEOWNERS, CHANGELOG.md, .github/dependabot.yml
  • Releases: exactly one (v1.0.0), one tag. For 71 forks and 118 stars there is no versioned artifact anyone can pin or cite. Docker Hub has no version tags either.
  • Labels are the stock GitHub set plus WIP and dependencies. Nothing domain-specific (area: frontend, area: api, area: docker, area: docs) — so good first issue cannot be scoped, which is the single cheapest way to convert a 71-fork audience into contributors.
  • Zero open issues, zero open PRs. Nothing for an interested visitor to pick up.
  • The Angular 21 upgrade was landed by the GitHub Copilot coding agent (branch copilot/update-angular-21-and-packages, PRs #94/#95) — worth knowing, because that is where several of the half-finished states above (SSR files left behind, RC dependency, stale docs) came from.

1.8 Code quality & testing (P2)

  • .spec.ts files exist (app.component, layout.component, contact-list/details/form, register) but nothing runs them — no CI step, and the API has no test runner installed at all.
  • Angular 21 defaults new projects to Vitest; this repo is still on Karma + Jasmine.
  • Still uses provideZoneChangeDetectionzoneless has been stable since Angular v20. For a repo selling “modern practices,” this is the most visible miss.
  • console.log(data) left in contact-list.component.ts:47.
  • frontend/ has no ESLint config at all.
  • angular.json commits a cli.analytics UUID (d893c76f-…) — should be false so forks do not inherit your analytics identity.
  • api/src/config/database.ts exports connectDB() that nothing callsserver.ts does its own inline mongoose.connect. Two sources of truth, one dead.
  • loadbalancer/nginx.conf: duplicate server_name directives (second silently overrides the first), resolver inside location blocks while also using static upstream blocks (contradictory — upstreams resolve once at startup), no X-Forwarded-For/X-Forwarded-Proto, no WebSocket upgrade headers, no gzip, no client_max_body_size. The EXPOSE comment says port 8080 while the directive says 80.
  • Root .gitignore is 3 lines (data, mongo/db, .env) with no node_modules — it works only because api/ and frontend/ carry their own.


What Happened Next

Every P0 and P1 item above was addressed in the modernization pass. The mechanical parts of this audit are captured as a repeatable /audit command in .claude/commands/audit.md, so the next pass starts from a checklist rather than from scratch.

Two things are worth carrying forward:

  1. The most damaging category was not the broken CI — it was the README claiming things the code did not do. A reference repo whose value proposition is “this is how you do it correctly” fails hardest when a visitor can disprove its claims in under a minute.
  2. Dated roadmaps lapse. A quarterly plan on a semi-annually maintained repo is a liability. The roadmap is undated now.