tool-compass 2.5.4__tar.gz → 2.5.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {tool_compass-2.5.4 → tool_compass-2.5.6}/.env.example +31 -6
  2. {tool_compass-2.5.4 → tool_compass-2.5.6}/.github/workflows/ci.yml +115 -15
  3. {tool_compass-2.5.4 → tool_compass-2.5.6}/.github/workflows/publish.yml +6 -6
  4. {tool_compass-2.5.4 → tool_compass-2.5.6}/.github/workflows/release-binaries.yml +2 -2
  5. tool_compass-2.5.6/.polyglot-pins.json +29 -0
  6. {tool_compass-2.5.4 → tool_compass-2.5.6}/CHANGELOG.md +26 -1
  7. {tool_compass-2.5.4 → tool_compass-2.5.6}/Dockerfile +21 -8
  8. {tool_compass-2.5.4 → tool_compass-2.5.6}/PKG-INFO +34 -7
  9. {tool_compass-2.5.4 → tool_compass-2.5.6}/README.md +33 -6
  10. {tool_compass-2.5.4 → tool_compass-2.5.6}/SECURITY.md +6 -7
  11. {tool_compass-2.5.4 → tool_compass-2.5.6}/atlas/README.md +16 -9
  12. {tool_compass-2.5.4 → tool_compass-2.5.6}/atlas/page.json +107 -23
  13. {tool_compass-2.5.4 → tool_compass-2.5.6}/atlas/statistics.json +237 -242
  14. {tool_compass-2.5.4 → tool_compass-2.5.6}/atlas/structure.json +327 -256
  15. {tool_compass-2.5.4 → tool_compass-2.5.6}/cli.py +26 -10
  16. {tool_compass-2.5.4 → tool_compass-2.5.6}/config.py +27 -7
  17. {tool_compass-2.5.4 → tool_compass-2.5.6}/docker-compose.yml +11 -0
  18. {tool_compass-2.5.4 → tool_compass-2.5.6}/docker-entrypoint.sh +66 -3
  19. {tool_compass-2.5.4 → tool_compass-2.5.6}/fly.toml +4 -3
  20. {tool_compass-2.5.4 → tool_compass-2.5.6}/gateway.py +63 -25
  21. {tool_compass-2.5.4 → tool_compass-2.5.6}/pyproject.toml +2 -2
  22. {tool_compass-2.5.4 → tool_compass-2.5.6}/requirements-dev.txt +3 -3
  23. {tool_compass-2.5.4 → tool_compass-2.5.6}/ui.py +89 -29
  24. {tool_compass-2.5.4 → tool_compass-2.5.6}/.dockerignore +0 -0
  25. {tool_compass-2.5.4 → tool_compass-2.5.6}/.gitattributes +0 -0
  26. {tool_compass-2.5.4 → tool_compass-2.5.6}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  27. {tool_compass-2.5.4 → tool_compass-2.5.6}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  28. {tool_compass-2.5.4 → tool_compass-2.5.6}/.github/dependabot.yml +0 -0
  29. {tool_compass-2.5.4 → tool_compass-2.5.6}/.github/workflows/release.yml +0 -0
  30. {tool_compass-2.5.4 → tool_compass-2.5.6}/.gitignore +0 -0
  31. {tool_compass-2.5.4 → tool_compass-2.5.6}/.pre-commit-config.yaml +0 -0
  32. {tool_compass-2.5.4 → tool_compass-2.5.6}/CODE_OF_CONDUCT.md +0 -0
  33. {tool_compass-2.5.4 → tool_compass-2.5.6}/CONTRIBUTING.md +0 -0
  34. {tool_compass-2.5.4 → tool_compass-2.5.6}/LICENSE +0 -0
  35. {tool_compass-2.5.4 → tool_compass-2.5.6}/Makefile +0 -0
  36. {tool_compass-2.5.4 → tool_compass-2.5.6}/SCORECARD.md +0 -0
  37. {tool_compass-2.5.4 → tool_compass-2.5.6}/SHIP_GATE.md +0 -0
  38. {tool_compass-2.5.4 → tool_compass-2.5.6}/_version.py +0 -0
  39. {tool_compass-2.5.4 → tool_compass-2.5.6}/analytics.py +0 -0
  40. {tool_compass-2.5.4 → tool_compass-2.5.6}/atlas/boundaries.yaml +0 -0
  41. {tool_compass-2.5.4 → tool_compass-2.5.6}/backend_client_mcp.py +0 -0
  42. {tool_compass-2.5.4 → tool_compass-2.5.6}/backend_client_simple.py +0 -0
  43. {tool_compass-2.5.4 → tool_compass-2.5.6}/bootstrap.py +0 -0
  44. {tool_compass-2.5.4 → tool_compass-2.5.6}/chain_indexer.py +0 -0
  45. {tool_compass-2.5.4 → tool_compass-2.5.6}/codecov.yml +0 -0
  46. {tool_compass-2.5.4 → tool_compass-2.5.6}/compass_config.example.json +0 -0
  47. {tool_compass-2.5.4 → tool_compass-2.5.6}/docs/index.md +0 -0
  48. {tool_compass-2.5.4 → tool_compass-2.5.6}/embedder.py +0 -0
  49. {tool_compass-2.5.4 → tool_compass-2.5.6}/indexer.py +0 -0
  50. {tool_compass-2.5.4 → tool_compass-2.5.6}/llms.txt +0 -0
  51. {tool_compass-2.5.4 → tool_compass-2.5.6}/requirements.txt +0 -0
  52. {tool_compass-2.5.4 → tool_compass-2.5.6}/scripts/check-org-urls.sh +0 -0
  53. {tool_compass-2.5.4 → tool_compass-2.5.6}/scripts/regenerate-scorecard.sh +0 -0
  54. {tool_compass-2.5.4 → tool_compass-2.5.6}/scripts/sync-version.mjs +0 -0
  55. {tool_compass-2.5.4 → tool_compass-2.5.6}/scripts/verify-metrics.sh +0 -0
  56. {tool_compass-2.5.4 → tool_compass-2.5.6}/sync_manager.py +0 -0
  57. {tool_compass-2.5.4 → tool_compass-2.5.6}/tool_manifest.py +0 -0
@@ -47,13 +47,18 @@ LOG_LEVEL=INFO
47
47
 
48
48
  # Host / port are set on the CLI, not via env vars — `ui.py` passes them
49
49
  # explicitly to Gradio's launch(), which overrides GRADIO_SERVER_NAME/PORT:
50
- # tool-compass ui --host 0.0.0.0 --port 7860
51
- # (defaults: 127.0.0.1:7860; the Docker image already binds 0.0.0.0.)
52
-
53
- # Public sharing: pass `tool-compass ui --share` for a public Gradio URL.
54
- # --share is REFUSED unless GRADIO_AUTH is set, so the link is never wide
55
- # open. Format is "user:pass" (both sides non-empty).
50
+ # tool-compass ui --host 0.0.0.0 --port 7860 --auth user:pass
51
+ # (defaults: 127.0.0.1:7860; the Docker UI image binds 0.0.0.0.)
52
+
53
+ # UI login in "user:pass" format (both sides non-empty). Applied on every UI
54
+ # start when set, and REQUIRED when binding a non-loopback host (including the
55
+ # Docker UI container) or passing `--share`.
56
+ # To start the Docker UI container (or any non-loopback bind), either:
57
+ # 1. Set GRADIO_AUTH=user:pass, OR
58
+ # 2. Set TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1 when running behind a proxy that
59
+ # does its own login (prints a startup warning and hides execute controls).
56
60
  # GRADIO_AUTH=user:pass
61
+ # TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1
57
62
 
58
63
  # =============================================================================
59
64
  # Docker-Specific
@@ -62,6 +67,26 @@ LOG_LEVEL=INFO
62
67
  # When running in Docker, use host.docker.internal to reach host services
63
68
  # OLLAMA_URL=http://host.docker.internal:11434
64
69
 
70
+ # =============================================================================
71
+ # HTTP Gateway Settings (when PORT is set or `tool-compass serve --http`)
72
+ # =============================================================================
73
+
74
+ # Port for streamable-http transport (unset = stdio mode)
75
+ # PORT=8080
76
+
77
+ # Bind host for HTTP mode (default: 127.0.0.1). Non-loopback values refuse to
78
+ # start unless TOOL_COMPASS_GATEWAY_AUTH_TOKEN (or gateway_auth_token) is set,
79
+ # or TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1 is set behind an authenticating proxy.
80
+ # HOST=127.0.0.1
81
+
82
+ # Bearer token for the HTTP gateway (overrides gateway_auth_token in
83
+ # compass_config.json). Required off loopback; optional on loopback.
84
+ # TOOL_COMPASS_GATEWAY_AUTH_TOKEN=
85
+
86
+ # Explicit opt-out allowing non-loopback HTTP startup without a bearer token
87
+ # when an authenticating reverse proxy sits in front (logs a startup warning).
88
+ # TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1
89
+
65
90
  # =============================================================================
66
91
  # Analytics (optional)
67
92
  # =============================================================================
@@ -204,7 +204,7 @@ jobs:
204
204
 
205
205
  - name: Atlas check
206
206
  if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
207
- run: npx --yes @dogfood-lab/atlas@1.24.0 check
207
+ run: npx --yes @dogfood-lab/atlas@1.26.0 check
208
208
 
209
209
  - name: Install dependencies
210
210
  run: |
@@ -219,7 +219,7 @@ jobs:
219
219
 
220
220
  - name: Upload JUnit report
221
221
  if: always()
222
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
222
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
223
223
  with:
224
224
  name: junit-reports-${{ matrix.python-version }}-${{ matrix.os }}
225
225
  path: junit-${{ matrix.python-version }}-${{ matrix.os }}.xml
@@ -242,7 +242,7 @@ jobs:
242
242
  # replace the artifact its earlier attempt saved.
243
243
  - name: Save coverage for Codecov
244
244
  if: ${{ env.COVERAGE_LEG == 'true' }}
245
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
245
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
246
246
  with:
247
247
  name: codecov-coverage
248
248
  path: coverage.xml
@@ -252,7 +252,7 @@ jobs:
252
252
 
253
253
  - name: Save test results for Codecov
254
254
  if: ${{ !cancelled() && env.COVERAGE_LEG == 'true' && steps.tests.outcome != 'skipped' }}
255
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
255
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
256
256
  with:
257
257
  name: codecov-test-results
258
258
  path: junit.xml
@@ -268,7 +268,7 @@ jobs:
268
268
 
269
269
  - name: Upload pip-audit report
270
270
  if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
271
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
271
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
272
272
  with:
273
273
  name: pip-audit-report
274
274
  path: pip-audit-report.json
@@ -277,13 +277,65 @@ jobs:
277
277
  # retention and is plenty of window for incident response.
278
278
  retention-days: 14
279
279
 
280
- # Now BLOCKING. The dogfood-swarm v3 pass reviewed the CVE baseline:
281
- # `pip-audit -r requirements.txt` reports no known vulnerabilities. The
280
+ # Now BLOCKING, in two parts. The core install (requirements.txt without
281
+ # the Web UI line) is audited strictly, with no ignores. The Web UI extra
282
+ # (gradio) is audited next with the dated Pillow exception (#157). The
282
283
  # report-only artifact step above still runs for the full JSON snapshot.
283
- - name: Audit dependencies for vulnerabilities
284
+ - name: Audit core dependencies for vulnerabilities
284
285
  if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
285
286
  run: |
286
- pip-audit -r requirements.txt
287
+ grep -vi '^gradio' requirements.txt > "$RUNNER_TEMP/requirements-core.txt"
288
+ pip-audit -r "$RUNNER_TEMP/requirements-core.txt"
289
+
290
+ - name: Audit Web UI extra for vulnerabilities
291
+ if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
292
+ run: |
293
+ grep -i '^gradio' requirements.txt > "$RUNNER_TEMP/requirements-ui.txt"
294
+ # Dated exception until 2026-12-10 (#157): gradio 6.31.0 requires
295
+ # pillow<13.0, holding pillow at 12.3.0 while the 40 advisories below
296
+ # are fixed in 13.0.0 and unreachable from tool-compass. Remove when
297
+ # a stable gradio allows pillow 13, or re-read on 2026-12-10.
298
+ pip-audit -r "$RUNNER_TEMP/requirements-ui.txt" \
299
+ --ignore-vuln PYSEC-2026-4327 \
300
+ --ignore-vuln PYSEC-2026-4328 \
301
+ --ignore-vuln PYSEC-2026-4326 \
302
+ --ignore-vuln PYSEC-2026-4339 \
303
+ --ignore-vuln PYSEC-2026-4340 \
304
+ --ignore-vuln PYSEC-2026-4343 \
305
+ --ignore-vuln PYSEC-2026-4334 \
306
+ --ignore-vuln PYSEC-2026-4337 \
307
+ --ignore-vuln PYSEC-2026-4341 \
308
+ --ignore-vuln PYSEC-2026-4330 \
309
+ --ignore-vuln PYSEC-2026-4338 \
310
+ --ignore-vuln PYSEC-2026-4335 \
311
+ --ignore-vuln PYSEC-2026-4336 \
312
+ --ignore-vuln PYSEC-2026-4331 \
313
+ --ignore-vuln PYSEC-2026-4332 \
314
+ --ignore-vuln PYSEC-2026-4329 \
315
+ --ignore-vuln PYSEC-2026-4333 \
316
+ --ignore-vuln PYSEC-2026-4342 \
317
+ --ignore-vuln CVE-2026-112895 \
318
+ --ignore-vuln CVE-2026-112400 \
319
+ --ignore-vuln CVE-2026-112897 \
320
+ --ignore-vuln CVE-2026-112395 \
321
+ --ignore-vuln CVE-2026-112404 \
322
+ --ignore-vuln CVE-2026-112899 \
323
+ --ignore-vuln CVE-2026-112900 \
324
+ --ignore-vuln CVE-2026-112893 \
325
+ --ignore-vuln CVE-2026-112891 \
326
+ --ignore-vuln CVE-2026-112390 \
327
+ --ignore-vuln CVE-2026-112401 \
328
+ --ignore-vuln CVE-2026-112391 \
329
+ --ignore-vuln CVE-2026-112894 \
330
+ --ignore-vuln CVE-2026-112406 \
331
+ --ignore-vuln CVE-2026-112399 \
332
+ --ignore-vuln CVE-2026-112892 \
333
+ --ignore-vuln CVE-2026-112393 \
334
+ --ignore-vuln CVE-2026-96638 \
335
+ --ignore-vuln CVE-2026-96640 \
336
+ --ignore-vuln CVE-2026-96778 \
337
+ --ignore-vuln GHSA-78g4-xqwv-q3g8 \
338
+ --ignore-vuln PYSEC-2026-4363
287
339
 
288
340
  codecov:
289
341
  # Uploads what the test job saved, from a job of its own, so the OIDC
@@ -310,13 +362,13 @@ jobs:
310
362
  # that matches nothing, unlike a name, does not fail: a failing test
311
363
  # run saves test results and no coverage.
312
364
  - name: Download coverage
313
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
365
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
314
366
  with:
315
367
  pattern: codecov-coverage
316
368
  path: .
317
369
 
318
370
  - name: Download test results
319
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
371
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
320
372
  with:
321
373
  pattern: codecov-test-results
322
374
  path: .
@@ -531,10 +583,10 @@ jobs:
531
583
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
532
584
 
533
585
  - name: Set up Docker Buildx
534
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
586
+ uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
535
587
 
536
588
  - name: Build Docker image
537
- uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
589
+ uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
538
590
  with:
539
591
  context: .
540
592
  target: ${{ matrix.target }}
@@ -547,10 +599,55 @@ jobs:
547
599
  - name: Smoke run
548
600
  run: |
549
601
  set -euo pipefail
550
- docker run -d --name tc-smoke -p ${{ matrix.port }}:${{ matrix.port }} ${{ matrix.tag }}
602
+ # PYTHONUNBUFFERED + PYTHONFAULTHANDLER: a start that stalls before the
603
+ # app logs anything (seen on hosted runners: the container printed only
604
+ # the entrypoint's volume warning for the whole window) must say where
605
+ # it is stuck. On timeout the step sends SIGABRT, which makes Python
606
+ # write every thread's stack to the container log before it dies.
607
+ if [ "${{ matrix.smoke }}" = "bind" ]; then
608
+ # UI image binds 0.0.0.0 and must refuse a bare start without login.
609
+ rc=0
610
+ docker run --rm ${{ matrix.tag }} >/dev/null 2>&1 || rc=$?
611
+ if [ "$rc" -ne 2 ]; then
612
+ echo "::error::UI container without GRADIO_AUTH exited $rc (expected 2)"
613
+ exit 1
614
+ fi
615
+ docker run -d --name tc-smoke -e PYTHONUNBUFFERED=1 -e PYTHONFAULTHANDLER=1 \
616
+ -e GRADIO_AUTH=u:p -p ${{ matrix.port }}:${{ matrix.port }} ${{ matrix.tag }}
617
+ else
618
+ SMOKE_ENV_ARGS=()
619
+ if [ "${{ matrix.target }}" = "mcp-gateway" ]; then
620
+ # Bare start (HOST=0.0.0.0, no token, no opt-out) must refuse with
621
+ # exit code 2. Bound with timeout so a fail-open start cannot hang.
622
+ set +e
623
+ BARE_OUT="$(timeout 30s docker run --rm --name tc-smoke-bare \
624
+ -e PYTHONUNBUFFERED=1 ${{ matrix.tag }} 2>&1)"
625
+ BARE_RC=$?
626
+ set -e
627
+ docker rm -f tc-smoke-bare >/dev/null 2>&1 || true
628
+ echo "$BARE_OUT"
629
+ if [ "$BARE_RC" -ne 2 ]; then
630
+ echo "::error::bare mcp-gateway start expected exit 2, got ${BARE_RC}"
631
+ exit 1
632
+ fi
633
+ SMOKE_VAL="ci-smoke"
634
+ SMOKE_ENV_ARGS=(-e "TOOL_COMPASS_GATEWAY_AUTH_TOKEN=${SMOKE_VAL}")
635
+ fi
636
+ docker run -d --name tc-smoke -e PYTHONUNBUFFERED=1 -e PYTHONFAULTHANDLER=1 \
637
+ ${SMOKE_ENV_ARGS[@]+"${SMOKE_ENV_ARGS[@]}"} \
638
+ -p ${{ matrix.port }}:${{ matrix.port }} ${{ matrix.tag }}
639
+ fi
551
640
  cleanup() { docker logs tc-smoke || true; docker rm -f tc-smoke || true; }
552
641
  trap cleanup EXIT
553
- for _ in $(seq 1 45); do
642
+ # 90 x 2s = 3 minutes. 45 x 2s was enough on every run that started
643
+ # normally (the image answers in a few seconds), but a hosted runner
644
+ # that is slow to start the process failed the required check with no
645
+ # code change; a crashed container still fails at once (below).
646
+ for _ in $(seq 1 90); do
647
+ if [ "$(docker inspect -f '{{.State.Running}}' tc-smoke)" != "true" ]; then
648
+ echo "::error::docker smoke (${{ matrix.target }}) container exited before it was ready"
649
+ exit 1
650
+ fi
554
651
  if [ "${{ matrix.smoke }}" = "health" ]; then
555
652
  if curl -sf "http://127.0.0.1:${{ matrix.port }}/health"; then
556
653
  echo "gateway /health ok"
@@ -569,6 +666,9 @@ jobs:
569
666
  sleep 2
570
667
  done
571
668
  echo "::error::docker smoke (${{ matrix.target }}) did not become ready on port ${{ matrix.port }}"
669
+ docker top tc-smoke || true
670
+ docker kill -s ABRT tc-smoke || true
671
+ sleep 2
572
672
  exit 1
573
673
 
574
674
  # ===========================================================================
@@ -77,7 +77,7 @@ jobs:
77
77
  run: twine check dist/*
78
78
 
79
79
  - name: Upload build artifacts
80
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
80
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
81
81
  with:
82
82
  name: dist
83
83
  path: dist/
@@ -104,13 +104,13 @@ jobs:
104
104
 
105
105
  steps:
106
106
  - name: Download build artifacts
107
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
107
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
108
108
  with:
109
109
  name: dist
110
110
  path: dist/
111
111
 
112
112
  - name: Publish to PyPI
113
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
113
+ uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
114
114
  with:
115
115
  # CT-B-009: emit SLSA build provenance attestations for the wheel
116
116
  # and sdist. PyPI verifies the attestation against the workflow's
@@ -207,13 +207,13 @@ jobs:
207
207
  # SHA-pinned per SLSA L3 supply-chain hygiene (CT-B-002). publish job
208
208
  # carries packages:write — a compromised mutable v3 tag would inherit
209
209
  # GHCR write capability during a release window. Bump via dependabot.
210
- uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0
210
+ uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
211
211
 
212
212
  - name: Set up Docker Buildx
213
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
213
+ uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
214
214
 
215
215
  - name: Build and push
216
- uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
216
+ uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
217
217
  with:
218
218
  context: .
219
219
  target: ${{ matrix.target }}
@@ -137,7 +137,7 @@ jobs:
137
137
  "$BIN" ui --help
138
138
 
139
139
  - name: Upload binary artifact
140
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
140
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
141
141
  with:
142
142
  name: binary-${{ matrix.target }}
143
143
  path: dist/${{ env.TOOL_NAME }}-*
@@ -154,7 +154,7 @@ jobs:
154
154
  attestations: write
155
155
  steps:
156
156
  - name: Download all binaries
157
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
157
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
158
158
  with:
159
159
  path: artifacts
160
160
  merge-multiple: true
@@ -0,0 +1,29 @@
1
+ {
2
+ "version": 1,
3
+ "pins": {
4
+ "0ec3b638688c0d83:zh": {
5
+ "lang": "zh",
6
+ "source": "Per-category scores are regenerated post-swarm via\n`bash scripts/regenerate-scorecard.sh` (which wraps `npx\n@mcptoolshop/shipcheck audit`). See [SCORECARD.md](SCORECARD.md) for the\ncurrent authoritative breakdown — the table below mirrors it and is\nintentionally not hand-authored. Hand-curated sections (Known Gaps,\nRemediation History) live outside the `<!-- SHIPCHECK-AUTO-START/END -->`\nmarkers in SCORECARD.md and survive regenerations.",
7
+ "translation": "通过 `bash scripts/regenerate-scorecard.sh` 在群集完成后重新生成每个类别的分数(该工具包装了 `npx @mcptoolshop/shipcheck audit`)。有关当前权威的细分,请参阅 [SCORECARD.md](SCORECARD.md)——下表是对其的镜像,并且有意不是手动编写的。手动策划的部分(已知差距、修复历史记录)位于 SCORECARD.md 中的 `<!-- SHIPCHECK-AUTO-START/END -->` 标记之外,并且会在重新生成时保留。",
8
+ "pinnedAt": "2026-11-12T11:38:07.275Z"
9
+ },
10
+ "28f2f2cfe1b673d7:ja": {
11
+ "lang": "ja",
12
+ "source": "Get the full `inputSchema` for a tool (required/enums/defaults)",
13
+ "translation": "ツールの完全な `inputSchema`(必須項目/列挙型/デフォルト値)を取得します。",
14
+ "pinnedAt": "2026-11-12T11:38:07.731Z"
15
+ },
16
+ "bb5538e8cb37670d:ja": {
17
+ "lang": "ja",
18
+ "source": "Set to `1` to allow starting the UI off loopback without `GRADIO_AUTH` behind an authenticating proxy (warns and hides execute controls)",
19
+ "translation": "`1` に設定すると、認証プロキシの背後で `GRADIO_AUTH` なしにループバック以外でUIを起動できます(警告を表示し、実行コントロールを非表示にします)。",
20
+ "pinnedAt": "2026-11-12T11:38:07.733Z"
21
+ },
22
+ "ee7649ad38ebf58b:zh": {
23
+ "lang": "zh",
24
+ "source": "📖 **Full documentation:** See the [Tool Compass Handbook](https://mcp-tool-shop-org.github.io/tool-compass/handbook/) for installation, configuration, and architecture deep-dives.",
25
+ "translation": "📖 **完整文档:** 请参阅 [Tool Compass Handbook](https://mcp-tool-shop-org.github.io/tool-compass/handbook/),其中包含有关安装、配置和架构的详细信息。",
26
+ "pinnedAt": "2026-11-12T11:38:07.271Z"
27
+ }
28
+ }
29
+ }
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.5.6] - 2026-11-12
11
+
12
+ ### Changed
13
+
14
+ - **The Gradio UI honours `GRADIO_AUTH` / `--auth` on every start, and refuses to start off loopback (including the Docker UI container) without a login.** Loopback starts (`127.0.0.1`, `localhost`, `::1`) continue to work without a login when `GRADIO_AUTH` is unset, and apply the login whenever `GRADIO_AUTH=user:pass` or `tool-compass ui --auth user:pass` is set (malformed values exit with code 2). Non-loopback starts and the Docker UI image require `GRADIO_AUTH=user:pass`, or `TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1` when running behind a proxy that enforces its own login (which prints a startup warning and hides the UI execute controls).
15
+ - Added a dated `pip-audit` exception in CI until 2026-12-10 (#157) for 39 unreachable `pillow` 12.3.0 advisories held below `13.0.0` by `gradio` 6.31.0's `pillow<13.0` requirement, and raised the coverage floor (`[tool.coverage.report].fail_under`) from 77 to 78.
16
+ - The HTTP gateway off loopback (`HOST` other than `127.0.0.1`, `localhost`, or `::1`) — including the `mcp-gateway` Docker image, the `docker-compose.yml` `gateway` service, and `fly.toml` — now refuses to start (exits 2) unless a non-empty bearer token is configured via `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` (or `gateway_auth_token` in `compass_config.json`, or `fly secrets set TOOL_COMPASS_GATEWAY_AUTH_TOKEN=<token>` on Fly.io) or the proxy opt-out `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1` is set (which logs a warning on every start). Loopback HTTP starts without a token continue to work unchanged, and `/health` and `/ready` remain unauthenticated for probes.
17
+ - Site dependencies updated with `npm audit fix` (`vite`, `undici`, `zod` and related packages); `npm audit` now reports 0 vulnerabilities for `site/`.
18
+ - The CI dependency audit is split in two: the core install is audited strictly with no exceptions, and the Web UI extra (`gradio`) keeps the dated Pillow exception.
19
+
20
+ ### Fixed
21
+
22
+ - The gateway's bearer check compares bytes instead of text. A non-ASCII `Authorization` header (or a non-ASCII configured token) made `hmac.compare_digest` raise, so the request got a 500 instead of a 401. It still refused the request, and a non-ASCII token now works.
23
+
24
+ ## [2.5.5] - 2026-10-15
25
+
26
+ ### Fixed
27
+
28
+ - **The Docker image no longer crashes with "Illegal instruction" on a CPU older than the one that built it.** `hnswlib` has no wheel for the image's Python, so the build compiles it, and its `setup.py` adds `-march=native` unless `HNSWLIB_NO_NATIVE` is set. The compiled extension therefore carried the build host's CPU extensions, and on a CPU without them `import hnswlib` (first reached from `indexer.py`) killed the process with SIGILL before the gateway printed anything. The builder stage now sets `HNSWLIB_NO_NATIVE=1`. It surfaced as an intermittent failure of the required `Docker mcp-gateway` smoke check, where a cached image layer built on one hosted runner ran on a different one.
29
+
30
+ ### Changed
31
+
32
+ - The CI Docker smoke waits up to 3 minutes instead of 90 seconds, fails at once if the container exits, and on a timeout sends SIGABRT so Python writes its stack to the log. No effect on the published package.
33
+
10
34
  ## [2.5.4] - 2026-10-14
11
35
 
12
36
  ### Security
@@ -42,7 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
42
66
 
43
67
  - `.gitattributes` pins LF on `*.sh`, the `Dockerfile` and the entrypoint. A
44
68
  shell script checked out with CRLF on Windows makes the shebang
45
- `#!/bin/sh
69
+ `#!/bin/sh
70
+ `, and Linux then fails with "no such file or directory" naming
46
71
  the script rather than the line endings — invisible to CI, which checks out
47
72
  LF on Linux, and visible only to a contributor building the image on Windows.
48
73
  Caught on the sibling xrpl-camp repo the same day.
@@ -28,6 +28,15 @@ COPY cli.py gateway.py ui.py indexer.py embedder.py chain_indexer.py \
28
28
  # the `tool-compass` console script ends up on PATH (ships with the image).
29
29
  RUN python -m venv /opt/venv
30
30
  ENV PATH="/opt/venv/bin:$PATH"
31
+ # hnswlib publishes no wheel for this interpreter, so pip compiles it here, and
32
+ # its setup.py adds -march=native unless HNSWLIB_NO_NATIVE is set. That bakes the
33
+ # build machine's CPU extensions (AVX-512 on some hosted runners) into the image;
34
+ # on a CPU without them the first `import hnswlib` dies with SIGILL ("Illegal
35
+ # instruction") before the gateway prints anything. Observed as an intermittent
36
+ # CI smoke failure when a cached layer built on one runner ran on another, and
37
+ # it is the same hazard for anyone pulling the published image onto an older CPU.
38
+ # Builder stage only: the production stage copies the venv, not the variable.
39
+ ENV HNSWLIB_NO_NATIVE=1
31
40
  RUN pip install --no-cache-dir --upgrade pip && \
32
41
  pip install --no-cache-dir -r requirements.txt && \
33
42
  pip install --no-cache-dir uvicorn && \
@@ -104,9 +113,10 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
104
113
 
105
114
  # Default command: Run Gradio UI.
106
115
  # UI-DOCKER-BIND-001: bind 0.0.0.0 explicitly so the UI is reachable through the
107
- # published port mapping (inside the container the container boundary is the
108
- # isolation layer; external exposure is controlled by `docker run -p` /
109
- # compose `ports:`). Without this the UI bound to 127.0.0.1 and was unreachable.
116
+ # published port mapping; without this the UI bound to 127.0.0.1 inside the
117
+ # container and was unreachable. Because 0.0.0.0 is non-loopback, startup
118
+ # refuses to run unless GRADIO_AUTH=user:pass is set (or
119
+ # TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1 is set behind a proxy that does its own login).
110
120
  CMD ["python", "ui.py", "--host", "0.0.0.0", "--port", "7860"]
111
121
 
112
122
  # =============================================================================
@@ -114,12 +124,15 @@ CMD ["python", "ui.py", "--host", "0.0.0.0", "--port", "7860"]
114
124
  # =============================================================================
115
125
  FROM production AS mcp-gateway
116
126
 
117
- # Override for HTTP mode (Fly.io / Smithery)
127
+ # Override for HTTP mode (Fly.io / Smithery / reverse-proxy deployments).
118
128
  # ci-infra-01: gateway.py's _run_http defaults HOST to 127.0.0.1 (loopback), so
119
- # without this the published image binds loopback and any orchestrator's proxy /
120
- # health check (the HEALTHCHECK below hits localhost, but Fly/Smithery reach the
121
- # container over the network) can't reach the gateway. 0.0.0.0 is the documented
122
- # reverse-proxy path — this image is meant to sit behind an authenticated edge.
129
+ # without this the published image binds loopback and orchestrators reaching the
130
+ # container over the network cannot connect. Because HOST=0.0.0.0 is non-loopback,
131
+ # the container refuses to start (exit 2) unless the operator passes
132
+ # TOOL_COMPASS_GATEWAY_AUTH_TOKEN (or sets gateway_auth_token in config), or sets
133
+ # TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1 behind an authenticating proxy. Neither
134
+ # variable is set here so the image fails closed by default; /health stays exempt
135
+ # so the HEALTHCHECK below works with a token set.
123
136
  ENV PORT=8080 \
124
137
  HOST=0.0.0.0
125
138
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tool-compass
3
- Version: 2.5.4
3
+ Version: 2.5.6
4
4
  Summary: Semantic MCP tool discovery gateway - find tools by intent, not memory
5
5
  Project-URL: Homepage, https://github.com/mcp-tool-shop-org/tool-compass
6
6
  Project-URL: Documentation, https://github.com/mcp-tool-shop-org/tool-compass#readme
@@ -143,21 +143,31 @@ tool-compass ui
143
143
  git clone https://github.com/mcp-tool-shop-org/tool-compass.git
144
144
  cd tool-compass
145
145
 
146
- # Start with Docker Compose (requires Ollama running locally)
147
- docker-compose up
146
+ # Start with Docker Compose (requires Ollama running locally and a UI login)
147
+ GRADIO_AUTH=user:pass docker compose up
148
148
 
149
149
  # Or include Ollama in the stack
150
- docker-compose --profile with-ollama up
150
+ GRADIO_AUTH=user:pass docker compose --profile with-ollama up
151
151
 
152
152
  # Access the UI at http://localhost:7860
153
153
  ```
154
154
 
155
- Or run the published image directly, without cloning:
155
+ Or run the published UI image directly, without cloning:
156
156
 
157
157
  ```bash
158
- docker run --rm -p 7860:7860 -v tool-compass-data:/app/tool_compass/db ghcr.io/mcp-tool-shop-org/tool-compass:latest
158
+ docker run --rm -p 7860:7860 \
159
+ -e GRADIO_AUTH=user:pass \
160
+ -v tool-compass-data:/app/tool_compass/db \
161
+ ghcr.io/mcp-tool-shop-org/tool-compass:ui
159
162
  ```
160
163
 
164
+ > **UI login required in containers.** Because the container binds `0.0.0.0`,
165
+ > startup refuses to run without a login. Start it in one of two ways: set
166
+ > `GRADIO_AUTH=user:pass` (via `.env`, shell environment, or `-e`), or set
167
+ > `TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1` when deploying behind a reverse proxy that
168
+ > enforces its own login (prints a startup warning and hides the UI execute
169
+ > controls).
170
+
161
171
  > **Mount the volume.** The semantic index lives in `/app/tool_compass/db`, and
162
172
  > building it means embedding every tool in every configured server. Without a
163
173
  > volume that work is thrown away when the container exits and paid again on the
@@ -169,6 +179,19 @@ docker run --rm -p 7860:7860 -v tool-compass-data:/app/tool_compass/db ghcr.
169
179
  > `linux/amd64` and `linux/arm64`, so the same tag runs on x86_64 servers
170
180
  > and Apple Silicon / ARM workstations.
171
181
 
182
+ To run the **MCP HTTP gateway** container (`mcp-gateway` stage, port `8080`), supply a bearer token — because the container binds `HOST=0.0.0.0`, it refuses to start without `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` (or `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1` when behind an authenticating reverse proxy):
183
+
184
+ ```bash
185
+ # Docker Compose (gateway profile)
186
+ TOOL_COMPASS_GATEWAY_AUTH_TOKEN=<your-token> docker compose --profile gateway up
187
+
188
+ # Or published gateway image directly
189
+ docker run --rm -p 8080:8080 \
190
+ -e TOOL_COMPASS_GATEWAY_AUTH_TOKEN=<your-token> \
191
+ -v tool-compass-data:/app/tool_compass/db \
192
+ ghcr.io/mcp-tool-shop-org/tool-compass:gateway
193
+ ```
194
+
172
195
  ## Features
173
196
 
174
197
  - **Hybrid Search** - Semantic (HNSW) + lexical fusion with exact-name boost — describe what you want, or paste a tool name and it ranks #1
@@ -289,7 +312,11 @@ The `hint` field in compass results guides this flow, suggesting when to use `de
289
312
  | `OLLAMA_URL` | Ollama server URL | `http://localhost:11434` |
290
313
  | `COMFYUI_URL` | ComfyUI server | `http://localhost:8188` |
291
314
  | `PORT` | Set to enable HTTP transport (e.g., for Fly.io) | unset (stdio) |
292
- | `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` | Bearer token required on the HTTP transport (opt-in; overrides the `gateway_auth_token` config field) | unset (no auth) |
315
+ | `HOST` | Bind address for the HTTP transport; non-loopback values require `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` (or `gateway_auth_token`) unless `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1` is set | `127.0.0.1` |
316
+ | `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` | Bearer token for the HTTP transport (overrides `gateway_auth_token` in `compass_config.json`). Required when `HOST` is not loopback (`127.0.0.1`, `localhost`, `::1`); optional on loopback | unset (refuses off loopback) |
317
+ | `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN` | Set to `1` to allow binding the HTTP gateway off loopback without a bearer token when an authenticating reverse proxy sits in front (logs a warning on every start) | unset (disabled) |
318
+ | `GRADIO_AUTH` | UI login (`user:pass`); applied on every UI start when set, and required off loopback / in Docker / on `--share` | unset (loopback only) |
319
+ | `TOOL_COMPASS_UI_ALLOW_NO_LOGIN` | Set to `1` to allow starting the UI off loopback without `GRADIO_AUTH` behind an authenticating proxy (warns and hides execute controls) | unset |
293
320
 
294
321
  **Default data directories:**
295
322
  - **Windows:** `%LOCALAPPDATA%\tool-compass\`
@@ -98,21 +98,31 @@ tool-compass ui
98
98
  git clone https://github.com/mcp-tool-shop-org/tool-compass.git
99
99
  cd tool-compass
100
100
 
101
- # Start with Docker Compose (requires Ollama running locally)
102
- docker-compose up
101
+ # Start with Docker Compose (requires Ollama running locally and a UI login)
102
+ GRADIO_AUTH=user:pass docker compose up
103
103
 
104
104
  # Or include Ollama in the stack
105
- docker-compose --profile with-ollama up
105
+ GRADIO_AUTH=user:pass docker compose --profile with-ollama up
106
106
 
107
107
  # Access the UI at http://localhost:7860
108
108
  ```
109
109
 
110
- Or run the published image directly, without cloning:
110
+ Or run the published UI image directly, without cloning:
111
111
 
112
112
  ```bash
113
- docker run --rm -p 7860:7860 -v tool-compass-data:/app/tool_compass/db ghcr.io/mcp-tool-shop-org/tool-compass:latest
113
+ docker run --rm -p 7860:7860 \
114
+ -e GRADIO_AUTH=user:pass \
115
+ -v tool-compass-data:/app/tool_compass/db \
116
+ ghcr.io/mcp-tool-shop-org/tool-compass:ui
114
117
  ```
115
118
 
119
+ > **UI login required in containers.** Because the container binds `0.0.0.0`,
120
+ > startup refuses to run without a login. Start it in one of two ways: set
121
+ > `GRADIO_AUTH=user:pass` (via `.env`, shell environment, or `-e`), or set
122
+ > `TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1` when deploying behind a reverse proxy that
123
+ > enforces its own login (prints a startup warning and hides the UI execute
124
+ > controls).
125
+
116
126
  > **Mount the volume.** The semantic index lives in `/app/tool_compass/db`, and
117
127
  > building it means embedding every tool in every configured server. Without a
118
128
  > volume that work is thrown away when the container exits and paid again on the
@@ -124,6 +134,19 @@ docker run --rm -p 7860:7860 -v tool-compass-data:/app/tool_compass/db ghcr.
124
134
  > `linux/amd64` and `linux/arm64`, so the same tag runs on x86_64 servers
125
135
  > and Apple Silicon / ARM workstations.
126
136
 
137
+ To run the **MCP HTTP gateway** container (`mcp-gateway` stage, port `8080`), supply a bearer token — because the container binds `HOST=0.0.0.0`, it refuses to start without `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` (or `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1` when behind an authenticating reverse proxy):
138
+
139
+ ```bash
140
+ # Docker Compose (gateway profile)
141
+ TOOL_COMPASS_GATEWAY_AUTH_TOKEN=<your-token> docker compose --profile gateway up
142
+
143
+ # Or published gateway image directly
144
+ docker run --rm -p 8080:8080 \
145
+ -e TOOL_COMPASS_GATEWAY_AUTH_TOKEN=<your-token> \
146
+ -v tool-compass-data:/app/tool_compass/db \
147
+ ghcr.io/mcp-tool-shop-org/tool-compass:gateway
148
+ ```
149
+
127
150
  ## Features
128
151
 
129
152
  - **Hybrid Search** - Semantic (HNSW) + lexical fusion with exact-name boost — describe what you want, or paste a tool name and it ranks #1
@@ -244,7 +267,11 @@ The `hint` field in compass results guides this flow, suggesting when to use `de
244
267
  | `OLLAMA_URL` | Ollama server URL | `http://localhost:11434` |
245
268
  | `COMFYUI_URL` | ComfyUI server | `http://localhost:8188` |
246
269
  | `PORT` | Set to enable HTTP transport (e.g., for Fly.io) | unset (stdio) |
247
- | `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` | Bearer token required on the HTTP transport (opt-in; overrides the `gateway_auth_token` config field) | unset (no auth) |
270
+ | `HOST` | Bind address for the HTTP transport; non-loopback values require `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` (or `gateway_auth_token`) unless `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1` is set | `127.0.0.1` |
271
+ | `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` | Bearer token for the HTTP transport (overrides `gateway_auth_token` in `compass_config.json`). Required when `HOST` is not loopback (`127.0.0.1`, `localhost`, `::1`); optional on loopback | unset (refuses off loopback) |
272
+ | `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN` | Set to `1` to allow binding the HTTP gateway off loopback without a bearer token when an authenticating reverse proxy sits in front (logs a warning on every start) | unset (disabled) |
273
+ | `GRADIO_AUTH` | UI login (`user:pass`); applied on every UI start when set, and required off loopback / in Docker / on `--share` | unset (loopback only) |
274
+ | `TOOL_COMPASS_UI_ALLOW_NO_LOGIN` | Set to `1` to allow starting the UI off loopback without `GRADIO_AUTH` behind an authenticating proxy (warns and hides execute controls) | unset |
248
275
 
249
276
  **Default data directories:**
250
277
  - **Windows:** `%LOCALAPPDATA%\tool-compass\`
@@ -61,8 +61,9 @@ Tool Compass is designed as a **local development tool**. It has these security
61
61
 
62
62
  | Component | Security Model |
63
63
  |-----------|----------------|
64
- | Gradio UI | No authentication by default |
65
- | MCP Gateway | Trusts backend servers |
64
+ | Gradio UI | Loopback (`127.0.0.1` / `localhost` / `::1`) has no login by default unless `GRADIO_AUTH` / `--auth` is set; non-loopback binds (including Docker) and `--share` require `GRADIO_AUTH=user:pass` (or `TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1` behind an authenticating proxy, which warns and hides execute controls) |
65
+ | MCP Gateway (stdio) | Local subprocess transport; trusts configured backend servers |
66
+ | MCP Gateway (HTTP) | Binds `127.0.0.1` by default; off loopback refuses to start without `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` (`gateway_auth_token`) or `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1` behind an authenticating proxy (`/health` and `/ready` remain unauthenticated for probes); trusts configured backend servers |
66
67
  | Analytics DB | Local SQLite, no encryption |
67
68
  | Embeddings | Sent to local Ollama |
68
69
 
@@ -70,14 +71,12 @@ Tool Compass is designed as a **local development tool**. It has these security
70
71
 
71
72
  If deploying Tool Compass in a shared environment:
72
73
 
73
- 1. **Enable Gradio authentication**:
74
- ```python
75
- demo.launch(auth=("user", "password"))
76
- ```
74
+ 1. **Configure UI login**:
75
+ Set `GRADIO_AUTH=user:pass` (or pass `tool-compass ui --auth user:pass`). Non-loopback binds and the Docker UI image refuse to start without `GRADIO_AUTH` unless `TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1` is explicitly set behind a reverse proxy that enforces its own login (which prints a startup warning and hides the UI execute controls).
77
76
 
78
77
  2. **Use environment variables** for sensitive config (not `compass_config.json`)
79
78
 
80
- 3. **Network isolation**: Run behind a reverse proxy with auth
79
+ 3. **Network isolation and HTTP gateway auth**: Run behind an authenticating reverse proxy (`TOOL_COMPASS_UI_ALLOW_NO_LOGIN=1` if delegating UI login to the proxy). When the HTTP gateway binds off loopback (`HOST` not `127.0.0.1`, `localhost`, or `::1`), set `TOOL_COMPASS_GATEWAY_AUTH_TOKEN` (or `gateway_auth_token` in config, or `fly secrets set TOOL_COMPASS_GATEWAY_AUTH_TOKEN=<token>` on Fly.io); only set `TOOL_COMPASS_GATEWAY_ALLOW_NO_TOKEN=1` when a proxy in front enforces authentication and blocks direct access to the gateway port
81
80
 
82
81
  4. **Rate limiting**: Add nginx/Cloudflare rate limiting for the UI
83
82