Audit — August 2026
Table of contents
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.ymlpulls:latest, so the “fastest start” path serves software two major Angular versions behind the source tree. - All three Hub repos have only a
latesttag. No semver, nothing pinnable. - Run history shows a
Mongo Buildworkflow 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 adependencieslabel 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 ./mongo — neither 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.ts → secret: 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— lowercaseastriggers a BuildKitStageNameCasingwarning. UseAS.RUN npm ci --productioninapi/Dockerfile— deprecated flag; usenpm ci --omit=dev.frontend/Dockerfileusesnpm install --legacy-peer-depsinstead ofnpm ci, so the committedpackage-lock.jsonis 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. |
1.6 Documentation & links (P1)
- Broken on the live docs site (
https://nitinksingh.com/mean-docker/, which thenitin27may.github.ioURL 301s to):architecture.html→ 404, butdocs/index.mdlinks to it.docs/architecture.mdsetshas_children: true+permalink: /docs/architecture, so the file exists but not at the linked URL.../api/README.html→ 404 (linked fromdocs/index.md)../frontend/README.html→ 404 (linked fromdocs/index.md)../loadbalancer/README.html→ 404 (linked fromdocs/index.md)- Cause: the Jekyll build uses
source: ./docs, so nothing outsidedocs/is ever published. These four links can never resolve as written.
CONTRIBUTING.mdlinksdocs/local-devlopment.html— a.htmllink inside a GitHub-rendered markdown file. Broken on GitHub. Should be.md.docs/local-devlopment.md— filename is misspelled (“devlopment”). Renaming breaks inbound links; needs a Jekyllredirect_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.ymlfooter:Copyright © 2025.- Repo
homepageis set tonitin27may.github.io/mean-docker/but that 301s tonitinksingh.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
WIPanddependencies. Nothing domain-specific (area: frontend,area: api,area: docker,area: docs) — sogood first issuecannot 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.tsfiles 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
provideZoneChangeDetection— zoneless has been stable since Angular v20. For a repo selling “modern practices,” this is the most visible miss. console.log(data)left incontact-list.component.ts:47.frontend/has no ESLint config at all.angular.jsoncommits acli.analyticsUUID (d893c76f-…) — should befalseso forks do not inherit your analytics identity.api/src/config/database.tsexportsconnectDB()that nothing calls —server.tsdoes its own inlinemongoose.connect. Two sources of truth, one dead.loadbalancer/nginx.conf: duplicateserver_namedirectives (second silently overrides the first),resolverinsidelocationblocks while also using staticupstreamblocks (contradictory — upstreams resolve once at startup), noX-Forwarded-For/X-Forwarded-Proto, no WebSocket upgrade headers, no gzip, noclient_max_body_size. TheEXPOSEcomment says port 8080 while the directive says 80.- Root
.gitignoreis 3 lines (data,mongo/db,.env) with nonode_modules— it works only becauseapi/andfrontend/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:
- 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.
- Dated roadmaps lapse. A quarterly plan on a semi-annually maintained repo is a liability. The roadmap is undated now.