← Heritage Community Bank
Fable 5 External Assessment
Docker Client Delivery Package & MCP Knowledge Server
Reviewer: Fable 5 Date: 2026-07-10 Heritage Community Bank — Jones & Associates
Docker Client Package
Showstopper
3Structural
12Spec Bugs
10Missing
4Sound
--network=none + -p 8080:8080 is non-functional. CPU-only inference unvalidated. Dev paths leak in shipped config.
MCP Knowledge Server
P0 Open
2P0
3P1
8Missing
3Sound
Auth not implemented — API key loaded but never checked. Port 8443 open to internet. Knowledge served unauthenticated. Verified live.
Bottom line: The two-layer architecture (local assessment / cloud knowledge) is correct and is the right product story for banks. But the spec contains one showstopper contradiction, one unvalidated assumption that could invalidate the entire Docker-preferred decision, and shipped defaults that leak the internal dev environment.
Structural Risks
Showstopper--network=none + port publish is non-functional

The client workflow runs docker run --network=none ... -p 8080:8080 then says "open browser → localhost:8080." Port publishing does not function with --network=none. The entire UI layer (Components 5B, 8, 10) is dead as specified.

Recommended fix: Two-container compose design. engine service on an internal network writing state to a shared volume; a thin ui service published on 127.0.0.1:8080.

BlockingCPU-only inference inside containers — unvalidated

On macOS, Docker runs in a VM with no Metal passthrough. A 9B–14B model on CPU may run 5–10× slower. If 500 controls takes 40+ hours, the architecture pivots.

Action: Run the CPU-only benchmark this week.

RiskDocker Desktop licensing — target segment exceeds free tier

Docker Desktop requires a paid subscription for organizations over 250 employees or $10M revenue. Every bank in the target segment exceeds the revenue threshold.

Key Spec Bugs
Bugdefaults.yaml ships internal dev environment paths

db_path: ~/AI/memory/databases/canonical/context.db, personal output paths. Shipped defaults must be container paths. Both a functional bug and an information-leak issue.

Bug--memory=32g limit vs. 16GB minimum host RAM

Container limit exceeds host minimum. A client passing the readiness gate can OOM overnight.

7 moreComponent 4 stale, runtime numbers inconsistent, 5 others
  • Component 4 checks host Python/Ollama — irrelevant in Docker mode
  • Runtime estimates range from 28s to 2min per control
  • "Write-only mount" — Docker has no write-only mount
  • MCP endpoint in offline config
  • SQLCipher NFR unimplementable without key management
  • Version numbering inconsistency (v0.1 vs v0.5)
  • Dashboard reads redacted logs but needs control names
What's Sound
SoundTwo-layer split, dual-mode design, Lambda exclusion list, chatbot boundary
  • Two-layer split — "AWS serves knowledge; client data stays local" is exactly right
  • Dual-mode design — engine runs native or containerized without rewrite — single best engineering decision
  • Lambda exclusion list — "no client data processing" is what a CISO wants in writing
  • Chatbot findings boundary — non-negotiable, correct instinct
Strategic Recommendations
  1. 1Run the CPU-only container benchmark before anything else.
  2. 2Have the appliance conversation now. If CPU-only fails, pivot to auditor-provided hardware (EVO-X2).
  3. 3Resolve the network architecture before writing any Trust Dashboard copy.
  4. 4Split engine from models. Fixes image size, updates, licensing, and versioning simultaneously.
  5. 5Descope v1.0 to Phase 0 + Phase 1. The Morning Report in a client's hands beats a chatbot in a spec.
Bottom line: The architecture is sound and the tool design is genuinely good. But authentication does not exist — the monetizable product is currently served to anyone on the internet, verified live.
P0 — Critical
P0Authentication does not exist — verified live

API_KEY = os.environ.get("CRI_MCP_API_KEY", "")never referenced again. No auth middleware. Unauthenticated request returned HTTP 200 with full server handshake.

Fix: Wire into FastMCP 3.x auth providers. One day of work.

P0Port 8443 directly reachable from internet

Docker binds 0.0.0.0:8443. Direct HTTP returned 200 unauthenticated, bypassing nginx/TLS entirely.

Fix: Bind to 127.0.0.1:8443 or close in security group.

P1 — This Month
P1No rate limiting, single shared API key, Splunk co-tenancy
  • No rate limiting — add limit_req at nginx
  • Single shared key = no per-client revocation or metering
  • MCP + SIEM on same EC2 — compromise of one exposes both
Code Quality
8 issuesPer-call JSON parsing, no error handling, log rotation broken, 5 more
  • 519KB file re-parsed on every call — load once at startup
  • No try/except around json.loads — malformed file = opaque 500
  • validate_selection KeyError on malformed baseline
  • Audit log doesn't rotate (filename fixed at process start)
  • No request/client identity in audit entries
  • get_prompt_template mutates loaded dict in place
  • Stray data/server.py duplicate
  • requirements.txt major-version-unpinned
API Design
Sound9-tool surface is broadly good — 4 refinements needed
  • Rename list_availablelist_decompositions
  • Resolve get_decomposition / get_control_characteristics overlap
  • Add get_manifest() discovery tool
  • Note validate_selection sends client metadata server-side

Right tool count: 9→11, not a rewrite.

Strategic Recommendations
  1. 1Turn on authentication and close port 8443 (this week).
  2. 2Load-once-in-memory + schema validation on load.
  3. 3Add manifest + get_manifest() tool.
  4. 4Then productize: per-client keys, Postgres, signed bundles, CI/CD.
Combined priority: The MCP server has an active security vulnerability (no auth, open port) that should be fixed immediately. The Docker spec has a design contradiction that blocks implementation. Both need attention this week.
This Week (P0)
  1. 1MCP: Implement authentication. Wire API key into bearer-token check. Close port 8443.
  2. 2MCP: Load-once-in-memory + try/except.
  3. 3Docker: Run CPU-only benchmark. Decides whether "Docker preferred" survives.
  4. 4Docker: Fix defaults.yaml. Remove dev paths. Set container-native defaults.
This Month (P1)
  1. 5Docker: Resolve network architecture. Two-container compose or file-based-only.
  2. 6Docker: Split engine from models.
  3. 7MCP: Add manifest + versioning.
  4. 8MCP: Rate limiting + per-client keys.
  5. 9Docker: Descope v1.0 to Phase 0 + Phase 1.
Architecture Decisions Needed
  1. AAppliance vs. client hardware. EVO-X2 pivot if CPU-only fails.
  2. BModel redistribution licensing. Legal review before any image ships.
  3. CYou are the third party. Prepare a vendor security package for client TPRM.
De-Prioritize
  1. x402 paywall, multi-framework expansion, Component 10 Chatbot, planned MCP tools