Metadata-Version: 2.4
Name: fixplesk
Version: 0.6.1
Summary: Tenant-scoped Plesk and WordPress diagnostics, declarative playbooks, and guarded remediation
Author: fixplesk contributors
Author-email: Tom Sapletta <tom@sapletta.com>
License-Expression: Apache-2.0
Keywords: plesk,wordpress,diagnostics,wp-cli,openrouter,litellm,deepseek
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic<3,>=2.7
Requires-Dist: httpx<1,>=0.27
Requires-Dist: PyYAML<7,>=6.0.1
Requires-Dist: packaging<27,>=24
Requires-Dist: defusedxml<1,>=0.7.1
Provides-Extra: llm
Requires-Dist: litellm<2,>=1.70; extra == "llm"
Provides-Extra: graphs
Requires-Dist: networkx<4,>=3.2; extra == "graphs"
Provides-Extra: analysis
Requires-Dist: scikit-learn<2,>=1.4; extra == "analysis"
Provides-Extra: solver
Requires-Dist: z3-solver<5,>=4.13; extra == "solver"
Provides-Extra: dev
Requires-Dist: pytest<10,>=8; extra == "dev"
Requires-Dist: pytest-cov<8,>=5; extra == "dev"
Requires-Dist: build<2,>=1.2; extra == "dev"
Requires-Dist: ruff<1,>=0.11; extra == "dev"
Requires-Dist: setuptools>=77; extra == "dev"
Requires-Dist: goal>=2.1.0; python_version >= "3.12" and extra == "dev"
Requires-Dist: costs>=0.1.53; python_version >= "3.9" and extra == "dev"
Dynamic: license-file

<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img"><title>ticket-146: feat(dashboard): add --web flag and SSE live log streaming (ticket-146)</title><rect width="128" height="128" rx="12" fill="#f8fafc"/><g stroke-width="1"><polygon points="64,55 72,61 69,71 59,71 56,61" fill="none" stroke="#d7dde5"/><polygon points="64,47 80,59 74,78 54,78 48,59" fill="none" stroke="#d7dde5"/><polygon points="64,38 89,56 79,85 49,85 39,56" fill="none" stroke="#d7dde5"/><polygon points="64,30 97,53 84,92 44,92 31,53" fill="none" stroke="#d7dde5"/><polygon points="64,21 105,51 89,99 39,99 23,51" fill="none" stroke="#d7dde5"/><line x1="64" y1="64" x2="64" y2="21" stroke="#aab4c0"/><line x1="64" y1="64" x2="105" y2="51" stroke="#aab4c0"/><line x1="64" y1="64" x2="89" y2="99" stroke="#aab4c0"/><line x1="64" y1="64" x2="39" y2="99" stroke="#aab4c0"/><line x1="64" y1="64" x2="23" y2="51" stroke="#aab4c0"/></g><polygon points="64,21 105,51 79,85 54,78 39,56" fill="#fb923c" fill-opacity="0.45" stroke="#c2410c" stroke-width="2"/><circle cx="64" cy="64" r="3" fill="#c2410c"/><g font-family="sans-serif" font-size="7" fill="#334155"><text x="64" y="11" text-anchor="middle">SCO</text><text x="114" y="48" text-anchor="middle">COU</text><text x="95" y="107" text-anchor="middle">UNC</text><text x="33" y="107" text-anchor="middle">VAL</text><text x="14" y="48" text-anchor="middle">DEL</text></g><text x="64" y="124" text-anchor="middle" font-family="sans-serif" font-size="8" fill="#0f172a">L · 129m</text></svg>
# fixplesk 0.6.0


## AI Cost Tracking

![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.6.1-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
![AI Cost](https://img.shields.io/badge/AI%20Cost-$1.67-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-5.8h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)

- 🤖 **LLM usage:** $1.6723 (10 commits)
- 👤 **Human dev:** ~$580 (5.8h @ $100/h, 30min dedup)

Generated on 2026-09-17 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)

---



Diagnostyka Plesk/WordPress oparta na dowodach, z opcjonalnym LLM i kontrolowanym wykonaniem.
**Wydanie rozwojowe. Nie jest certyfikowanym skanerem ani autonomicznym administratorem serwera.**

Wersja 0.6.0 rozwija poprzedni pakiet 0.5.0 (jego moduł `guard` pozostaje bez zmian). Zachowano wcześniejsze moduły diagnostyki,
DSL, kolektory, prywatność LLM, Executor, SQLite, security/offline, dokumentację i laboratoria.
Nowa grupa poleceń `guard` nie zastępuje `security`, `diagnose`, `triage` ani `apply`.

## Nowe w 0.6.0: bezpośrednie DeepSeek API

`backend="deepseek"`, własny `DEEPSEEK_API_KEY`, oficjalny endpoint, bez SDK i bez
pośrednictwa OpenRouter. Profile `deepseek-flash` (V4.1 Flash według dokumentacji
z 2026-09-10) i `deepseek-v4-pro`. Tryby thinking, poziomy `low/high/max`, JSON,
ograniczone odpowiedzi, brak zapisu reasoning_content. Dane nadal przechodzą strict.
Zgoda na bezpośrednie API **nie jest gwarancją ZDR**. Domyślnie nie przełączamy
istniejących konfiguracji; szczegółowe warunki są w `docs/DEEPSEEK.md`.

## Nowe: bezpośrednie Z.ai API (backend `zai`)

`backend="zai"`, własny `ZAI_API_KEY`, stały endpoint `https://api.z.ai/api/paas/v4`,
bez SDK i bez pośrednictwa OpenRouter. Modele potwierdzone żywą listą `/models`
(2026-09-16): `glm-5.3`, `glm-5.3-flash` (profile `accurate`/`fast`) oraz starsze
GLM-5.x. Te same jawne zgody co DeepSeek (`require_zdr=false`,
`accept_provider_data_policy=true`); szczegóły w `docs/ZAI.md`,
konfiguracja w `examples/zai/`.

```bash
fixplesk --config config.toml llm-info
fixplesk --config config.toml llm-preview --snapshot snapshot.json --output payload.json
```

### Domyślne modele per provider i kolejność według dostępności

Skrypty (`scripts/demo_llm_twin.py`, `scripts/benchmark_llm.py`) czytają z `.env`:
`LLM_PROVIDER_ORDER` (kolejność providerów wg dostępności klucza), `LLM_MODEL_DEEPSEEK`/`_ALT`,
`LLM_MODEL_ZAI`/`_ALT`, `LLM_MODEL_OPENROUTER`/`_ALT`. Przy porażce modelu skrypt jawnie
przechodzi na alternatywę **tego samego** providera, potem do następnego w kolejności;
każdy hop jest logowany. Rdzeń silnika fixplesk nadal nie robi cichego fallbacku.
`LLM_MODEL` pozostaje pojedynczym domyślnym modelem narzędzia `costs`.

Konfiguracja: `examples/deepseek/llm-section.toml`; pełne demo:
`examples/deepseek/config.toml`. Poprzednie artefakty: `history/0.5.0/dist/`.
Wyniki bieżącej weryfikacji: `docs/TESTING_0.6.0.md`.

## Laboratorium Docker: cyfrowy bliźniak i doradca LLM jedną komendą

`bash scripts/demo_twin.sh` uruchamia w izolowanym Dockerze deterministyczny bliźniak
Plesk/WordPress (sieć wewnętrzna, read-only, `cap_drop: ALL`) i kontener demo, który
wstrzykuje usterkę, wykonuje offline diagnozę, a po podaniu klucza wysyła jeden
zminimalizowany payload doradczy do wybranego modelu. Bez kluczy przepływ jest w pełni
offline i kończy się podglądem kontraktu dostawcy (`llm-preview`).

```bash
DEEPSEEK_API_KEY=sk-... bash scripts/demo_twin.sh        # deepseek-flash (V4.1 Flash, direct)
ZAI_API_KEY=... bash scripts/demo_twin.sh                 # glm-5.3 (Z.ai direct)
OPENROUTER_API_KEY=sk-or-... bash scripts/demo_twin.sh    # openrouter/z-ai/glm-5.3 (ZDR)
bash scripts/demo_twin.sh                                 # offline + preview, bez kluczy
```

Pozostałe profile izolowane: `scripts/test_offline_docker.sh` (network_mode: none),
`scripts/test_twin_docker.sh` (pełny pakiet testów wobec bliźniaka),
`scripts/test_guard_docker.sh`, `scripts/test_security_docker.sh`,
`scripts/test_wordpress_docker.sh` (prawdziwy WordPress). Szczegóły: `docs/DIGITAL_TWIN.md`.

## Benchmark doradczy LLM: DeepSeek vs z.ai

`scripts/benchmark_llm.py` porównuje silniki na tych samych scenariuszach (załączone
snapshoty + usterki syntetycznego bliźniaka), mierząc skuteczność walidacji `Advice`,
latencję, zużycie tokenów i koszt. Podczas pomiaru każdy przebieg raportuje na bieżąco
(czas, tokeny, status), więc długie żądania są zawsze widoczne. Ceny wariantów OpenRouter
pobierane są na żywo; klucze tylko ze środowiska.

```bash
python scripts/benchmark_llm.py --selftest                        # bez sieci i kluczy
DEEPSEEK_API_KEY=... ZAI_API_KEY=... \
  python scripts/benchmark_llm.py --repeats 2                     # właściwy pomiar
```

Wynik (2026-09-16, kontrakt strict): **deepseek-v4-pro 90%** skutecznych porad (47 s śr.),
zai-glm-5.3 60% (157 s, plan coding), oba flashe 30%. Pełne tabele: `test-results/`
i `docs/analysis/llm-benchmark.md`.

## Funkcje zachowane z 0.5.0

| Funkcja | Implementacja | Granica |
|---|---|---|
| Pliki i katalogi | Lokalny skan document_root widocznych domen, metadane, hash, podejrzane wzorce, porównanie przebiegów | Bez dekodowania/wykonania PHP, usuwania i automatycznej kwarantanny |
| Integralność WP | Cache oficjalnych sum core dla wybranej wersji i locale oraz lokalne porównanie | Wersję wskazuje operator; brak potwierdzenia inventory i pełnej obsługi sum wtyczek premium |
| Bazy danych o zagrożeniach | Pobieranie KEV JSON, EPSS CSV.gz, Wordfence v3 JSON, sum WP; walidacja, historia, SHA-256, TTL | Bez podpisów producenta/TUF; import lokalny nie jest uwierzytelniony |
| Wersje WP | Dopasowanie core/plugin/theme do zakresów Wordfence; dołączenie cached KEV/EPSS po CVE | Komparator celowo obsługuje tylko numeryczne wersje; sufiksy oznaczają niepewność |
| Wskaźniki plików | Import własnych list SHA-256 CSV/JSON; zgodność i sprzeczności źródeł | Trafienie nie uruchamia usuwania |
| Wycieki kont | HIBP k-anon e-mail oraz osobna kontrola hasła online/offline | Bez łączenia kont z hasłami, łamania hashy i prób logowania |
| Dane WP | Odczytowy audyt wybranych wyeksportowanych pól opcji/postów i deklarowanych ról | Bez nowego połączenia SQL, automatycznego eksportu tabel i odczytu user_pass |
| Skrypty runtime | Prywatne pliki w `/tmp`, zamknięty DSL read-only, SHA-256, akceptacja, wynik, kopia trwała | Tylko `file.stat` i `file.sha256`; dowolny kod LLM nie jest wykonywany |
| Prawa pliku | Plan ograniczenia praw grupy/other jednego pliku; realny `fchmod` po akceptacji | Bez chown/rekurencji/ACL/dowiązań; tylko lokalny host |
| Obserwacje zmiany | HTTP GET, wybrane jednostki systemd, przyrost logów, diff i trwały dziennik | To jawnie wybrany zakres; nie test wszystkich usług i transakcji biznesowych |

## Instalacja

Wymagania: Python 3.11+, Linux/POSIX. Wykonanie skryptów przez sealed memfd wymaga Linuksa.
Nie opublikowano paczki w PyPI.

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install ./dist/fixplesk-0.6.0-py3-none-any.whl
fixplesk --version
fixplesk guard --help
```

Instalacja zależności wymaga internetu albo przygotowanego repozytorium lokalnego.
Analiza zapisanych danych działa bez LLM i bez sieci. Nowe pobrania i sondy wymagają `--allow-network`.

## Skan jednej domeny lub widocznych domen

Najpierw utwórz i sprawdź zaufane inventory. Opcja `--config` występuje **przed** podkomendą.
Nie używaj inventory pochodzącego od niezaufanego klienta HTTP. Uprawnienia deklarowane w pliku nie nadają uprawnień systemowych.

```bash
fixplesk init
fixplesk --config config.toml guard files scan --domain example.com --output scan-before.json
fixplesk --config config.toml guard files scan --all --output scan-all.json
fixplesk guard files compare --before scan-before.json --after scan-after.json --output diff.json
```

Zastąp `example.com` własnym celem istniejącym w konfiguracji. `--all` oznacza wyłącznie widoczne
w inventory cele, nie automatyczne odkrycie wszystkich domen serwera. Nie skanuj całego `/` jako root.
Nowe lokalne funkcje odmówią działania przy aktywnej konfiguracji SSH, zamiast czytać przypadkowo host lokalny.

Skan nie podąża za dowiązaniami; nie czyta hardlinków, urządzeń ani FIFO, nie przechodzi na inny filesystem.
Ma limity plików, bajtów, głębokości i czasu. Brak dostępu, rotacja czy limit trafia do `gaps`.
Odczyt nie jest atomowym snapshotem i może zmienić atime. Prawa POSIX nie dowodzą dostępności HTTP.
Nie jest oceniana pełna semantyka ACL/SELinux/nginx/Apache. Raportów nie zapisuj pod katalogiem witryny.

## Pobieranie i import baz

```bash
fixplesk guard feeds list
fixplesk guard feeds sync --source cisa-kev --allow-network
fixplesk guard feeds sync --source epss --allow-network
# WORDFENCE_API_KEY ustaw w chronionym środowisku procesu, nie w argv.
fixplesk guard feeds sync --source wordfence --allow-network
fixplesk guard feeds status --source wordfence
fixplesk guard wp-match --inventory components.json --output vulnerabilities.json
```

`components.json` jest tablicą: `id`, `domain`, `type` (`core`/`plugin`/`theme`), `slug`, `version`,
`observed_at` (ISO8601 ze strefą), `identity_verified` (bool). Ostatnie pole jest deklaracją operatora,
nie zdalnym poświadczeniem tożsamości. Nie kopiuj daty z zegara bez rzeczywistego odczytu.

Cache zachowuje całe źródło i jego informacje licencyjne. Domyślnie trafia do `~/.cache/fixplesk/feeds`.
Import lokalny: `guard feeds import --source ... --input ... --obtained-at ...`.
Nieudany nowy import nie przełącza wskaźnika na niepoprawne dane. Nie ma automatycznej aktualizacji aplikacji ani pakietów serwera.
Opcjonalny przykład timera odświeżającego **dane** jest w `integrations/systemd`; nie jest instalowany ani włączany.

Przykład sum **historycznej, jawnie wybranej** wersji, nie rekomendacja aktualnej wersji WordPressa:

```bash
fixplesk guard feeds sync --source wp-checksums --version 6.8.1 --locale en_US --allow-network
fixplesk --config config.toml guard files scan --domain example.com \
  --checksum-version 6.8.1 --checksum-locale en_US --output integrity.json
```

Nie przypisuj tych samych sum witrynom o różnych wersjach. Niezgodność jest dowodem różnicy,
a nie automatycznie dowodem malware. Pierwszy własny skan nie stanowi zaufanej czystej bazy.

## Kontrola kont i haseł

```bash
# HIBP_API_KEY w chronionym środowisku. Ukryty prompt e-mail; bez adresu w argv.
fixplesk guard breach email --account-ref mail-001 --consent --allow-network

# Osobne pytanie o hasło. Wymagany lokalny TTY; bez potoku, pliku ani argumentu z hasłem.
fixplesk guard breach password --online --consent --allow-network

# Offline: legalnie pozyskany korpus SHA1:count i jego rzeczywista data.
fixplesk guard breach password --offline-hashes /secure/pwned-passwords.txt \
  --corpus-date 2026-09-15T00:00:00Z --consent
```

Kontrola e-maila używa k-anon z prefiksem 6 znaków; wymaga odpowiedniego planu HIBP.
Kontrola hasła używa prefiksu 5 znaków i paddingu. Nie ma fallbacku do wysłania całego adresu/hasła.
Nie są przechowywane surowe odpowiedzi dotyczące innych kont. Szczegóły i granice w `docs/guard/BREACHES.md`.
Podana data korpusu jest przykładem składni — musi odpowiadać faktycznemu źródłu.

## Jawne skrypty tworzone podczas działania

```bash
fixplesk --config config.toml guard session create --domain example.com --output session.json
# Podstaw rzeczywistą ścieżkę zwróconą w session.json.
fixplesk --config config.toml guard session stage --domain example.com \
  --session /tmp/fixplesk-UID-IDENTYFIKATOR --spec script-spec.json --output staged.json
fixplesk --config config.toml guard session list --domain example.com \
  --session /tmp/fixplesk-UID-IDENTYFIKATOR
```

`script-spec.json` może być propozycją LLM lub operatora:

```json
{"schema_version":1,"origin":"llm","operation":"file.sha256","path":"index.php","purpose":"Sprawdź pełną sumę pliku bez wykonywania PHP"}
```

`guard session run --script ID --approve SHA256` to podgląd. Dopiero dodanie `--execute` uruchamia
zamknięty szablon. Skrypt musi pozostać bajt w bajt zgodny z generatorem. Wykonanie używa `python -I`
i zapieczętowanych bajtów memfd, a nie dowolnej zawartości pod zmienną nazwą w `/tmp`.
Nie jest to sandbox dla arbitralnego kodu. Nie ma tu nowej automatycznej rozmowy z LLM: przyjmowany jest
walidowany JSON propozycji. Dotychczasowa integracja LLM paczki nadal istnieje oddzielnie.

Kopia skryptów, manifestów, rollbacków i wyników jest pod `state_dir/guard-sessions/ID`.
Audyt zdarzeń znajduje się we wspólnym SQLite. `guard session export` tworzy prywatny ZIP.
Nie wysyłaj całego katalogu sesji do LLM ani do publicznego zgłoszenia.

## Ograniczenie praw jednego pliku

Zapis wymaga `allow_mutations=true` w zaufanym celu inventory oraz uprawnień OS. Nie rozszerzaj uprawnień procesu tylko po to,
żeby test przeszedł. Nowa operacja nie próbuje zgadywać docelowego właściciela ani zastępować natywnych napraw konfiguracji Pleska.

```bash
fixplesk --config config.toml guard permissions plan --domain example.com \
  --path backup.sql --mode 0640 --policy checks.json \
  --why "Usuń zbędny zapis dla innych kont po przeglądzie uprawnień" --output permission-plan.json
fixplesk guard permissions run --plan permission-plan.json --output dry-run.json
```

Dobór 0640 jest przykładem, nie uniwersalną polityką. Pole `digest` z dry-run zatwierdza konkretny plan:

```bash
fixplesk --config config.toml guard permissions run --plan permission-plan.json \
  --approve SHA256_PLANU --execute --allow-network --output execution.json
```

`checks.json` określa kontrolę, np. `{"http_checks":[{"path":"/","expected_status":200,"required_text":"Nazwa witryny"}],"services":[]}`.
HTTP200 bez właściwego znacznika nie przechodzi. Zmiana treści blokuje wynik, chyba że jawnie ustawisz
`allow_content_drift=true`. Dla usług podaj wyłącznie rzeczywiste jednostki z inventory.
Pełny model: `fixplesk guard schema --kind policy`.

Plan ważny jest 15 minut. Zmienia tylko prawa grupy/other jednego zwykłego pliku, bez rozszerzania praw.
Odmowa przy ACL, hardlinkach, symlinkach, zmianie treści/tożsamości i nierozliczonym wcześniejszym planie.
Brak sukcesu sondy nie powoduje domyślnie ponownego odsłonięcia starych uprawnień.
Zgoda `--allow-restore-previous-mode` musi być częścią planu **przed** wykonaniem.

Po nieznanym wyniku użyj istniejącego `recovery status` albo nowego `guard permissions reconcile --digest ... --allow-network`.
`--acknowledge` rozlicza obserwowany stan po kontrolach; nie wznawia i nie powtarza zapisu.
Inny wynik, zmieniona treść lub nowe inventory wymagają procedury ręcznej.

## Testy, demo i plany rozwoju

```bash
python scripts/demo_guard.py --output-dir /tmp/fixplesk-guard-demo
python -m pytest tests/assurance -q
bash scripts/test_guard_docker.sh
```

Demo tworzy wyłącznie syntetyczne pliki i lokalne atrapowe odpowiedzi kontroli; nie diagnozuje Pleska.
Status bieżących testów jest w `docs/TESTING_0.6.0.md`; wcześniejszy pomiar zachowano w `docs/guard/TESTING_0.5.0.md`. Docker pozostaje osobnym profilem.
Szczegóły: `docs/guard/ARCHITECTURE.md`, `docs/guard/ROADMAP.md`, `docs/guard/SOURCES.md`.
Poprzednia instrukcja: `docs/history/README_0.4.0.md`.


## License

Licensed under Apache-2.0.
