ftmon 2.0.0a1__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.
- ftmon-2.0.0a1/.github/workflows/ci.yml +26 -0
- ftmon-2.0.0a1/.github/workflows/release.yml +82 -0
- ftmon-2.0.0a1/.gitignore +14 -0
- ftmon-2.0.0a1/AGENTS.md +44 -0
- ftmon-2.0.0a1/CLAUDE.md +118 -0
- ftmon-2.0.0a1/CONTRIBUTING.md +58 -0
- ftmon-2.0.0a1/DESIGN.md +990 -0
- ftmon-2.0.0a1/LICENSE +21 -0
- ftmon-2.0.0a1/PKG-INFO +190 -0
- ftmon-2.0.0a1/README.md +157 -0
- ftmon-2.0.0a1/SPEC.md +1071 -0
- ftmon-2.0.0a1/design/builtins/disk.toml +126 -0
- ftmon-2.0.0a1/design/builtins/events.toml +38 -0
- ftmon-2.0.0a1/design/builtins/hog.toml +42 -0
- ftmon-2.0.0a1/design/builtins/leak.toml +67 -0
- ftmon-2.0.0a1/design/builtins/load.toml +47 -0
- ftmon-2.0.0a1/design/builtins/net.toml +40 -0
- ftmon-2.0.0a1/design/builtins/self.toml +62 -0
- ftmon-2.0.0a1/design/builtins/service.toml +38 -0
- ftmon-2.0.0a1/docs/definitions.md +371 -0
- ftmon-2.0.0a1/docs/external-checks.md +179 -0
- ftmon-2.0.0a1/docs/install.md +597 -0
- ftmon-2.0.0a1/docs/manual.md +336 -0
- ftmon-2.0.0a1/docs/why-ftmon.md +75 -0
- ftmon-2.0.0a1/extra-monitors/README.md +63 -0
- ftmon-2.0.0a1/extra-monitors/_template/README.md +31 -0
- ftmon-2.0.0a1/extra-monitors/_template/checks.toml.example +4 -0
- ftmon-2.0.0a1/extra-monitors/_template/fixtures/ok.txt +1 -0
- ftmon-2.0.0a1/extra-monitors/_template/monitor.toml +21 -0
- ftmon-2.0.0a1/extra-monitors/_template/recipe.toml +20 -0
- ftmon-2.0.0a1/pyproject.toml +70 -0
- ftmon-2.0.0a1/src/ftmon/__init__.py +3 -0
- ftmon-2.0.0a1/src/ftmon/__main__.py +9 -0
- ftmon-2.0.0a1/src/ftmon/checks/__init__.py +7 -0
- ftmon-2.0.0a1/src/ftmon/checks/jsoncheck.py +55 -0
- ftmon-2.0.0a1/src/ftmon/checks/model.py +36 -0
- ftmon-2.0.0a1/src/ftmon/checks/nagios.py +98 -0
- ftmon-2.0.0a1/src/ftmon/checks/registry.py +150 -0
- ftmon-2.0.0a1/src/ftmon/checks/runner.py +121 -0
- ftmon-2.0.0a1/src/ftmon/checks/sampler.py +129 -0
- ftmon-2.0.0a1/src/ftmon/checks/text.py +10 -0
- ftmon-2.0.0a1/src/ftmon/cli.py +837 -0
- ftmon-2.0.0a1/src/ftmon/clock.py +130 -0
- ftmon-2.0.0a1/src/ftmon/config.py +324 -0
- ftmon-2.0.0a1/src/ftmon/daemon.py +654 -0
- ftmon-2.0.0a1/src/ftmon/definitions/__init__.py +31 -0
- ftmon-2.0.0a1/src/ftmon/definitions/builtins/disk.toml +126 -0
- ftmon-2.0.0a1/src/ftmon/definitions/builtins/events.toml +38 -0
- ftmon-2.0.0a1/src/ftmon/definitions/builtins/hog.toml +42 -0
- ftmon-2.0.0a1/src/ftmon/definitions/builtins/leak.toml +67 -0
- ftmon-2.0.0a1/src/ftmon/definitions/builtins/load.toml +47 -0
- ftmon-2.0.0a1/src/ftmon/definitions/builtins/net.toml +40 -0
- ftmon-2.0.0a1/src/ftmon/definitions/builtins/self.toml +62 -0
- ftmon-2.0.0a1/src/ftmon/definitions/builtins/service.toml +38 -0
- ftmon-2.0.0a1/src/ftmon/definitions/loader.py +1147 -0
- ftmon-2.0.0a1/src/ftmon/definitions/manage.py +162 -0
- ftmon-2.0.0a1/src/ftmon/definitions/schema.py +160 -0
- ftmon-2.0.0a1/src/ftmon/demo.py +191 -0
- ftmon-2.0.0a1/src/ftmon/deploy/Caddyfile.demo +53 -0
- ftmon-2.0.0a1/src/ftmon/engine/__init__.py +0 -0
- ftmon-2.0.0a1/src/ftmon/engine/actions.py +127 -0
- ftmon-2.0.0a1/src/ftmon/engine/context.py +52 -0
- ftmon-2.0.0a1/src/ftmon/engine/effects.py +107 -0
- ftmon-2.0.0a1/src/ftmon/engine/episodes.py +239 -0
- ftmon-2.0.0a1/src/ftmon/engine/events.py +364 -0
- ftmon-2.0.0a1/src/ftmon/engine/incidents.py +311 -0
- ftmon-2.0.0a1/src/ftmon/engine/pipeline.py +249 -0
- ftmon-2.0.0a1/src/ftmon/engine/render.py +39 -0
- ftmon-2.0.0a1/src/ftmon/engine/rings.py +109 -0
- ftmon-2.0.0a1/src/ftmon/engine/scheduler.py +77 -0
- ftmon-2.0.0a1/src/ftmon/expr/__init__.py +38 -0
- ftmon-2.0.0a1/src/ftmon/expr/eval.py +253 -0
- ftmon-2.0.0a1/src/ftmon/expr/functions.py +133 -0
- ftmon-2.0.0a1/src/ftmon/expr/ir.py +83 -0
- ftmon-2.0.0a1/src/ftmon/expr/parse.py +255 -0
- ftmon-2.0.0a1/src/ftmon/expr/tribool.py +41 -0
- ftmon-2.0.0a1/src/ftmon/mcp_server.py +588 -0
- ftmon-2.0.0a1/src/ftmon/model.py +160 -0
- ftmon-2.0.0a1/src/ftmon/notify/__init__.py +19 -0
- ftmon-2.0.0a1/src/ftmon/notify/base.py +55 -0
- ftmon-2.0.0a1/src/ftmon/notify/desktop.py +51 -0
- ftmon-2.0.0a1/src/ftmon/notify/file.py +44 -0
- ftmon-2.0.0a1/src/ftmon/notify/http.py +68 -0
- ftmon-2.0.0a1/src/ftmon/notify/ntfy.py +64 -0
- ftmon-2.0.0a1/src/ftmon/notify/smtp.py +87 -0
- ftmon-2.0.0a1/src/ftmon/notify/webhook.py +51 -0
- ftmon-2.0.0a1/src/ftmon/paths.py +98 -0
- ftmon-2.0.0a1/src/ftmon/scenarios/__init__.py +1 -0
- ftmon-2.0.0a1/src/ftmon/scenarios/demo-v1.jsonl +16 -0
- ftmon-2.0.0a1/src/ftmon/selfmon.py +83 -0
- ftmon-2.0.0a1/src/ftmon/sources/__init__.py +0 -0
- ftmon-2.0.0a1/src/ftmon/sources/base.py +198 -0
- ftmon-2.0.0a1/src/ftmon/sources/disk.py +94 -0
- ftmon-2.0.0a1/src/ftmon/sources/fixtures.py +363 -0
- ftmon-2.0.0a1/src/ftmon/sources/journald.py +155 -0
- ftmon-2.0.0a1/src/ftmon/sources/net.py +84 -0
- ftmon-2.0.0a1/src/ftmon/sources/process.py +124 -0
- ftmon-2.0.0a1/src/ftmon/sources/system.py +121 -0
- ftmon-2.0.0a1/src/ftmon/sources/unit.py +144 -0
- ftmon-2.0.0a1/src/ftmon/store/__init__.py +8 -0
- ftmon-2.0.0a1/src/ftmon/store/db.py +70 -0
- ftmon-2.0.0a1/src/ftmon/store/doctor.py +67 -0
- ftmon-2.0.0a1/src/ftmon/store/migrations/0001_init.sql +51 -0
- ftmon-2.0.0a1/src/ftmon/store/migrations/0002_action_runs.sql +6 -0
- ftmon-2.0.0a1/src/ftmon/store/migrations/0003_notification_deliveries.sql +44 -0
- ftmon-2.0.0a1/src/ftmon/store/outbox.py +350 -0
- ftmon-2.0.0a1/src/ftmon/store/query.py +500 -0
- ftmon-2.0.0a1/src/ftmon/store/retention.py +337 -0
- ftmon-2.0.0a1/src/ftmon/store/writer.py +435 -0
- ftmon-2.0.0a1/src/ftmon/systemd/ftmon-demo-build.service +27 -0
- ftmon-2.0.0a1/src/ftmon/systemd/ftmon-demo-refresh.service +17 -0
- ftmon-2.0.0a1/src/ftmon/systemd/ftmon-demo-refresh.timer +15 -0
- ftmon-2.0.0a1/src/ftmon/systemd/ftmon-demo-web.service +38 -0
- ftmon-2.0.0a1/src/ftmon/systemd/ftmon-server.service +54 -0
- ftmon-2.0.0a1/src/ftmon/systemd/ftmon.service +18 -0
- ftmon-2.0.0a1/src/ftmon/web/__init__.py +6 -0
- ftmon-2.0.0a1/src/ftmon/web/app.py +756 -0
- ftmon-2.0.0a1/src/ftmon/web/demo_app.py +161 -0
- ftmon-2.0.0a1/src/ftmon/web/static/brand/README.md +16 -0
- ftmon-2.0.0a1/src/ftmon/web/static/brand/apple-touch-icon.png +0 -0
- ftmon-2.0.0a1/src/ftmon/web/static/brand/favicon-64.png +0 -0
- ftmon-2.0.0a1/src/ftmon/web/static/brand/favicon.ico +0 -0
- ftmon-2.0.0a1/src/ftmon/web/static/brand/ftmon-mark.png +0 -0
- ftmon-2.0.0a1/src/ftmon/web/static/ftmon.css +1 -0
- ftmon-2.0.0a1/src/ftmon/web/static/ftmon.js +34 -0
- ftmon-2.0.0a1/src/ftmon/web/static/vendor/README.md +10 -0
- ftmon-2.0.0a1/src/ftmon/web/static/vendor/uPlot.LICENSE.txt +21 -0
- ftmon-2.0.0a1/src/ftmon/web/static/vendor/uPlot.iife.min.js +2 -0
- ftmon-2.0.0a1/src/ftmon/web/static/vendor/uPlot.min.css +1 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/base.html +8 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/dashboard.html +5 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/events.html +1 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/incident.html +4 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/incident_rows.html +2 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/incidents.html +1 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/metrics.html +2 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/monitors.html +2 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/self.html +1 -0
- ftmon-2.0.0a1/src/ftmon/web/templates/trends.html +8 -0
- ftmon-2.0.0a1/tests/__init__.py +0 -0
- ftmon-2.0.0a1/tests/conftest.py +45 -0
- ftmon-2.0.0a1/tests/e2e/__init__.py +0 -0
- ftmon-2.0.0a1/tests/e2e/harness.py +149 -0
- ftmon-2.0.0a1/tests/e2e/test_daemon_e2e.py +297 -0
- ftmon-2.0.0a1/tests/e2e/test_demo_builder.py +28 -0
- ftmon-2.0.0a1/tests/e2e_real/__init__.py +1 -0
- ftmon-2.0.0a1/tests/e2e_real/test_real_system.py +144 -0
- ftmon-2.0.0a1/tests/e2e_real/test_server_deployment.py +116 -0
- ftmon-2.0.0a1/tests/extra_monitors/test_recipes.py +129 -0
- ftmon-2.0.0a1/tests/reqindex.json +189 -0
- ftmon-2.0.0a1/tests/traceability_pending.json +31 -0
- ftmon-2.0.0a1/tests/unit/__init__.py +0 -0
- ftmon-2.0.0a1/tests/unit/test_actions.py +118 -0
- ftmon-2.0.0a1/tests/unit/test_check_registry.py +122 -0
- ftmon-2.0.0a1/tests/unit/test_cli.py +384 -0
- ftmon-2.0.0a1/tests/unit/test_config_secrets.py +101 -0
- ftmon-2.0.0a1/tests/unit/test_core.py +101 -0
- ftmon-2.0.0a1/tests/unit/test_definitions.py +590 -0
- ftmon-2.0.0a1/tests/unit/test_demo.py +67 -0
- ftmon-2.0.0a1/tests/unit/test_doctor.py +70 -0
- ftmon-2.0.0a1/tests/unit/test_engine.py +339 -0
- ftmon-2.0.0a1/tests/unit/test_episodes.py +163 -0
- ftmon-2.0.0a1/tests/unit/test_events_engine.py +221 -0
- ftmon-2.0.0a1/tests/unit/test_expr_eval.py +140 -0
- ftmon-2.0.0a1/tests/unit/test_expr_parse.py +120 -0
- ftmon-2.0.0a1/tests/unit/test_external_checks.py +130 -0
- ftmon-2.0.0a1/tests/unit/test_external_integration.py +120 -0
- ftmon-2.0.0a1/tests/unit/test_external_sampler.py +155 -0
- ftmon-2.0.0a1/tests/unit/test_fixtures.py +114 -0
- ftmon-2.0.0a1/tests/unit/test_incidents.py +264 -0
- ftmon-2.0.0a1/tests/unit/test_m2_integration.py +191 -0
- ftmon-2.0.0a1/tests/unit/test_mcp.py +432 -0
- ftmon-2.0.0a1/tests/unit/test_notification_dispatch.py +247 -0
- ftmon-2.0.0a1/tests/unit/test_notify_remote.py +247 -0
- ftmon-2.0.0a1/tests/unit/test_packaging.py +130 -0
- ftmon-2.0.0a1/tests/unit/test_quiet.py +122 -0
- ftmon-2.0.0a1/tests/unit/test_retention.py +256 -0
- ftmon-2.0.0a1/tests/unit/test_samplers.py +596 -0
- ftmon-2.0.0a1/tests/unit/test_samplers_m3.py +111 -0
- ftmon-2.0.0a1/tests/unit/test_scenarios_m4.py +176 -0
- ftmon-2.0.0a1/tests/unit/test_store.py +418 -0
- ftmon-2.0.0a1/tests/unit/test_traceability.py +171 -0
- ftmon-2.0.0a1/tests/unit/test_trends.py +148 -0
- ftmon-2.0.0a1/tests/unit/test_tribool.py +88 -0
- ftmon-2.0.0a1/tests/unit/test_web.py +389 -0
- ftmon-2.0.0a1/tests/unit/test_web_demo.py +200 -0
- ftmon-2.0.0a1/tools/gen_reqindex.py +161 -0
- ftmon-2.0.0a1/uv.lock +1052 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# The same gate contributors run locally (README "Development" section).
|
|
2
|
+
# Deterministic by design: realsystem tests are deselected by pytest defaults.
|
|
3
|
+
name: CI
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.11", "3.13"]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: astral-sh/setup-uv@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
- run: uv sync --locked
|
|
25
|
+
- run: uv run ruff check src tests
|
|
26
|
+
- run: uv run pytest -q
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Tag-driven release: v* tags run the full gate, build sdist+wheel, publish
|
|
2
|
+
# to PyPI via Trusted Publishing (OIDC - no stored API tokens), then attach
|
|
3
|
+
# the artifacts to a GitHub Release. The PyPI side requires a (pending)
|
|
4
|
+
# trusted publisher registered for project "ftmon" with this repository,
|
|
5
|
+
# workflow file "release.yml", and environment "pypi".
|
|
6
|
+
name: Release
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: astral-sh/setup-uv@v5
|
|
21
|
+
- run: uv sync --locked
|
|
22
|
+
- run: uv run ruff check src tests
|
|
23
|
+
- run: uv run pytest -q
|
|
24
|
+
|
|
25
|
+
build:
|
|
26
|
+
needs: test
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: astral-sh/setup-uv@v5
|
|
31
|
+
- run: uv build
|
|
32
|
+
# The tag must match the built version so PyPI, the GitHub Release,
|
|
33
|
+
# and `ftmon --version` can never disagree.
|
|
34
|
+
- name: Check tag matches package version
|
|
35
|
+
run: |
|
|
36
|
+
version=$(uv run python -c "import ftmon; print(ftmon.__version__)")
|
|
37
|
+
test "v${version}" = "${GITHUB_REF_NAME}" || {
|
|
38
|
+
echo "tag ${GITHUB_REF_NAME} != package version v${version}" >&2
|
|
39
|
+
exit 1
|
|
40
|
+
}
|
|
41
|
+
- uses: actions/upload-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: dist
|
|
44
|
+
path: dist/
|
|
45
|
+
|
|
46
|
+
publish-pypi:
|
|
47
|
+
needs: build
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
environment:
|
|
50
|
+
name: pypi
|
|
51
|
+
url: https://pypi.org/p/ftmon
|
|
52
|
+
permissions:
|
|
53
|
+
id-token: write # OIDC token for PyPI Trusted Publishing
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/download-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: dist
|
|
58
|
+
path: dist/
|
|
59
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
60
|
+
|
|
61
|
+
github-release:
|
|
62
|
+
needs: publish-pypi
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
permissions:
|
|
65
|
+
contents: write
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/download-artifact@v4
|
|
68
|
+
with:
|
|
69
|
+
name: dist
|
|
70
|
+
path: dist/
|
|
71
|
+
- name: Create GitHub Release
|
|
72
|
+
env:
|
|
73
|
+
GH_TOKEN: ${{ github.token }}
|
|
74
|
+
run: |
|
|
75
|
+
prerelease=""
|
|
76
|
+
case "${GITHUB_REF_NAME}" in
|
|
77
|
+
*a*|*b*|*rc*) prerelease="--prerelease" ;;
|
|
78
|
+
esac
|
|
79
|
+
gh release create "${GITHUB_REF_NAME}" dist/* \
|
|
80
|
+
--repo "${GITHUB_REPOSITORY}" \
|
|
81
|
+
--title "${GITHUB_REF_NAME}" \
|
|
82
|
+
--generate-notes ${prerelease}
|
ftmon-2.0.0a1/.gitignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
.venv/
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
dist/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.coverage
|
|
9
|
+
# Local AI-agent session state (tool permission grants), not project content.
|
|
10
|
+
.claude/
|
|
11
|
+
# The original GPLv2 Perl project is published on SourceForge. Keep any local
|
|
12
|
+
# reference checkout out of this MIT-licensed v2 repository.
|
|
13
|
+
ftmon-legacy/
|
|
14
|
+
ftmon_legacy/
|
ftmon-2.0.0a1/AGENTS.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
> **Note to AI Agents:** The comprehensive, up-to-date instructions, architecture, and workflow for this repository are maintained in [CLAUDE.md](CLAUDE.md). Please read `CLAUDE.md` before proceeding with any code changes.
|
|
4
|
+
|
|
5
|
+
## Project Structure & Module Organization
|
|
6
|
+
|
|
7
|
+
This repository contains FTMON v2. The original Perl implementation remains
|
|
8
|
+
separately published at <https://sourceforge.net/projects/ftmon/> and must not
|
|
9
|
+
be added to this repository.
|
|
10
|
+
|
|
11
|
+
- `SPEC.md` is the authoritative product and architecture specification for new work. Requirements have stable IDs and tests should reference them.
|
|
12
|
+
- `CLAUDE.md` summarizes the v2 architecture, commands, and the spec/traceability workflow for AI coding agents.
|
|
13
|
+
- `README.md` gives users and contributors the repository entry point.
|
|
14
|
+
- `docs/` contains the install guide, user manual, and definition reference.
|
|
15
|
+
|
|
16
|
+
New v2 code should follow `SPEC.md`: Python package code in `ftmon/`, tests in `tests/`, and user-editable monitor definitions as TOML.
|
|
17
|
+
|
|
18
|
+
## Build, Test, and Development Commands
|
|
19
|
+
|
|
20
|
+
Use the checked-in `uv` environment for development and validation:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
uv sync
|
|
24
|
+
uv run ruff check src tests
|
|
25
|
+
uv run pytest -q
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Coding Style & Naming Conventions
|
|
29
|
+
|
|
30
|
+
For v2, follow `SPEC.md`: Python 3.11+, `uv`, `ruff`, `pytest`, SQLite, and TOML monitor definitions. Keep platform-specific logic behind the specified adapters.
|
|
31
|
+
|
|
32
|
+
## Testing Guidelines
|
|
33
|
+
|
|
34
|
+
Add pytest coverage for all v2 behavior. Name tests by behavior, and include requirement IDs from `SPEC.md` where relevant, for example `test_daemon_rejects_second_instance_pm_02`. Prefer fixture-driven deterministic tests; keep real-system smoke tests opt-in.
|
|
35
|
+
|
|
36
|
+
## Commit & Pull Request Guidelines
|
|
37
|
+
|
|
38
|
+
This repository has no existing commit history, so no local convention is established. Use concise, imperative commit subjects such as `Add daemon lock handling` or `Document legacy config validation`.
|
|
39
|
+
|
|
40
|
+
Pull requests should describe the change, reference affected `SPEC.md` requirement IDs, and list validation commands run. Include screenshots only for web UI changes.
|
|
41
|
+
|
|
42
|
+
## Security & Configuration Tips
|
|
43
|
+
|
|
44
|
+
Monitor definitions must remain declarative TOML with restricted expression evaluation, as specified in `SPEC.md`.
|
ftmon-2.0.0a1/CLAUDE.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
FTMON v2 is a lightweight, local-first, single-host systems monitor for Linux
|
|
8
|
+
(Python ≥ 3.11, MIT). It detects memory leaks, CPU hogs, disks filling, service
|
|
9
|
+
failures, and notable journal events; keeps bounded metric history in SQLite;
|
|
10
|
+
and exposes a CLI, a loopback-only web dashboard, and a local stdio MCP server.
|
|
11
|
+
|
|
12
|
+
It succeeds a GPL-licensed Perl monitoring engine from 2001–2003, porting its
|
|
13
|
+
design ideas (delta/monotonic calcs, consecutive-cycle confirmation,
|
|
14
|
+
baselining, escalation) but none of its code. The original source is
|
|
15
|
+
intentionally kept out of this repository (a local reference checkout may sit
|
|
16
|
+
in the gitignored `ftmon-legacy/`) and remains available at
|
|
17
|
+
<https://sourceforge.net/projects/ftmon/>. Do not vendor it here — the MIT/GPL
|
|
18
|
+
boundary is deliberate.
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
Everything runs through `uv`:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
uv sync # install/refresh the environment
|
|
26
|
+
uv run ruff check src tests # lint (ruff is also the formatter; line length 100)
|
|
27
|
+
uv run pytest -q # full gate: unit + e2e + traceability (~20 s)
|
|
28
|
+
uv run pytest tests/unit/test_expr_eval.py -q # one file
|
|
29
|
+
uv run ftmon init --profile desktop|server # write config + builtin definitions
|
|
30
|
+
uv run ftmon check # one-shot sample/evaluate
|
|
31
|
+
uv run ftmon daemon # the monitor loop
|
|
32
|
+
uv run ftmon web # dashboard on http://127.0.0.1:8420/ (loopback only)
|
|
33
|
+
uv run ftmon doctor # DB/config diagnostics
|
|
34
|
+
python3 tools/gen_reqindex.py --check # regenerate/verify tests/reqindex.json from SPEC.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Real-system smoke tests are opt-in (deselected by default); the CI suite is
|
|
38
|
+
deterministic and fixture-driven.
|
|
39
|
+
|
|
40
|
+
## Spec-driven workflow (the central process fact)
|
|
41
|
+
|
|
42
|
+
`SPEC.md` is the authoritative product spec; `DESIGN.md` is the companion
|
|
43
|
+
design (elements marked FROZEN must not be changed without amending the doc
|
|
44
|
+
first). Every requirement has a stable ID like `SA-06`, `IN-03`, `EC-02`.
|
|
45
|
+
|
|
46
|
+
Traceability is machine-enforced by `tests/unit/test_traceability.py` (TS-01):
|
|
47
|
+
|
|
48
|
+
- Tests cite the requirement IDs they verify in **bracketed docstrings**:
|
|
49
|
+
`"""[EX-06] ..."""`.
|
|
50
|
+
- `tests/reqindex.json` is generated from SPEC.md by `tools/gen_reqindex.py`
|
|
51
|
+
and must match on regeneration; `tests/traceability_pending.json` lists
|
|
52
|
+
testable IDs not yet covered (a ratchet — an ID can't be both covered and
|
|
53
|
+
pending).
|
|
54
|
+
- Adding/changing a requirement ⇒ regenerate the index. Covering a pending
|
|
55
|
+
ID ⇒ remove it from the pending list. `NG-*`/`DO-*` IDs are exempt.
|
|
56
|
+
|
|
57
|
+
When you land a user-visible change, updating the matching docs
|
|
58
|
+
(`docs/manual.md`, `docs/install.md`, `docs/definitions.md`) is part of the
|
|
59
|
+
work package, not a follow-up. See `CONTRIBUTING.md` — its prime rule:
|
|
60
|
+
comments/docstrings record **why** (constraint, trade-off, spec ID), never
|
|
61
|
+
narrate mechanics. Every module docstring cites the spec IDs that shaped it.
|
|
62
|
+
|
|
63
|
+
## Architecture
|
|
64
|
+
|
|
65
|
+
Package layout (full annotated tree in DESIGN.md §1):
|
|
66
|
+
|
|
67
|
+
- `src/ftmon/expr/` — restricted expression language (parse → IR → eval).
|
|
68
|
+
**Stdlib-only, imports nothing from `ftmon.*`** (EX-04); `eval` never raises
|
|
69
|
+
(EX-06) — errors become `None` plus a counter tick. Three-valued logic in
|
|
70
|
+
`tribool.py`.
|
|
71
|
+
- `src/ftmon/definitions/` — TOML monitor definitions: `schema.py` validator,
|
|
72
|
+
`loader.py` (TOML → `MonitorDef`, topo-sorted derived metrics), and
|
|
73
|
+
`builtins/*.toml` package data. **Definitions are data, never code.** The
|
|
74
|
+
normative copies live in `design/builtins/*.toml` and are mirrored into
|
|
75
|
+
`src/ftmon/definitions/builtins/`; keep the two trees identical.
|
|
76
|
+
- `src/ftmon/sources/` — `Sampler`/`EventSource` implementations (process,
|
|
77
|
+
disk, system, net, unit, journald) plus deterministic `fixtures.py` fakes
|
|
78
|
+
that ship in the prod package and keep the platform seams honest (PL-04).
|
|
79
|
+
- `src/ftmon/checks/` — administrator-registered external checks: `registry.py`
|
|
80
|
+
(argv authority), `runner.py` (no shell, scrubbed env, process-group kill,
|
|
81
|
+
bounded output), Nagios/FTMON-JSON adapters. AI/definitions may reference a
|
|
82
|
+
check alias but can never create one (EC-01).
|
|
83
|
+
- `src/ftmon/engine/` — scheduler tick loop, per-monitor `pipeline.py`
|
|
84
|
+
(snapshot → rings → derived → rules), ring buffers, `incidents.py` (pure
|
|
85
|
+
state machine, FROZEN), episodes, effects/actions.
|
|
86
|
+
- `src/ftmon/store/` — SQLite (WAL, incremental autovacuum): migrations gated
|
|
87
|
+
by `PRAGMA user_version`, daemon-side batched `writer.py`, shared `query.py`,
|
|
88
|
+
retention/rollups, durable notification `outbox.py`.
|
|
89
|
+
- `src/ftmon/notify/` — desktop/file/ntfy/webhook/SMTP adapters with
|
|
90
|
+
independent durable retry.
|
|
91
|
+
- `daemon.py` (composition root; owns the only bulk-write connection),
|
|
92
|
+
`cli.py`, `mcp_server.py`, `web/` (operational app + isolated synthetic demo
|
|
93
|
+
app), `demo.py`, `selfmon.py`, `systemd/` units.
|
|
94
|
+
|
|
95
|
+
Key invariants:
|
|
96
|
+
|
|
97
|
+
- **No direct time access** — `time.time`/`datetime.now`/`time.sleep` only in
|
|
98
|
+
`clock.py` (TS-03); everything else takes an injected `Clock`.
|
|
99
|
+
- Platform-specific behavior lives behind exactly four seams: samplers, event
|
|
100
|
+
sources, notification adapter, paths/service wrapper (PL-01). No platform
|
|
101
|
+
conditionals elsewhere.
|
|
102
|
+
- The web UI binds to 127.0.0.1 only; no auth exists by design (NG-05) — never
|
|
103
|
+
add a non-loopback listener.
|
|
104
|
+
- Core model types are frozen dataclasses; the pipeline is pure-ish data flow
|
|
105
|
+
so incidents can be tested independently.
|
|
106
|
+
- Respect SPEC §1.1 non-goals (fleet monitoring, plugin loading, etc.) — they
|
|
107
|
+
are enforced scope, not TODOs.
|
|
108
|
+
|
|
109
|
+
## Conventions
|
|
110
|
+
|
|
111
|
+
- Lint rules are enforced as tests; `uv run pytest -q` is the gate for
|
|
112
|
+
everything.
|
|
113
|
+
- Name tests by behavior and requirement, e.g.
|
|
114
|
+
`test_daemon_rejects_second_instance_pm_02`.
|
|
115
|
+
- Commit subjects are concise and imperative, often milestone-prefixed
|
|
116
|
+
(`M9: add bounded external checks`, `Docs: ...`).
|
|
117
|
+
- `dist/`, `.venv/`, caches, and `ftmon-legacy/` are gitignored; don't commit
|
|
118
|
+
build artifacts or the legacy tree.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Contributing / Documentation Standard
|
|
2
|
+
|
|
3
|
+
This project is built spec-first and implemented partly by AI models working
|
|
4
|
+
from frozen contracts. Documentation is how rationale survives that process.
|
|
5
|
+
These rules are binding for all code, human- or model-written.
|
|
6
|
+
|
|
7
|
+
## The prime rule: document the WHY
|
|
8
|
+
|
|
9
|
+
Code shows *what* it does; comments and docstrings exist to record what the
|
|
10
|
+
code cannot show — **why it is this way**: the constraint, the trade-off, the
|
|
11
|
+
rejected alternative, the spec requirement. A comment that restates the next
|
|
12
|
+
line is noise and will be removed in review.
|
|
13
|
+
|
|
14
|
+
Bad: `# loop over the entities`
|
|
15
|
+
Good: `# deadline is checked between entities, not inside psutil calls -
|
|
16
|
+
a stuck native call cannot be interrupted in-process (SA-02)`
|
|
17
|
+
|
|
18
|
+
## Docstrings
|
|
19
|
+
|
|
20
|
+
- **Every module** starts with a docstring: one line of purpose, then the
|
|
21
|
+
rationale/constraints that shaped it, citing SPEC requirement IDs in
|
|
22
|
+
parentheses — e.g. `(EX-04)`, `(PM-06)`. The IDs are load-bearing: they let
|
|
23
|
+
a reader jump from code to the reasoning in SPEC.md/DESIGN.md.
|
|
24
|
+
- **Every public class/function** gets a docstring stating behavior at the
|
|
25
|
+
contract level (inputs, outputs, error behavior, None semantics). Private
|
|
26
|
+
helpers need one only when their reason for existing is non-obvious.
|
|
27
|
+
- **Every test** carries the requirement ID(s) it verifies in its docstring,
|
|
28
|
+
bracketed: `"""[EX-06] Three-valued semantics..."""` — the traceability
|
|
29
|
+
tooling (TS-01) depends on this.
|
|
30
|
+
|
|
31
|
+
## Comments
|
|
32
|
+
|
|
33
|
+
- Explain invariants the type system cannot express ("oldest first",
|
|
34
|
+
"one transaction per tick, PM-03").
|
|
35
|
+
- Explain deliberate omissions ("per-process attribution deferred, NG-06").
|
|
36
|
+
- Never narrate mechanics, never leave commented-out code, never write
|
|
37
|
+
comments addressed to a reviewer about the change itself.
|
|
38
|
+
|
|
39
|
+
## Documentation deliverables (SPEC section 17)
|
|
40
|
+
|
|
41
|
+
| Doc | Audience | Grows |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `docs/manual.md` (DO-04) | end users - install, concepts, daily use, tuning | one chapter per milestone; placeholders are marked |
|
|
44
|
+
| `docs/definitions.md` (DO-01) | monitor authors, human or AI (also the MCP resource) | frozen with the language |
|
|
45
|
+
| `docs/install.md` (DO-02) | operators | M6 |
|
|
46
|
+
| `--help` text (DO-03) | CLI users | with each subcommand |
|
|
47
|
+
| SPEC.md / DESIGN.md | maintainers | change-controlled, changelog required |
|
|
48
|
+
|
|
49
|
+
When you land a user-visible feature, updating the manual chapter is part of
|
|
50
|
+
the work package, not a follow-up.
|
|
51
|
+
|
|
52
|
+
## Style
|
|
53
|
+
|
|
54
|
+
Python >= 3.11, ruff (line length 100) is the linter and formatter authority.
|
|
55
|
+
No direct `time.time`/`datetime.now`/`time.sleep` outside `clock.py` (TS-03).
|
|
56
|
+
The `expr/` package imports stdlib only (EX-04). Layering rules: DESIGN.md
|
|
57
|
+
section 1. All lint rules are enforced as tests - `uv run pytest tests -q`
|
|
58
|
+
is the gate.
|