skopos-fleet 0.1.0__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 (163) hide show
  1. skopos_fleet-0.1.0/LICENSE +21 -0
  2. skopos_fleet-0.1.0/PKG-INFO +195 -0
  3. skopos_fleet-0.1.0/README.md +167 -0
  4. skopos_fleet-0.1.0/pyproject.toml +47 -0
  5. skopos_fleet-0.1.0/setup.cfg +4 -0
  6. skopos_fleet-0.1.0/skopos/__init__.py +2 -0
  7. skopos_fleet-0.1.0/skopos/agent/__init__.py +18 -0
  8. skopos_fleet-0.1.0/skopos/agent/analyst.py +98 -0
  9. skopos_fleet-0.1.0/skopos/agent/config.py +103 -0
  10. skopos_fleet-0.1.0/skopos/agent/context.py +239 -0
  11. skopos_fleet-0.1.0/skopos/agent/ecosystem_briefing.py +485 -0
  12. skopos_fleet-0.1.0/skopos/agent/injection_guard.py +115 -0
  13. skopos_fleet-0.1.0/skopos/agent/providers.py +369 -0
  14. skopos_fleet-0.1.0/skopos/agent/rate_limit.py +31 -0
  15. skopos_fleet-0.1.0/skopos/agent/redact.py +25 -0
  16. skopos_fleet-0.1.0/skopos/agent/security_report.py +157 -0
  17. skopos_fleet-0.1.0/skopos/agent/service.py +145 -0
  18. skopos_fleet-0.1.0/skopos/agent_portal.py +52 -0
  19. skopos_fleet-0.1.0/skopos/agent_token.py +109 -0
  20. skopos_fleet-0.1.0/skopos/agent_widget.py +493 -0
  21. skopos_fleet-0.1.0/skopos/analytics_filters.py +218 -0
  22. skopos_fleet-0.1.0/skopos/apache.py +7 -0
  23. skopos_fleet-0.1.0/skopos/apache_logs.py +149 -0
  24. skopos_fleet-0.1.0/skopos/app_auth.py +192 -0
  25. skopos_fleet-0.1.0/skopos/app_shell.py +352 -0
  26. skopos_fleet-0.1.0/skopos/auth_store.py +320 -0
  27. skopos_fleet-0.1.0/skopos/backfill.py +205 -0
  28. skopos_fleet-0.1.0/skopos/brand.py +69 -0
  29. skopos_fleet-0.1.0/skopos/charts.py +766 -0
  30. skopos_fleet-0.1.0/skopos/collector.py +185 -0
  31. skopos_fleet-0.1.0/skopos/components/voice_input/__init__.py +19 -0
  32. skopos_fleet-0.1.0/skopos/config.py +234 -0
  33. skopos_fleet-0.1.0/skopos/config_io.py +167 -0
  34. skopos_fleet-0.1.0/skopos/config_paths.py +22 -0
  35. skopos_fleet-0.1.0/skopos/countries.py +263 -0
  36. skopos_fleet-0.1.0/skopos/db.py +373 -0
  37. skopos_fleet-0.1.0/skopos/db_connection.py +123 -0
  38. skopos_fleet-0.1.0/skopos/db_dialect.py +103 -0
  39. skopos_fleet-0.1.0/skopos/db_migrate.py +188 -0
  40. skopos_fleet-0.1.0/skopos/db_settings.py +109 -0
  41. skopos_fleet-0.1.0/skopos/doc_i18n.py +141 -0
  42. skopos_fleet-0.1.0/skopos/docs_i18n_render.py +792 -0
  43. skopos_fleet-0.1.0/skopos/docs_i18n_translations.py +527 -0
  44. skopos_fleet-0.1.0/skopos/docs_i18n_translations_rest.py +2630 -0
  45. skopos_fleet-0.1.0/skopos/docs_i18n_ui.py +369 -0
  46. skopos_fleet-0.1.0/skopos/docs_viewer.py +181 -0
  47. skopos_fleet-0.1.0/skopos/economy/__init__.py +14 -0
  48. skopos_fleet-0.1.0/skopos/economy/capabilities.py +109 -0
  49. skopos_fleet-0.1.0/skopos/economy/config.py +56 -0
  50. skopos_fleet-0.1.0/skopos/economy/consumer.py +62 -0
  51. skopos_fleet-0.1.0/skopos/economy/handlers.py +162 -0
  52. skopos_fleet-0.1.0/skopos/economy/invoke.py +58 -0
  53. skopos_fleet-0.1.0/skopos/economy/manifest.py +95 -0
  54. skopos_fleet-0.1.0/skopos/ecosystem.py +60 -0
  55. skopos_fleet-0.1.0/skopos/enrich.py +62 -0
  56. skopos_fleet-0.1.0/skopos/env.py +5 -0
  57. skopos_fleet-0.1.0/skopos/env_io.py +65 -0
  58. skopos_fleet-0.1.0/skopos/geo_centroids.py +263 -0
  59. skopos_fleet-0.1.0/skopos/geo_earth.py +147 -0
  60. skopos_fleet-0.1.0/skopos/geoip.py +196 -0
  61. skopos_fleet-0.1.0/skopos/host_infer.py +108 -0
  62. skopos_fleet-0.1.0/skopos/i18n.py +265 -0
  63. skopos_fleet-0.1.0/skopos/log_sources.py +164 -0
  64. skopos_fleet-0.1.0/skopos/nav_chrome.py +40 -0
  65. skopos_fleet-0.1.0/skopos/nginx.py +110 -0
  66. skopos_fleet-0.1.0/skopos/nginx_logs.py +104 -0
  67. skopos_fleet-0.1.0/skopos/password_policy.py +93 -0
  68. skopos_fleet-0.1.0/skopos/period_picker.py +326 -0
  69. skopos_fleet-0.1.0/skopos/premium_chrome.py +642 -0
  70. skopos_fleet-0.1.0/skopos/public_status.py +49 -0
  71. skopos_fleet-0.1.0/skopos/security/__init__.py +11 -0
  72. skopos_fleet-0.1.0/skopos/security/alert_i18n.py +38 -0
  73. skopos_fleet-0.1.0/skopos/security/audit.py +312 -0
  74. skopos_fleet-0.1.0/skopos/security/auto_scan.py +92 -0
  75. skopos_fleet-0.1.0/skopos/security/charts.py +738 -0
  76. skopos_fleet-0.1.0/skopos/security/collector.py +63 -0
  77. skopos_fleet-0.1.0/skopos/security/docker_insights.py +263 -0
  78. skopos_fleet-0.1.0/skopos/security/fail2ban_status.py +111 -0
  79. skopos_fleet-0.1.0/skopos/security/history_charts.py +131 -0
  80. skopos_fleet-0.1.0/skopos/security/knock_analyzer.py +181 -0
  81. skopos_fleet-0.1.0/skopos/security/knock_charts.py +156 -0
  82. skopos_fleet-0.1.0/skopos/security/port_knocks.py +204 -0
  83. skopos_fleet-0.1.0/skopos/security/posture.py +265 -0
  84. skopos_fleet-0.1.0/skopos/security/posture_loader.py +118 -0
  85. skopos_fleet-0.1.0/skopos/security/probe.py +324 -0
  86. skopos_fleet-0.1.0/skopos/security/project_audit.py +154 -0
  87. skopos_fleet-0.1.0/skopos/security/report_builder.py +164 -0
  88. skopos_fleet-0.1.0/skopos/security/store.py +691 -0
  89. skopos_fleet-0.1.0/skopos/security/tls_certs.py +340 -0
  90. skopos_fleet-0.1.0/skopos/setup_state.py +187 -0
  91. skopos_fleet-0.1.0/skopos/shell_safe.py +58 -0
  92. skopos_fleet-0.1.0/skopos/ssh.py +76 -0
  93. skopos_fleet-0.1.0/skopos/ssh_setup.py +124 -0
  94. skopos_fleet-0.1.0/skopos/telegram_notify.py +271 -0
  95. skopos_fleet-0.1.0/skopos/theme_prefs.py +76 -0
  96. skopos_fleet-0.1.0/skopos/themes.py +2803 -0
  97. skopos_fleet-0.1.0/skopos/traffic.py +33 -0
  98. skopos_fleet-0.1.0/skopos/ui.py +259 -0
  99. skopos_fleet-0.1.0/skopos/ui_agent.py +65 -0
  100. skopos_fleet-0.1.0/skopos/ui_agent_settings.py +127 -0
  101. skopos_fleet-0.1.0/skopos/ui_briefing.py +268 -0
  102. skopos_fleet-0.1.0/skopos/ui_dashboard_auth.py +207 -0
  103. skopos_fleet-0.1.0/skopos/ui_database_settings.py +179 -0
  104. skopos_fleet-0.1.0/skopos/ui_onboarding.py +86 -0
  105. skopos_fleet-0.1.0/skopos/ui_password_gate.py +97 -0
  106. skopos_fleet-0.1.0/skopos/ui_security.py +126 -0
  107. skopos_fleet-0.1.0/skopos/ui_security_report.py +170 -0
  108. skopos_fleet-0.1.0/skopos/ui_sidebar.py +5 -0
  109. skopos_fleet-0.1.0/skopos/ui_topbar.py +333 -0
  110. skopos_fleet-0.1.0/skopos/ui_wizard.py +389 -0
  111. skopos_fleet-0.1.0/skopos/uvicorn_log.py +59 -0
  112. skopos_fleet-0.1.0/skopos_fleet.egg-info/PKG-INFO +195 -0
  113. skopos_fleet-0.1.0/skopos_fleet.egg-info/SOURCES.txt +161 -0
  114. skopos_fleet-0.1.0/skopos_fleet.egg-info/dependency_links.txt +1 -0
  115. skopos_fleet-0.1.0/skopos_fleet.egg-info/entry_points.txt +2 -0
  116. skopos_fleet-0.1.0/skopos_fleet.egg-info/requires.txt +14 -0
  117. skopos_fleet-0.1.0/skopos_fleet.egg-info/top_level.txt +2 -0
  118. skopos_fleet-0.1.0/skoposctl.py +57 -0
  119. skopos_fleet-0.1.0/tests/test_agent_config.py +27 -0
  120. skopos_fleet-0.1.0/tests/test_agent_injection.py +55 -0
  121. skopos_fleet-0.1.0/tests/test_agent_service.py +47 -0
  122. skopos_fleet-0.1.0/tests/test_agent_token.py +47 -0
  123. skopos_fleet-0.1.0/tests/test_alert_i18n.py +17 -0
  124. skopos_fleet-0.1.0/tests/test_analytics_filters.py +13 -0
  125. skopos_fleet-0.1.0/tests/test_apache_logs.py +199 -0
  126. skopos_fleet-0.1.0/tests/test_app_smoke.py +34 -0
  127. skopos_fleet-0.1.0/tests/test_auth_store.py +113 -0
  128. skopos_fleet-0.1.0/tests/test_chart_labels.py +43 -0
  129. skopos_fleet-0.1.0/tests/test_collector_status.py +21 -0
  130. skopos_fleet-0.1.0/tests/test_db_backend.py +92 -0
  131. skopos_fleet-0.1.0/tests/test_db_migrate.py +80 -0
  132. skopos_fleet-0.1.0/tests/test_display_df.py +42 -0
  133. skopos_fleet-0.1.0/tests/test_docker_insights.py +51 -0
  134. skopos_fleet-0.1.0/tests/test_docs_public.py +12 -0
  135. skopos_fleet-0.1.0/tests/test_docs_viewer.py +77 -0
  136. skopos_fleet-0.1.0/tests/test_economy.py +82 -0
  137. skopos_fleet-0.1.0/tests/test_ecosystem_briefing.py +114 -0
  138. skopos_fleet-0.1.0/tests/test_ecosystem_connectivity.py +146 -0
  139. skopos_fleet-0.1.0/tests/test_env_io.py +21 -0
  140. skopos_fleet-0.1.0/tests/test_fullscreen_plot.py +32 -0
  141. skopos_fleet-0.1.0/tests/test_geoip_fallback.py +49 -0
  142. skopos_fleet-0.1.0/tests/test_globe_chart.py +45 -0
  143. skopos_fleet-0.1.0/tests/test_i18n.py +129 -0
  144. skopos_fleet-0.1.0/tests/test_llm_fallback.py +84 -0
  145. skopos_fleet-0.1.0/tests/test_llm_rate_limit.py +13 -0
  146. skopos_fleet-0.1.0/tests/test_openrouter_providers.py +64 -0
  147. skopos_fleet-0.1.0/tests/test_password_policy.py +67 -0
  148. skopos_fleet-0.1.0/tests/test_period_picker.py +78 -0
  149. skopos_fleet-0.1.0/tests/test_port_knocks.py +49 -0
  150. skopos_fleet-0.1.0/tests/test_posture.py +62 -0
  151. skopos_fleet-0.1.0/tests/test_scan_history.py +88 -0
  152. skopos_fleet-0.1.0/tests/test_security_audit.py +38 -0
  153. skopos_fleet-0.1.0/tests/test_security_charts.py +138 -0
  154. skopos_fleet-0.1.0/tests/test_security_hardening.py +73 -0
  155. skopos_fleet-0.1.0/tests/test_security_probe.py +21 -0
  156. skopos_fleet-0.1.0/tests/test_security_report.py +39 -0
  157. skopos_fleet-0.1.0/tests/test_security_scan_config.py +30 -0
  158. skopos_fleet-0.1.0/tests/test_settings_ssh.py +70 -0
  159. skopos_fleet-0.1.0/tests/test_setup_wizard.py +72 -0
  160. skopos_fleet-0.1.0/tests/test_telegram_notify.py +173 -0
  161. skopos_fleet-0.1.0/tests/test_theme_prefs.py +14 -0
  162. skopos_fleet-0.1.0/tests/test_theme_state.py +40 -0
  163. skopos_fleet-0.1.0/tests/test_themes.py +153 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stats Fleet Center contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,195 @@
1
+ Metadata-Version: 2.4
2
+ Name: skopos-fleet
3
+ Version: 0.1.0
4
+ Summary: SKOPOS (Σκοπός) — self-hosted nginx analytics and fleet security for the AICOM ecosystem.
5
+ Author: AICOM (alexar76)
6
+ License: MIT
7
+ Keywords: nginx,analytics,security,streamlit,aimarket,aicom,fleet,ssh
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: streamlit>=1.33
16
+ Requires-Dist: pandas>=2.0
17
+ Requires-Dist: plotly>=5.18
18
+ Requires-Dist: pyyaml>=6.0
19
+ Requires-Dist: paramiko>=3.4
20
+ Requires-Dist: python-dotenv>=1.0
21
+ Requires-Dist: httpx>=0.27
22
+ Requires-Dist: geoip2>=4.8
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.0; extra == "dev"
25
+ Provides-Extra: postgres
26
+ Requires-Dist: psycopg[binary]>=3.2; extra == "postgres"
27
+ Dynamic: license-file
28
+
29
+ # SKOPOS — fleet observability satellite of the AICOM ecosystem
30
+
31
+ <!-- aicom-readme-badges -->
32
+ <p align="center">
33
+ <a href="https://github.com/alexar76/skopos/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/alexar76/skopos/ci.yml?branch=main&label=CI" alt="CI" /></a>
34
+ <a href="https://github.com/alexar76/skopos/actions/workflows/pages.yml"><img src="https://img.shields.io/github/actions/workflow/status/alexar76/skopos/pages.yml?branch=main&label=Pages" alt="Pages deploy" /></a>
35
+ <a href="https://skopos.modelmarket.dev/"><img src="https://img.shields.io/badge/demo-live-brightgreen" alt="Live demo" /></a>
36
+ <a href="https://alexar76.github.io/skopos/"><img src="https://img.shields.io/badge/landing-cosmic-4de8ff" alt="Cosmic landing" /></a>
37
+ <img src="https://img.shields.io/badge/python-%3E%3D3.11-brightgreen.svg" alt="Python >=3.11" />
38
+ <img src="https://img.shields.io/badge/tests-226_passing-brightgreen" alt="226 tests passing" />
39
+ <img src="https://img.shields.io/badge/stack-Streamlit%20%2B%20PostgreSQL-6e40c9" alt="Streamlit + PostgreSQL" />
40
+ <a href="#testing--coverage"><img src="docs/badges/coverage.svg" alt="Test coverage" /></a>
41
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /></a>
42
+ </p>
43
+ <!-- /aicom-readme-badges -->
44
+
45
+
46
+
47
+ > **Self-hosted nginx & Apache analytics and AI security for your server fleet** — GA-like HTTP dashboards, Security Center with 3D threat map, scan history, and an AI agent with voice input. No third-party trackers; data stays on your infrastructure.
48
+
49
+ <p align="center">
50
+ <a href="https://skopos.modelmarket.dev/app/">
51
+ <img src="docs/screenshots/readme/hero-banner.png" alt="SKOPOS — Web Analytics and Security Center with 3D threat map" width="100%" />
52
+ </a>
53
+ </p>
54
+
55
+ <p align="center">
56
+ <strong><a href="https://skopos.modelmarket.dev">Live demo</a></strong>
57
+ ·
58
+ <strong><a href="https://alexar76.github.io/skopos/">Landing</a></strong>
59
+ ·
60
+ nine built-in themes
61
+ </p>
62
+
63
+ In Greek, **skopos** (σκοπός) means *watcher* or *scout*. **SKOPOS** is the fleet observability satellite of the [AICOM / AIMarket ecosystem](https://magic-ai-factory.com): nginx traffic over SSH, security posture across factory / metis / oracle hosts, and an LLM analyst in the sidebar.
64
+
65
+ | | |
66
+ |---|---|
67
+ | **Role** | SSH log collection → SQLite/PostgreSQL analytics → Security Center + AI agent |
68
+ | **Live demo** | [skopos.modelmarket.dev](https://skopos.modelmarket.dev) |
69
+ | **Landing (GitHub Pages)** | [alexar76.github.io/skopos](https://alexar76.github.io/skopos/) |
70
+ | **Monitors** | **nginx** access logs (primary), **Apache** combined, CPU/RAM/disk, ports, fail2ban, port knocks |
71
+ | **Charter** | Read-only SSH probes · self-hosted data · optional dashboard password |
72
+
73
+ ### Features
74
+
75
+ - **Analytics** — nginx access logs over SSH, SQLite, Streamlit charts, traffic globe
76
+ - **Security** — CPU/RAM/disk/network, port audit, firewall, cyberpunk 3D threat map, consolidated **Security Report**
77
+ - **Scan History** — score timeline, findings trends, snapshot comparison
78
+ - **Auto-scan** — configurable background security scans (default every 60 min)
79
+ - **AI Agent** — OpenRouter (default), DeepSeek, OpenAI, Anthropic, Ollama, LM Studio; sidebar chat with voice input
80
+ - **i18n** — English (default), Russian, Spanish (`locales/`)
81
+
82
+ ### Quick start
83
+
84
+ See [docs/quickstart.md](docs/quickstart.md).
85
+
86
+ ```bash
87
+ cp servers.example.yaml servers.yaml
88
+ cp agent.example.yaml agent.yaml
89
+ export OPENROUTER_API_KEY=sk-or-...
90
+ python skoposctl.py collect
91
+ python skoposctl.py security-scan
92
+ streamlit run dashboard.py
93
+ ```
94
+
95
+ | Page | URL |
96
+ |------|-----|
97
+ | Analytics | `http://localhost:8501` |
98
+ | Security | sidebar → **Security** |
99
+ | Scan History | sidebar → **Scan History** |
100
+ | Settings | sidebar → **Settings** |
101
+
102
+ ### Security
103
+
104
+ - **Security Score** (0–100, grade A–F) in sidebar on every page
105
+ - **Threat alerts** banner when critical/high issues exist
106
+ - **Dashboard password** — set/change/regenerate in the admin panel; only a
107
+ salted PBKDF2 **hash** is stored in the DB (env `SKOPOS_DASHBOARD_PASSWORD`
108
+ still supported for bootstrap). A post-deploy modal prompts you to set one, and
109
+ passwords expire after 90 days with a Telegram reminder. See
110
+ [configuration → Dashboard password & rotation](docs/en/guide/configuration.md)
111
+ - **SKOPOS_SSH_STRICT_HOST_KEYS=1** — verify SSH host keys (recommended)
112
+ - See [docs/audit-findings.md](docs/audit-findings.md)
113
+
114
+ ### Documentation
115
+
116
+ | Doc | Description |
117
+ |-----|-------------|
118
+ | **[In-app Documentation](http://localhost:8501/Documentation)** | Guides with screenshots (also in sidebar / top bar) |
119
+ | [Quick Start](docs/quickstart.md) | 5-minute setup |
120
+ | [User Guide](docs/user-guide.md) | Full UI reference |
121
+ | [Use Cases](docs/use-cases.md) | Common workflows |
122
+ | [Security Module](docs/security.md) | Architecture & extension |
123
+ | [nginx scope](docs/en/guide/nginx.md) | **Analytics are nginx-only** — disclaimer & log format |
124
+ | [CHANGELOG](CHANGELOG.md) | Release notes |
125
+
126
+ > **Analytics scope:** traffic dashboards parse **nginx access logs** by default. **Apache combined** logs are supported when `apache.enabled: true`. Security probes are stack-agnostic.
127
+
128
+ ### Configure servers
129
+
130
+ Edit `servers.yaml` — SSH host, nginx log paths. See `servers.example.yaml`.
131
+
132
+ For per-domain analytics, include `$host` in nginx `log_format`.
133
+
134
+ ### AI agent
135
+
136
+ Default provider: **OpenRouter** via `OPENROUTER_API_KEY`. Edit `agent.yaml` for DeepSeek, OpenAI, Anthropic, Ollama, or LM Studio.
137
+
138
+ For a full prioritized remediation report, open **Security → Security Report**. The sidebar agent supports follow-up chat (including voice) on every page.
139
+
140
+ ### Docker
141
+
142
+ ```bash
143
+ docker compose up -d --build
144
+ ```
145
+
146
+ Open `http://localhost:8501`
147
+
148
+ ### License
149
+
150
+ MIT — see [LICENSE](LICENSE).
151
+
152
+ ### Testing & coverage
153
+
154
+ ```bash
155
+ pip install -r requirements.txt pytest pytest-cov
156
+ bash scripts/ci_coverage_badge.sh -- tests/ -q --cov=skopos
157
+ ```
158
+
159
+ **226** pytest cases · line coverage on `skopos/` tracked in [`docs/badges/coverage.svg`](docs/badges/coverage.svg) (regenerate with `bash scripts/ci_coverage_badge.sh`, verified by [CI](.github/workflows/ci.yml)).
160
+
161
+ ### Screenshots
162
+
163
+ Captured from [skopos.modelmarket.dev](https://skopos.modelmarket.dev/app/) — Analytics page in six themes (sidebar **Theme** picker):
164
+
165
+ <p align="center">
166
+ <img src="docs/screenshots/themes/analytics-light.png" alt="Light theme" width="48%" />
167
+ <img src="docs/screenshots/themes/analytics-premium.png" alt="Premium theme" width="48%" />
168
+ </p>
169
+ <p align="center"><sub>Light · Premium</sub></p>
170
+
171
+ <p align="center">
172
+ <img src="docs/screenshots/themes/analytics-midnight.png" alt="Cosmos theme" width="48%" />
173
+ <img src="docs/screenshots/themes/analytics-ocean.png" alt="Ocean theme" width="48%" />
174
+ </p>
175
+ <p align="center"><sub>Cosmos (midnight) · Ocean</sub></p>
176
+
177
+ <p align="center">
178
+ <img src="docs/screenshots/themes/analytics-aurora.png" alt="Aurora theme" width="48%" />
179
+ <img src="docs/screenshots/themes/analytics-slate.png" alt="Slate theme" width="48%" />
180
+ </p>
181
+ <p align="center"><sub>Aurora · Slate</sub></p>
182
+
183
+ | View | Screenshot |
184
+ |------|------------|
185
+ | Security — 3D Threat Map | ![3D threat map](docs/screenshots/en/security-3d-map.png) |
186
+ | Sidebar & navigation | ![Sidebar](docs/screenshots/en/sidebar-nav.png) |
187
+ | Floating AI agent | ![Agent](docs/screenshots/en/floating-agent.png) |
188
+
189
+ Regenerate hero + theme gallery from production:
190
+
191
+ ```bash
192
+ export SKOPOS_DASHBOARD_PASSWORD='…'
193
+ pip install playwright pillow && playwright install chromium
194
+ python scripts/capture_readme_screenshots.py --base-url https://skopos.modelmarket.dev/app/
195
+ ```
@@ -0,0 +1,167 @@
1
+ # SKOPOS — fleet observability satellite of the AICOM ecosystem
2
+
3
+ <!-- aicom-readme-badges -->
4
+ <p align="center">
5
+ <a href="https://github.com/alexar76/skopos/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/alexar76/skopos/ci.yml?branch=main&label=CI" alt="CI" /></a>
6
+ <a href="https://github.com/alexar76/skopos/actions/workflows/pages.yml"><img src="https://img.shields.io/github/actions/workflow/status/alexar76/skopos/pages.yml?branch=main&label=Pages" alt="Pages deploy" /></a>
7
+ <a href="https://skopos.modelmarket.dev/"><img src="https://img.shields.io/badge/demo-live-brightgreen" alt="Live demo" /></a>
8
+ <a href="https://alexar76.github.io/skopos/"><img src="https://img.shields.io/badge/landing-cosmic-4de8ff" alt="Cosmic landing" /></a>
9
+ <img src="https://img.shields.io/badge/python-%3E%3D3.11-brightgreen.svg" alt="Python >=3.11" />
10
+ <img src="https://img.shields.io/badge/tests-226_passing-brightgreen" alt="226 tests passing" />
11
+ <img src="https://img.shields.io/badge/stack-Streamlit%20%2B%20PostgreSQL-6e40c9" alt="Streamlit + PostgreSQL" />
12
+ <a href="#testing--coverage"><img src="docs/badges/coverage.svg" alt="Test coverage" /></a>
13
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /></a>
14
+ </p>
15
+ <!-- /aicom-readme-badges -->
16
+
17
+
18
+
19
+ > **Self-hosted nginx & Apache analytics and AI security for your server fleet** — GA-like HTTP dashboards, Security Center with 3D threat map, scan history, and an AI agent with voice input. No third-party trackers; data stays on your infrastructure.
20
+
21
+ <p align="center">
22
+ <a href="https://skopos.modelmarket.dev/app/">
23
+ <img src="docs/screenshots/readme/hero-banner.png" alt="SKOPOS — Web Analytics and Security Center with 3D threat map" width="100%" />
24
+ </a>
25
+ </p>
26
+
27
+ <p align="center">
28
+ <strong><a href="https://skopos.modelmarket.dev">Live demo</a></strong>
29
+ ·
30
+ <strong><a href="https://alexar76.github.io/skopos/">Landing</a></strong>
31
+ ·
32
+ nine built-in themes
33
+ </p>
34
+
35
+ In Greek, **skopos** (σκοπός) means *watcher* or *scout*. **SKOPOS** is the fleet observability satellite of the [AICOM / AIMarket ecosystem](https://magic-ai-factory.com): nginx traffic over SSH, security posture across factory / metis / oracle hosts, and an LLM analyst in the sidebar.
36
+
37
+ | | |
38
+ |---|---|
39
+ | **Role** | SSH log collection → SQLite/PostgreSQL analytics → Security Center + AI agent |
40
+ | **Live demo** | [skopos.modelmarket.dev](https://skopos.modelmarket.dev) |
41
+ | **Landing (GitHub Pages)** | [alexar76.github.io/skopos](https://alexar76.github.io/skopos/) |
42
+ | **Monitors** | **nginx** access logs (primary), **Apache** combined, CPU/RAM/disk, ports, fail2ban, port knocks |
43
+ | **Charter** | Read-only SSH probes · self-hosted data · optional dashboard password |
44
+
45
+ ### Features
46
+
47
+ - **Analytics** — nginx access logs over SSH, SQLite, Streamlit charts, traffic globe
48
+ - **Security** — CPU/RAM/disk/network, port audit, firewall, cyberpunk 3D threat map, consolidated **Security Report**
49
+ - **Scan History** — score timeline, findings trends, snapshot comparison
50
+ - **Auto-scan** — configurable background security scans (default every 60 min)
51
+ - **AI Agent** — OpenRouter (default), DeepSeek, OpenAI, Anthropic, Ollama, LM Studio; sidebar chat with voice input
52
+ - **i18n** — English (default), Russian, Spanish (`locales/`)
53
+
54
+ ### Quick start
55
+
56
+ See [docs/quickstart.md](docs/quickstart.md).
57
+
58
+ ```bash
59
+ cp servers.example.yaml servers.yaml
60
+ cp agent.example.yaml agent.yaml
61
+ export OPENROUTER_API_KEY=sk-or-...
62
+ python skoposctl.py collect
63
+ python skoposctl.py security-scan
64
+ streamlit run dashboard.py
65
+ ```
66
+
67
+ | Page | URL |
68
+ |------|-----|
69
+ | Analytics | `http://localhost:8501` |
70
+ | Security | sidebar → **Security** |
71
+ | Scan History | sidebar → **Scan History** |
72
+ | Settings | sidebar → **Settings** |
73
+
74
+ ### Security
75
+
76
+ - **Security Score** (0–100, grade A–F) in sidebar on every page
77
+ - **Threat alerts** banner when critical/high issues exist
78
+ - **Dashboard password** — set/change/regenerate in the admin panel; only a
79
+ salted PBKDF2 **hash** is stored in the DB (env `SKOPOS_DASHBOARD_PASSWORD`
80
+ still supported for bootstrap). A post-deploy modal prompts you to set one, and
81
+ passwords expire after 90 days with a Telegram reminder. See
82
+ [configuration → Dashboard password & rotation](docs/en/guide/configuration.md)
83
+ - **SKOPOS_SSH_STRICT_HOST_KEYS=1** — verify SSH host keys (recommended)
84
+ - See [docs/audit-findings.md](docs/audit-findings.md)
85
+
86
+ ### Documentation
87
+
88
+ | Doc | Description |
89
+ |-----|-------------|
90
+ | **[In-app Documentation](http://localhost:8501/Documentation)** | Guides with screenshots (also in sidebar / top bar) |
91
+ | [Quick Start](docs/quickstart.md) | 5-minute setup |
92
+ | [User Guide](docs/user-guide.md) | Full UI reference |
93
+ | [Use Cases](docs/use-cases.md) | Common workflows |
94
+ | [Security Module](docs/security.md) | Architecture & extension |
95
+ | [nginx scope](docs/en/guide/nginx.md) | **Analytics are nginx-only** — disclaimer & log format |
96
+ | [CHANGELOG](CHANGELOG.md) | Release notes |
97
+
98
+ > **Analytics scope:** traffic dashboards parse **nginx access logs** by default. **Apache combined** logs are supported when `apache.enabled: true`. Security probes are stack-agnostic.
99
+
100
+ ### Configure servers
101
+
102
+ Edit `servers.yaml` — SSH host, nginx log paths. See `servers.example.yaml`.
103
+
104
+ For per-domain analytics, include `$host` in nginx `log_format`.
105
+
106
+ ### AI agent
107
+
108
+ Default provider: **OpenRouter** via `OPENROUTER_API_KEY`. Edit `agent.yaml` for DeepSeek, OpenAI, Anthropic, Ollama, or LM Studio.
109
+
110
+ For a full prioritized remediation report, open **Security → Security Report**. The sidebar agent supports follow-up chat (including voice) on every page.
111
+
112
+ ### Docker
113
+
114
+ ```bash
115
+ docker compose up -d --build
116
+ ```
117
+
118
+ Open `http://localhost:8501`
119
+
120
+ ### License
121
+
122
+ MIT — see [LICENSE](LICENSE).
123
+
124
+ ### Testing & coverage
125
+
126
+ ```bash
127
+ pip install -r requirements.txt pytest pytest-cov
128
+ bash scripts/ci_coverage_badge.sh -- tests/ -q --cov=skopos
129
+ ```
130
+
131
+ **226** pytest cases · line coverage on `skopos/` tracked in [`docs/badges/coverage.svg`](docs/badges/coverage.svg) (regenerate with `bash scripts/ci_coverage_badge.sh`, verified by [CI](.github/workflows/ci.yml)).
132
+
133
+ ### Screenshots
134
+
135
+ Captured from [skopos.modelmarket.dev](https://skopos.modelmarket.dev/app/) — Analytics page in six themes (sidebar **Theme** picker):
136
+
137
+ <p align="center">
138
+ <img src="docs/screenshots/themes/analytics-light.png" alt="Light theme" width="48%" />
139
+ <img src="docs/screenshots/themes/analytics-premium.png" alt="Premium theme" width="48%" />
140
+ </p>
141
+ <p align="center"><sub>Light · Premium</sub></p>
142
+
143
+ <p align="center">
144
+ <img src="docs/screenshots/themes/analytics-midnight.png" alt="Cosmos theme" width="48%" />
145
+ <img src="docs/screenshots/themes/analytics-ocean.png" alt="Ocean theme" width="48%" />
146
+ </p>
147
+ <p align="center"><sub>Cosmos (midnight) · Ocean</sub></p>
148
+
149
+ <p align="center">
150
+ <img src="docs/screenshots/themes/analytics-aurora.png" alt="Aurora theme" width="48%" />
151
+ <img src="docs/screenshots/themes/analytics-slate.png" alt="Slate theme" width="48%" />
152
+ </p>
153
+ <p align="center"><sub>Aurora · Slate</sub></p>
154
+
155
+ | View | Screenshot |
156
+ |------|------------|
157
+ | Security — 3D Threat Map | ![3D threat map](docs/screenshots/en/security-3d-map.png) |
158
+ | Sidebar & navigation | ![Sidebar](docs/screenshots/en/sidebar-nav.png) |
159
+ | Floating AI agent | ![Agent](docs/screenshots/en/floating-agent.png) |
160
+
161
+ Regenerate hero + theme gallery from production:
162
+
163
+ ```bash
164
+ export SKOPOS_DASHBOARD_PASSWORD='…'
165
+ pip install playwright pillow && playwright install chromium
166
+ python scripts/capture_readme_screenshots.py --base-url https://skopos.modelmarket.dev/app/
167
+ ```
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "skopos-fleet"
7
+ version = "0.1.0"
8
+ description = "SKOPOS (Σκοπός) — self-hosted nginx analytics and fleet security for the AICOM ecosystem."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.11"
12
+ authors = [{ name = "AICOM (alexar76)" }]
13
+ keywords = ["nginx", "analytics", "security", "streamlit", "aimarket", "aicom", "fleet", "ssh"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ ]
20
+ dependencies = [
21
+ "streamlit>=1.33",
22
+ "pandas>=2.0",
23
+ "plotly>=5.18",
24
+ "pyyaml>=6.0",
25
+ "paramiko>=3.4",
26
+ "python-dotenv>=1.0",
27
+ "httpx>=0.27",
28
+ "geoip2>=4.8",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = ["pytest>=8.0"]
33
+ postgres = ["psycopg[binary]>=3.2"]
34
+
35
+ [project.scripts]
36
+ skoposctl = "skoposctl:main"
37
+
38
+ [tool.setuptools]
39
+ py-modules = ["skoposctl"]
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["."]
43
+ include = ["skopos*"]
44
+
45
+ [tool.pytest.ini_options]
46
+ testpaths = ["tests"]
47
+ pythonpath = ["."]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,2 @@
1
+ __all__ = ["config", "db", "collector", "nginx", "ssh"]
2
+
@@ -0,0 +1,18 @@
1
+ from .analyst import AnalysisResult, chat_with_agent, chat_with_agent_stream, run_security_analysis
2
+ from .config import AgentConfig, load_agent_config
3
+ from .context import build_agent_context, build_server_context
4
+ from .providers import ChatMessage, LLMProviderError, chat_completion
5
+
6
+ __all__ = [
7
+ "AgentConfig",
8
+ "AnalysisResult",
9
+ "ChatMessage",
10
+ "LLMProviderError",
11
+ "build_agent_context",
12
+ "build_server_context",
13
+ "chat_completion",
14
+ "chat_with_agent",
15
+ "chat_with_agent_stream",
16
+ "load_agent_config",
17
+ "run_security_analysis",
18
+ ]
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from ..config import AppConfig
6
+ from ..db import connect_for_config, init_db
7
+ from .config import AgentConfig, load_agent_config
8
+ from .context import build_server_context
9
+ from .providers import ChatMessage, chat_completion, chat_completion_stream
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class AnalysisResult:
14
+ provider: str
15
+ model: str
16
+ report: str
17
+
18
+
19
+ ANALYSIS_PROMPT = """Perform a comprehensive security audit based on the server context below.
20
+
21
+ Structure your report with:
22
+ 1. Executive summary (risk level: Critical/High/Medium/Low)
23
+ 2. Open ports & exposure analysis
24
+ 3. Resource health (CPU, memory, disk)
25
+ 4. Docker containers — role, ports, CPU/RAM per container, stopped vs running
26
+ 5. Authentication & SSH hardening
27
+ 6. HTTP attack surface from traffic logs
28
+ 7. Prioritized remediation checklist (numbered, actionable)
29
+
30
+ Be specific to the actual data. Reference server names and ports."""
31
+
32
+
33
+ def run_security_analysis(
34
+ app_cfg: AppConfig,
35
+ agent_cfg: AgentConfig,
36
+ *,
37
+ server_name: str | None = None,
38
+ provider_id: str | None = None,
39
+ ) -> AnalysisResult:
40
+ con = connect_for_config(app_cfg)
41
+ init_db(con)
42
+ try:
43
+ context = build_server_context(app_cfg, con, server_name=server_name)
44
+ context = context[: agent_cfg.max_context_chars]
45
+ prov_id = provider_id or agent_cfg.default_provider
46
+ prov = agent_cfg.providers[prov_id]
47
+ messages = [
48
+ ChatMessage(role="system", content=agent_cfg.system_prompt),
49
+ ChatMessage(
50
+ role="user",
51
+ content=f"{ANALYSIS_PROMPT}\n\n---\n\n{context}",
52
+ ),
53
+ ]
54
+ report = chat_completion(agent_cfg, messages, provider_id=prov_id)
55
+ return AnalysisResult(provider=prov_id, model=prov.model, report=report)
56
+ finally:
57
+ con.close()
58
+
59
+
60
+ def chat_with_agent(
61
+ agent_cfg: AgentConfig,
62
+ history: list[ChatMessage],
63
+ user_message: str,
64
+ *,
65
+ context: str,
66
+ provider_id: str | None = None,
67
+ ) -> str:
68
+ messages = [
69
+ ChatMessage(role="system", content=agent_cfg.system_prompt),
70
+ ChatMessage(
71
+ role="user",
72
+ content=f"Server context (for reference):\n\n{context[: agent_cfg.max_context_chars]}",
73
+ ),
74
+ ChatMessage(role="assistant", content="Understood. I have the server context. How can I help?"),
75
+ ]
76
+ messages.extend(history)
77
+ messages.append(ChatMessage(role="user", content=user_message))
78
+ return chat_completion(agent_cfg, messages, provider_id=provider_id)
79
+
80
+
81
+ def chat_with_agent_stream(
82
+ agent_cfg: AgentConfig,
83
+ history: list[ChatMessage],
84
+ user_message: str,
85
+ *,
86
+ context: str,
87
+ provider_id: str | None = None,
88
+ ):
89
+ messages = [
90
+ ChatMessage(role="system", content=agent_cfg.system_prompt),
91
+ ChatMessage(
92
+ role="user",
93
+ content=f"Server context (for reference):\n\n{context[: agent_cfg.max_context_chars]}",
94
+ ),
95
+ ]
96
+ messages.extend(history)
97
+ messages.append(ChatMessage(role="user", content=user_message))
98
+ yield from chat_completion_stream(agent_cfg, messages, provider_id=provider_id)
@@ -0,0 +1,103 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Any, Literal
7
+
8
+ import yaml
9
+
10
+ ProviderKind = Literal["openai_compatible", "anthropic_compatible", "ollama", "lmstudio"]
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class ProviderConfig:
15
+ id: str
16
+ kind: ProviderKind
17
+ base_url: str
18
+ model: str
19
+ api_key: str | None = None
20
+ api_key_env: str | None = None
21
+ extra_headers: dict[str, str] | None = None
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class AgentConfig:
26
+ default_provider: str
27
+ providers: dict[str, ProviderConfig]
28
+ system_prompt: str
29
+ max_context_chars: int = 120_000
30
+ briefing_provider: str | None = None
31
+ briefing_model: str | None = None
32
+
33
+
34
+ def _resolve_api_key(raw: dict[str, Any]) -> str | None:
35
+ if raw.get("api_key"):
36
+ return str(raw["api_key"])
37
+ env_name = raw.get("api_key_env")
38
+ if env_name:
39
+ return os.environ.get(str(env_name))
40
+ return None
41
+
42
+
43
+ def load_agent_config(path: str = "./agent.yaml") -> AgentConfig:
44
+ p = Path(path).expanduser()
45
+ if not p.exists():
46
+ p = Path(path.replace(".yaml", ".example.yaml")).expanduser()
47
+ if not p.exists():
48
+ raise FileNotFoundError(f"Agent config not found: {path}")
49
+
50
+ raw = yaml.safe_load(p.read_text(encoding="utf-8"))
51
+ if not isinstance(raw, dict):
52
+ raise ValueError("agent.yaml must be a mapping")
53
+
54
+ default = str(raw.get("default_provider", "deepseek"))
55
+ providers_raw = raw.get("providers") or {}
56
+ providers: dict[str, ProviderConfig] = {}
57
+ for pid, pr in providers_raw.items():
58
+ if not isinstance(pr, dict):
59
+ continue
60
+ providers[str(pid)] = ProviderConfig(
61
+ id=str(pid),
62
+ kind=str(pr.get("kind", "openai_compatible")), # type: ignore[arg-type]
63
+ base_url=str(pr.get("base_url", "")),
64
+ model=str(pr.get("model", "")),
65
+ api_key=_resolve_api_key(pr),
66
+ api_key_env=str(pr["api_key_env"]) if pr.get("api_key_env") else None,
67
+ extra_headers=pr.get("extra_headers"),
68
+ )
69
+
70
+ return AgentConfig(
71
+ default_provider=default,
72
+ providers=providers,
73
+ system_prompt=str(
74
+ raw.get(
75
+ "system_prompt",
76
+ "You are a senior security analyst for production Linux servers.",
77
+ )
78
+ ),
79
+ max_context_chars=int(raw.get("max_context_chars", 120_000)),
80
+ briefing_provider=str(raw["briefing_provider"]).strip() if raw.get("briefing_provider") else None,
81
+ briefing_model=str(raw["briefing_model"]).strip() if raw.get("briefing_model") else None,
82
+ )
83
+
84
+
85
+ def get_provider(cfg: AgentConfig, provider_id: str | None = None) -> ProviderConfig:
86
+ pid = provider_id or cfg.default_provider
87
+ if pid not in cfg.providers:
88
+ raise KeyError(f"Unknown provider '{pid}'. Available: {', '.join(cfg.providers)}")
89
+ prov = cfg.providers[pid]
90
+ if not prov.api_key and prov.kind in ("openai_compatible", "anthropic_compatible"):
91
+ if prov.api_key_env:
92
+ key = os.environ.get(prov.api_key_env)
93
+ if key:
94
+ return ProviderConfig(
95
+ id=prov.id,
96
+ kind=prov.kind,
97
+ base_url=prov.base_url,
98
+ model=prov.model,
99
+ api_key=key,
100
+ api_key_env=prov.api_key_env,
101
+ extra_headers=prov.extra_headers,
102
+ )
103
+ return prov