diff --git a/saikyo-audit-report/bin/saikyo-audit-report b/saikyo-audit-report/bin/saikyo-audit-report new file mode 100644 index 0000000..0cdb86a --- /dev/null +++ b/saikyo-audit-report/bin/saikyo-audit-report @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +set -euo pipefail + +OUT_BASE="/var/lib/saikyo-audit-report" +TS="$(date -u +%Y%m%dT%H%M%SZ)" +OUT_DIR="${OUT_BASE}/${TS}" + +mkdir -p "${OUT_DIR}" "${OUT_DIR}/crypto" "${OUT_DIR}/apt" "${OUT_DIR}/system" "${OUT_DIR}/licenses" "${OUT_DIR}/network" +chmod 0755 "${OUT_BASE}" "${OUT_DIR}" || true + +{ + echo "timestamp_utc=${TS}" + echo "hostname=$(hostname)" + echo "kernel=$(uname -srmo)" + echo "arch=$(dpkg --print-architecture 2>/dev/null || true)" +} > "${OUT_DIR}/system/summary.env" + +# Saikyo subscription/license status (if present) +if command -v saikyo-license >/dev/null 2>&1; then + (saikyo-license status 2>&1 || true) > "${OUT_DIR}/system/saikyo-license.status" + (saikyo-license verify 2>&1 || true) > "${OUT_DIR}/system/saikyo-license.verify" +else + echo "saikyo-license not installed" > "${OUT_DIR}/system/saikyo-license.status" +fi + +if [ -f /etc/os-release ]; then + cp -f /etc/os-release "${OUT_DIR}/system/os-release" || true +fi +if [ -f /usr/lib/os-release ]; then + cp -f /usr/lib/os-release "${OUT_DIR}/system/os-release.lib" || true +fi + +# Package inventory +(dpkg-query -W -f='${Package}\t${Version}\n' 2>/dev/null || true) > "${OUT_DIR}/packages.tsv" + +# APT sources + keyrings +mkdir -p "${OUT_DIR}/apt/sources.list.d" || true +if [ -f /etc/apt/sources.list ]; then + cp -f /etc/apt/sources.list "${OUT_DIR}/apt/sources.list" || true +fi +if [ -d /etc/apt/sources.list.d ]; then + cp -a /etc/apt/sources.list.d/. "${OUT_DIR}/apt/sources.list.d/" 2>/dev/null || true +fi +(ls -la /usr/share/keyrings 2>/dev/null || true) > "${OUT_DIR}/apt/keyrings.ls" + +(grep -RhsE '^(deb|deb-src)\s' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null || true) > "${OUT_DIR}/apt/sources.lines" + +# Hashes for installed Saikyo keyring/list (if present) +for f in \ + /usr/share/keyrings/saikyo-archive-keyring.gpg \ + /etc/apt/sources.list.d/saikyo-os.list \ + /etc/os-release \ + /usr/lib/os-release +do + if [ -f "$f" ]; then + sha256sum "$f" >> "${OUT_DIR}/sha256sum.files" || true + fi +done + +# Crypto / GOST checks +{ + echo "openssl_version=$(openssl version 2>/dev/null || true)" + echo "openssl_engines=" + openssl engine -t -c 2>/dev/null || true +} > "${OUT_DIR}/crypto/openssl-engine.txt" + +if command -v gostsum >/dev/null 2>&1; then + echo test | gostsum > "${OUT_DIR}/crypto/gostsum.txt" 2>&1 || true +else + echo "gostsum not installed" > "${OUT_DIR}/crypto/gostsum.txt" +fi + +if command -v lsmod >/dev/null 2>&1; then + lsmod | grep -i gost > "${OUT_DIR}/crypto/lsmod-gost.txt" 2>/dev/null || true +fi + +(grep -RhsE '(pool\.ntp\.org|ntp\.org|time\.google\.com|time\.windows\.com|geoip|telemetry)' /etc 2>/dev/null || true) > "${OUT_DIR}/network/external-indicators.txt" + +(systemctl list-unit-files 2>/dev/null || true) > "${OUT_DIR}/system/unit-files.txt" + +# Secure Boot / MOK / TPM +{ + echo "mokutil_sb_state:" + (mokutil --sb-state 2>/dev/null || true) + echo + echo "mokutil_list_enrolled:" + (mokutil --list-enrolled 2>/dev/null || true) + echo + echo "tpm_devices:" + (ls -la /dev/tpm* 2>/dev/null || true) + echo + echo "saikyo_mok_der_present:" + if [ -f /usr/share/saikyo-os/secure-boot/saikyo-mok.der ]; then + sha256sum /usr/share/saikyo-os/secure-boot/saikyo-mok.der || true + else + echo "missing" + fi +} > "${OUT_DIR}/system/secure-boot.txt" + +LICENSE_TSV="${OUT_DIR}/licenses/licenses.tsv" +PROBLEM_TSV="${OUT_DIR}/licenses/problematic-licenses.tsv" + +echo -e "package\tlicense" > "${LICENSE_TSV}" +echo -e "package\tmatched" > "${PROBLEM_TSV}" + +while IFS=$'\t' read -r pkg ver; do + [ -n "${pkg}" ] || continue + cfile="/usr/share/doc/${pkg}/copyright" + lic="UNKNOWN" + if [ -f "${cfile}" ]; then + lic=$(awk -F': ' 'BEGIN{l=""} $1=="License" && l=="" {l=$2} END{if(l=="") print "UNKNOWN"; else print l}' "${cfile}" 2>/dev/null || echo "UNKNOWN") + fi + echo -e "${pkg}\t${lic}" >> "${LICENSE_TSV}" + case "${lic}" in + *SSPL*|*Elastic*|*RSAL*|*Redis*Source*Available*|*Server*Side*Public*License*|*AGPL*) + echo -e "${pkg}\t${lic}" >> "${PROBLEM_TSV}" + ;; + esac +done < "${OUT_DIR}/packages.tsv" + +echo "Report created: ${OUT_DIR}" diff --git a/saikyo-audit-report/debian/.debhelper/generated/saikyo-audit-report/dh_installchangelogs.dch.trimmed b/saikyo-audit-report/debian/.debhelper/generated/saikyo-audit-report/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..3dc997f --- /dev/null +++ b/saikyo-audit-report/debian/.debhelper/generated/saikyo-audit-report/dh_installchangelogs.dch.trimmed @@ -0,0 +1,5 @@ +saikyo-audit-report (1.0.0) stable; urgency=medium + + * Initial release. + + -- SAIKYO OS Tue, 07 Jan 2026 00:00:00 +0000 diff --git a/saikyo-audit-report/debian/.debhelper/generated/saikyo-audit-report/installed-by-dh_install b/saikyo-audit-report/debian/.debhelper/generated/saikyo-audit-report/installed-by-dh_install new file mode 100644 index 0000000..376e84e --- /dev/null +++ b/saikyo-audit-report/debian/.debhelper/generated/saikyo-audit-report/installed-by-dh_install @@ -0,0 +1 @@ +./bin/saikyo-audit-report diff --git a/saikyo-audit-report/debian/.debhelper/generated/saikyo-audit-report/installed-by-dh_installdocs b/saikyo-audit-report/debian/.debhelper/generated/saikyo-audit-report/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-audit-report/debian/changelog b/saikyo-audit-report/debian/changelog new file mode 100644 index 0000000..3dc997f --- /dev/null +++ b/saikyo-audit-report/debian/changelog @@ -0,0 +1,5 @@ +saikyo-audit-report (1.0.0) stable; urgency=medium + + * Initial release. + + -- SAIKYO OS Tue, 07 Jan 2026 00:00:00 +0000 diff --git a/saikyo-audit-report/debian/control b/saikyo-audit-report/debian/control new file mode 100644 index 0000000..572d6ae --- /dev/null +++ b/saikyo-audit-report/debian/control @@ -0,0 +1,14 @@ +Source: saikyo-audit-report +Section: admin +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-audit-report +Architecture: all +Depends: ${misc:Depends}, bash, coreutils, dpkg, ca-certificates, gnupg, openssl, findutils, grep, sed, gawk +Description: Saikyo OS audit and registry report generator + Generates a local report directory with package inventory, repositories/keys, + OS identity, hashes, and crypto/GOST checks. diff --git a/saikyo-audit-report/debian/copyright b/saikyo-audit-report/debian/copyright new file mode 100644 index 0000000..63ff112 --- /dev/null +++ b/saikyo-audit-report/debian/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-audit-report +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-audit-report/debian/debhelper-build-stamp b/saikyo-audit-report/debian/debhelper-build-stamp new file mode 100644 index 0000000..45a2118 --- /dev/null +++ b/saikyo-audit-report/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-audit-report diff --git a/saikyo-audit-report/debian/files b/saikyo-audit-report/debian/files new file mode 100644 index 0000000..92380ae --- /dev/null +++ b/saikyo-audit-report/debian/files @@ -0,0 +1,2 @@ +saikyo-audit-report_1.0.0_all.deb admin optional +saikyo-audit-report_1.0.0_amd64.buildinfo admin optional diff --git a/saikyo-audit-report/debian/install b/saikyo-audit-report/debian/install new file mode 100644 index 0000000..68e314f --- /dev/null +++ b/saikyo-audit-report/debian/install @@ -0,0 +1 @@ +bin/saikyo-audit-report usr/sbin/saikyo-audit-report diff --git a/saikyo-audit-report/debian/rules b/saikyo-audit-report/debian/rules new file mode 100755 index 0000000..0fc3651 --- /dev/null +++ b/saikyo-audit-report/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: diff --git a/saikyo-audit-report/debian/saikyo-audit-report.substvars b/saikyo-audit-report/debian/saikyo-audit-report.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-audit-report/debian/saikyo-audit-report.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-audit-report/debian/saikyo-audit-report/DEBIAN/control b/saikyo-audit-report/debian/saikyo-audit-report/DEBIAN/control new file mode 100644 index 0000000..e0f48e6 --- /dev/null +++ b/saikyo-audit-report/debian/saikyo-audit-report/DEBIAN/control @@ -0,0 +1,11 @@ +Package: saikyo-audit-report +Version: 1.0.0 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 15 +Depends: bash, coreutils, dpkg, ca-certificates, gnupg, openssl, findutils, grep, sed, gawk +Section: admin +Priority: optional +Description: Saikyo OS audit and registry report generator + Generates a local report directory with package inventory, repositories/keys, + OS identity, hashes, and crypto/GOST checks. diff --git a/saikyo-audit-report/debian/saikyo-audit-report/DEBIAN/md5sums b/saikyo-audit-report/debian/saikyo-audit-report/DEBIAN/md5sums new file mode 100644 index 0000000..d5d2a49 --- /dev/null +++ b/saikyo-audit-report/debian/saikyo-audit-report/DEBIAN/md5sums @@ -0,0 +1,3 @@ +c95795694790aeed9d166d5eaf2765da usr/sbin/saikyo-audit-report/saikyo-audit-report +a97380b9477d5cbeab831599b3321dfe usr/share/doc/saikyo-audit-report/changelog.gz +3a820ad7cb163259d2f91549750a52ed usr/share/doc/saikyo-audit-report/copyright diff --git a/saikyo-audit-report/debian/saikyo-audit-report/usr/sbin/saikyo-audit-report/saikyo-audit-report b/saikyo-audit-report/debian/saikyo-audit-report/usr/sbin/saikyo-audit-report/saikyo-audit-report new file mode 100755 index 0000000..0cdb86a --- /dev/null +++ b/saikyo-audit-report/debian/saikyo-audit-report/usr/sbin/saikyo-audit-report/saikyo-audit-report @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +set -euo pipefail + +OUT_BASE="/var/lib/saikyo-audit-report" +TS="$(date -u +%Y%m%dT%H%M%SZ)" +OUT_DIR="${OUT_BASE}/${TS}" + +mkdir -p "${OUT_DIR}" "${OUT_DIR}/crypto" "${OUT_DIR}/apt" "${OUT_DIR}/system" "${OUT_DIR}/licenses" "${OUT_DIR}/network" +chmod 0755 "${OUT_BASE}" "${OUT_DIR}" || true + +{ + echo "timestamp_utc=${TS}" + echo "hostname=$(hostname)" + echo "kernel=$(uname -srmo)" + echo "arch=$(dpkg --print-architecture 2>/dev/null || true)" +} > "${OUT_DIR}/system/summary.env" + +# Saikyo subscription/license status (if present) +if command -v saikyo-license >/dev/null 2>&1; then + (saikyo-license status 2>&1 || true) > "${OUT_DIR}/system/saikyo-license.status" + (saikyo-license verify 2>&1 || true) > "${OUT_DIR}/system/saikyo-license.verify" +else + echo "saikyo-license not installed" > "${OUT_DIR}/system/saikyo-license.status" +fi + +if [ -f /etc/os-release ]; then + cp -f /etc/os-release "${OUT_DIR}/system/os-release" || true +fi +if [ -f /usr/lib/os-release ]; then + cp -f /usr/lib/os-release "${OUT_DIR}/system/os-release.lib" || true +fi + +# Package inventory +(dpkg-query -W -f='${Package}\t${Version}\n' 2>/dev/null || true) > "${OUT_DIR}/packages.tsv" + +# APT sources + keyrings +mkdir -p "${OUT_DIR}/apt/sources.list.d" || true +if [ -f /etc/apt/sources.list ]; then + cp -f /etc/apt/sources.list "${OUT_DIR}/apt/sources.list" || true +fi +if [ -d /etc/apt/sources.list.d ]; then + cp -a /etc/apt/sources.list.d/. "${OUT_DIR}/apt/sources.list.d/" 2>/dev/null || true +fi +(ls -la /usr/share/keyrings 2>/dev/null || true) > "${OUT_DIR}/apt/keyrings.ls" + +(grep -RhsE '^(deb|deb-src)\s' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null || true) > "${OUT_DIR}/apt/sources.lines" + +# Hashes for installed Saikyo keyring/list (if present) +for f in \ + /usr/share/keyrings/saikyo-archive-keyring.gpg \ + /etc/apt/sources.list.d/saikyo-os.list \ + /etc/os-release \ + /usr/lib/os-release +do + if [ -f "$f" ]; then + sha256sum "$f" >> "${OUT_DIR}/sha256sum.files" || true + fi +done + +# Crypto / GOST checks +{ + echo "openssl_version=$(openssl version 2>/dev/null || true)" + echo "openssl_engines=" + openssl engine -t -c 2>/dev/null || true +} > "${OUT_DIR}/crypto/openssl-engine.txt" + +if command -v gostsum >/dev/null 2>&1; then + echo test | gostsum > "${OUT_DIR}/crypto/gostsum.txt" 2>&1 || true +else + echo "gostsum not installed" > "${OUT_DIR}/crypto/gostsum.txt" +fi + +if command -v lsmod >/dev/null 2>&1; then + lsmod | grep -i gost > "${OUT_DIR}/crypto/lsmod-gost.txt" 2>/dev/null || true +fi + +(grep -RhsE '(pool\.ntp\.org|ntp\.org|time\.google\.com|time\.windows\.com|geoip|telemetry)' /etc 2>/dev/null || true) > "${OUT_DIR}/network/external-indicators.txt" + +(systemctl list-unit-files 2>/dev/null || true) > "${OUT_DIR}/system/unit-files.txt" + +# Secure Boot / MOK / TPM +{ + echo "mokutil_sb_state:" + (mokutil --sb-state 2>/dev/null || true) + echo + echo "mokutil_list_enrolled:" + (mokutil --list-enrolled 2>/dev/null || true) + echo + echo "tpm_devices:" + (ls -la /dev/tpm* 2>/dev/null || true) + echo + echo "saikyo_mok_der_present:" + if [ -f /usr/share/saikyo-os/secure-boot/saikyo-mok.der ]; then + sha256sum /usr/share/saikyo-os/secure-boot/saikyo-mok.der || true + else + echo "missing" + fi +} > "${OUT_DIR}/system/secure-boot.txt" + +LICENSE_TSV="${OUT_DIR}/licenses/licenses.tsv" +PROBLEM_TSV="${OUT_DIR}/licenses/problematic-licenses.tsv" + +echo -e "package\tlicense" > "${LICENSE_TSV}" +echo -e "package\tmatched" > "${PROBLEM_TSV}" + +while IFS=$'\t' read -r pkg ver; do + [ -n "${pkg}" ] || continue + cfile="/usr/share/doc/${pkg}/copyright" + lic="UNKNOWN" + if [ -f "${cfile}" ]; then + lic=$(awk -F': ' 'BEGIN{l=""} $1=="License" && l=="" {l=$2} END{if(l=="") print "UNKNOWN"; else print l}' "${cfile}" 2>/dev/null || echo "UNKNOWN") + fi + echo -e "${pkg}\t${lic}" >> "${LICENSE_TSV}" + case "${lic}" in + *SSPL*|*Elastic*|*RSAL*|*Redis*Source*Available*|*Server*Side*Public*License*|*AGPL*) + echo -e "${pkg}\t${lic}" >> "${PROBLEM_TSV}" + ;; + esac +done < "${OUT_DIR}/packages.tsv" + +echo "Report created: ${OUT_DIR}" diff --git a/saikyo-audit-report/debian/saikyo-audit-report/usr/share/doc/saikyo-audit-report/changelog.gz b/saikyo-audit-report/debian/saikyo-audit-report/usr/share/doc/saikyo-audit-report/changelog.gz new file mode 100644 index 0000000..c67714c Binary files /dev/null and b/saikyo-audit-report/debian/saikyo-audit-report/usr/share/doc/saikyo-audit-report/changelog.gz differ diff --git a/saikyo-audit-report/debian/saikyo-audit-report/usr/share/doc/saikyo-audit-report/copyright b/saikyo-audit-report/debian/saikyo-audit-report/usr/share/doc/saikyo-audit-report/copyright new file mode 100644 index 0000000..63ff112 --- /dev/null +++ b/saikyo-audit-report/debian/saikyo-audit-report/usr/share/doc/saikyo-audit-report/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-audit-report +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-audit-report/debian/source/format b/saikyo-audit-report/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/saikyo-audit-report/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/saikyo-av-gui/autostart/saikyo-av-gui.desktop b/saikyo-av-gui/autostart/saikyo-av-gui.desktop new file mode 100644 index 0000000..d01cb75 --- /dev/null +++ b/saikyo-av-gui/autostart/saikyo-av-gui.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Saikyo Antivirus (Tray) +Comment=Start Saikyo Antivirus tray app +Exec=/usr/bin/saikyo-av-gui --tray +Icon=saikyo-av +Terminal=false +X-GNOME-Autostart-enabled=true diff --git a/saikyo-av-gui/bin/saikyo-av-admin b/saikyo-av-gui/bin/saikyo-av-admin new file mode 100644 index 0000000..f2e1b07 --- /dev/null +++ b/saikyo-av-gui/bin/saikyo-av-admin @@ -0,0 +1,323 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPORTS_DIR="/var/lib/saikyo-av/reports" +LOG_DIR="/var/log/saikyo-av" +QUAR_DIR="/var/lib/saikyo-av/quarantine" + +mkdir -p "${REPORTS_DIR}" "${LOG_DIR}" "${QUAR_DIR}" || true +chmod 0755 /var/lib/saikyo-av "${REPORTS_DIR}" "${LOG_DIR}" 2>/dev/null || true + +cmd="${1:-}" +shift || true + +utc_ts() { + date -u +%Y-%m-%dT%H:%M:%SZ +} + +write_report() { + local id="$1" + local summary="$2" + local severity="$3" + local outfile="${REPORTS_DIR}/${id}.json" + local created + created="$(utc_ts)" + + # shellcheck disable=SC2129 + { + echo "{" + echo " \"created_utc\": \"${created}\"," + echo " \"severity\": \"${severity}\"," + echo " \"summary\": \"${summary}\"," + echo " \"details\": {" + echo " \"action\": \"${cmd}\"," + echo " \"argv\": \"$*\"" + echo " }," + echo " \"artifacts\": {}," + echo " \"suggested_fixes\": []" + echo "}" + } > "${outfile}" + + echo "${outfile}" +} + +run_to_file() { + local out="$1" + shift + ("$@" 2>&1 || true) | sed -e 's/\r$//' > "${out}" +} + +json_escape() { + # Minimal JSON string escape (no unicode handling needed for our file paths) + sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\t/\\t/g' -e 's/\r/\\r/g' -e 's/\n/\\n/g' +} + +write_scan_report() { + local id="$1" + local summary="$2" + local severity="$3" + local log_file="$4" + local detections_tsv="$5" + local outfile="${REPORTS_DIR}/${id}.json" + local created + created="$(utc_ts)" + + local scanned_count infected_count + scanned_count="$(grep -E '^Scanned files:' "${log_file}" 2>/dev/null | awk -F': ' '{print $2}' | tail -n 1 || true)" + infected_count="$(grep -E '^Infected files:' "${log_file}" 2>/dev/null | awk -F': ' '{print $2}' | tail -n 1 || true)" + scanned_count="${scanned_count:-0}" + infected_count="${infected_count:-0}" + + { + echo "{" + echo " \"created_utc\": \"${created}\"," + echo " \"severity\": \"${severity}\"," + echo " \"summary\": \"${summary}\"," + echo " \"details\": {" + echo " \"action\": \"${cmd}\"," + echo " \"argv\": \"$*\"," + echo " \"scanned_files\": ${scanned_count}," + echo " \"infected_files\": ${infected_count}," + echo " \"detections\": [" + + if [[ -s "${detections_tsv}" ]]; then + first=1 + while IFS=$'\t' read -r pth sig; do + [[ -n "${pth}" ]] || continue + pth_esc="$(printf '%s' "${pth}" | json_escape)" + sig_esc="$(printf '%s' "${sig}" | json_escape)" + if [[ "${first}" -eq 1 ]]; then + first=0 + else + echo "," + fi + printf ' {"path":"%s","name":"%s"}' "${pth_esc}" "${sig_esc}" + done < "${detections_tsv}" + echo + fi + + echo " ]" + echo " }," + echo " \"artifacts\": {" + echo " \"clamav_log\": \"${log_file}\"," + echo " \"detections_tsv\": \"${detections_tsv}\"" + echo " }," + echo " \"suggested_fixes\": [" + echo " {\"id\":\"quarantine\",\"title\":\"Quarantine infected file\",\"description\":\"Move selected file to local quarantine.\",\"requires_consent\":true}," + echo " {\"id\":\"delete\",\"title\":\"Delete infected file\",\"description\":\"Delete selected infected file (dangerous).\",\"requires_consent\":true}" + echo " ]" + echo "}" + } > "${outfile}" + + echo "${outfile}" +} + +svc_is_enabled() { + systemctl is-enabled saikyo-avd.timer 2>/dev/null || true +} + +svc_is_active() { + systemctl is-active saikyo-avd.timer 2>/dev/null || true +} + +case "${cmd}" in + health) + echo "ok" + ;; + + status-protection) + echo "enabled=$(svc_is_enabled) active=$(svc_is_active)" + ;; + + run-evidence) + outdir="${REPORTS_DIR}/artifacts" + mkdir -p "${outdir}" || true + ts="$(date -u +%Y%m%dT%H%M%SZ)" + evidence_out="${outdir}/saikyo-evidence-${ts}.log" + + if command -v saikyo-evidence >/dev/null 2>&1; then + run_to_file "${evidence_out}" saikyo-evidence + elif [ -x /usr/bin/saikyo-evidence ]; then + run_to_file "${evidence_out}" /usr/bin/saikyo-evidence + else + echo "saikyo-evidence not found" > "${evidence_out}" + fi + + rpt="$(write_report "evidence-${ts}" "Evidence report generated" "info" "${cmd}")" + # Append artifacts into report JSON (minimal, without jq dependency). + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"evidence_log\": \"${evidence_out}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + scan) + outdir="${REPORTS_DIR}/artifacts" + mkdir -p "${outdir}" || true + ts="$(date -u +%Y%m%dT%H%M%SZ)" + log_file="${outdir}/clamav-scan-${ts}.log" + det_tsv="${outdir}/clamav-detections-${ts}.tsv" + + if ! command -v clamscan >/dev/null 2>&1; then + echo "clamscan not found (install clamav)" > "${log_file}" + rpt="$(write_report "scan-${ts}" "ClamAV scan failed: clamscan not found" "warn" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"clamav_log\": \"${log_file}\"}#" "${rpt}" || true + echo "${rpt}" + exit 1 + fi + + # Full scan of / with exclusions. + # - exclude hidden paths (/.*/) + # - exclude virtual/system dirs + # - keep a log we can parse + ( + clamscan \ + -r \ + --infected \ + --no-summary \ + --exclude-dir='^/proc/' \ + --exclude-dir='^/sys/' \ + --exclude-dir='^/dev/' \ + --exclude-dir='^/run/' \ + --exclude-dir='^/tmp/' \ + --exclude-dir='/.*/' \ + / \ + 2>&1 + echo + clamscan -r --infected --exclude-dir='^/proc/' --exclude-dir='^/sys/' --exclude-dir='^/dev/' --exclude-dir='^/run/' --exclude-dir='^/tmp/' --exclude-dir='/.*/' / --summary 2>/dev/null || true + ) | sed -e 's/\r$//' > "${log_file}" + + : > "${det_tsv}" + # Parse "PATH: SIGNATURE FOUND" lines. + grep -E ' FOUND$' "${log_file}" | sed -E 's/: (.+) FOUND$//;t;d' >/dev/null 2>&1 || true + while IFS= read -r line; do + # Example: /path/file: Eicar-Test-Signature FOUND + pth="${line%%:*}" + rest="${line#*: }" + sig="${rest% FOUND}" + printf '%s\t%s\n' "${pth}" "${sig}" >> "${det_tsv}" + done < <(grep -E ' FOUND$' "${log_file}" || true) + + infected_count="$(wc -l < "${det_tsv}" 2>/dev/null || echo 0)" + severity="info" + summary="ClamAV scan completed: no threats" + if [[ "${infected_count}" -gt 0 ]]; then + severity="bad" + summary="ClamAV scan completed: ${infected_count} threat(s) found" + fi + + rpt="$(write_scan_report "scan-${ts}" "${summary}" "${severity}" "${log_file}" "${det_tsv}" "${cmd}")" + echo "${rpt}" + ;; + + quarantine) + target="${1:-}" + if [[ -z "${target}" ]]; then + echo "missing file path" >&2 + exit 2 + fi + ts="$(date -u +%Y%m%dT%H%M%SZ)" + bn="$(basename -- "${target}" 2>/dev/null || echo file)" + dest="${QUAR_DIR}/${ts}-${bn}" + if [[ ! -f "${target}" ]]; then + rpt="$(write_report "quarantine-${ts}" "Quarantine failed: file not found" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + fi + mv -f -- "${target}" "${dest}" 2>>"${LOG_DIR}/saikyo-av-admin.log" || { + rpt="$(write_report "quarantine-${ts}" "Quarantine failed: move error" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + } + rpt="$(write_report "quarantine-${ts}" "File quarantined" "warn" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"from\": \"${target}\", \"to\": \"${dest}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + delete) + target="${1:-}" + if [[ -z "${target}" ]]; then + echo "missing file path" >&2 + exit 2 + fi + ts="$(date -u +%Y%m%dT%H%M%SZ)" + if [[ ! -e "${target}" ]]; then + rpt="$(write_report "delete-${ts}" "Delete failed: path not found" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + fi + rm -rf -- "${target}" 2>>"${LOG_DIR}/saikyo-av-admin.log" || { + rpt="$(write_report "delete-${ts}" "Delete failed" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + } + rpt="$(write_report "delete-${ts}" "File deleted" "warn" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"path\": \"${target}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + run-audit) + outdir="${REPORTS_DIR}/artifacts" + mkdir -p "${outdir}" || true + ts="$(date -u +%Y%m%dT%H%M%SZ)" + audit_out="${outdir}/saikyo-audit-report-${ts}.txt" + + if command -v saikyo-audit-report >/dev/null 2>&1; then + run_to_file "${audit_out}" saikyo-audit-report + else + echo "saikyo-audit-report not installed" > "${audit_out}" + fi + + rpt="$(write_report "audit-${ts}" "Audit report executed" "info" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"audit_stdout\": \"${audit_out}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + collect-artifacts) + outdir="${REPORTS_DIR}/artifacts" + mkdir -p "${outdir}" || true + ts="$(date -u +%Y%m%dT%H%M%SZ)" + collect_out="${outdir}/collect-artifacts-${ts}.log" + + if [ -x /usr/share/saikyo-os/forensics/collect-artifacts.sh ]; then + run_to_file "${collect_out}" /usr/share/saikyo-os/forensics/collect-artifacts.sh + else + echo "collect-artifacts.sh not found" > "${collect_out}" + fi + + rpt="$(write_report "collect-${ts}" "Artifacts collection executed" "info" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"collector_log\": \"${collect_out}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + enable-protection) + ts="$(date -u +%Y%m%dT%H%M%SZ)" + (systemctl daemon-reload 2>&1 || true) >> "${LOG_DIR}/saikyo-av-admin.log" || true + if systemctl enable --now saikyo-avd.timer >/dev/null 2>&1; then + rpt="$(write_report "enable-${ts}" "Protection enabled" "info" "${cmd}")" + echo "${rpt}" + exit 0 + else + rpt="$(write_report "enable-${ts}" "Protection enable failed" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + fi + ;; + + disable-protection) + ts="$(date -u +%Y%m%dT%H%M%SZ)" + (systemctl daemon-reload 2>&1 || true) >> "${LOG_DIR}/saikyo-av-admin.log" || true + if systemctl disable --now saikyo-avd.timer >/dev/null 2>&1; then + rpt="$(write_report "disable-${ts}" "Protection disabled" "warn" "${cmd}")" + echo "${rpt}" + exit 0 + else + rpt="$(write_report "disable-${ts}" "Protection disable failed" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + fi + ;; + + *) + echo "Usage: saikyo-av-admin {health|status-protection|run-evidence|run-audit|collect-artifacts|scan|quarantine |delete |enable-protection|disable-protection}" >&2 + exit 2 + ;; +esac diff --git a/saikyo-av-gui/bin/saikyo-av-gui b/saikyo-av-gui/bin/saikyo-av-gui new file mode 100644 index 0000000..ef60e07 --- /dev/null +++ b/saikyo-av-gui/bin/saikyo-av-gui @@ -0,0 +1,532 @@ +#!/usr/bin/env python3 +import argparse +import json +import pathlib +import subprocess +import sys +from datetime import datetime, timezone + +from PyQt5 import QtCore, QtGui, QtWidgets + +REPORTS_DIR_DEFAULT = "/var/lib/saikyo-av/reports" + + +def utc_now() -> str: + return datetime.now(tz=timezone.utc).isoformat(timespec="seconds") + + +def load_reports(reports_dir: pathlib.Path): + items = [] + if not reports_dir.exists(): + return items + for p in sorted(reports_dir.glob("*.json"), key=lambda x: x.stat().st_mtime, reverse=True): + try: + data = json.loads(p.read_text(encoding="utf-8")) + except Exception: + data = {} + items.append( + { + "id": p.stem, + "path": str(p), + "summary": data.get("summary") or "(no summary)", + "severity": data.get("severity") or "unknown", + "created_utc": data.get("created_utc") or "", + "raw": data, + } + ) + return items + + +class ReportsModel(QtCore.QAbstractTableModel): + def __init__(self): + super().__init__() + self.items = [] + + def rowCount(self, parent=QtCore.QModelIndex()): + return len(self.items) + + def columnCount(self, parent=QtCore.QModelIndex()): + return 3 + + def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole): + if role != QtCore.Qt.DisplayRole: + return None + if orientation == QtCore.Qt.Horizontal: + return ["Summary", "Severity", "Created (UTC)"][section] + return str(section + 1) + + def data(self, index, role=QtCore.Qt.DisplayRole): + if not index.isValid(): + return None + item = self.items[index.row()] + col = index.column() + + if role == QtCore.Qt.DisplayRole: + if col == 0: + return item.get("summary") + if col == 1: + return item.get("severity") + if col == 2: + return item.get("created_utc") + + if role == QtCore.Qt.ForegroundRole and col == 1: + sev = (item.get("severity") or "").lower() + if sev in {"ok", "info", "low"}: + return QtGui.QBrush(QtGui.QColor("#2dd4bf")) + if sev in {"medium", "warn", "warning"}: + return QtGui.QBrush(QtGui.QColor("#fbbf24")) + if sev in {"high", "critical", "bad"}: + return QtGui.QBrush(QtGui.QColor("#fb7185")) + return QtGui.QBrush(QtGui.QColor("#9bb0d1")) + + return None + + def set_items(self, items): + self.beginResetModel() + self.items = items + self.endResetModel() + + +class MainWindow(QtWidgets.QMainWindow): + def __init__(self, reports_dir: pathlib.Path, start_in_tray: bool): + super().__init__() + self.reports_dir = reports_dir + self.start_in_tray = start_in_tray + + self.setWindowTitle("Saikyo Antivirus") + self.setWindowIcon(QtGui.QIcon.fromTheme("saikyo-av", QtGui.QIcon.fromTheme("security-high"))) + self.setMinimumSize(980, 620) + + self.model = ReportsModel() + self.table = QtWidgets.QTableView() + self.table.setModel(self.model) + self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) + self.table.horizontalHeader().setStretchLastSection(True) + self.table.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) + self.table.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) + self.table.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) + + self.details = QtWidgets.QPlainTextEdit() + self.details.setReadOnly(True) + self.details.setFont(QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)) + + self.det_list = QtWidgets.QListWidget() + self.det_list.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) + + self.btn_quarantine = QtWidgets.QPushButton("Quarantine") + self.btn_delete = QtWidgets.QPushButton("Delete") + for b in [self.btn_quarantine, self.btn_delete]: + b.setCursor(QtCore.Qt.PointingHandCursor) + b.setMinimumHeight(32) + self.btn_delete.setStyleSheet("background:#2b1420;color:#ffd4df;border:1px solid #5b2031;") + self.btn_quarantine.setEnabled(False) + self.btn_delete.setEnabled(False) + + self.scan_status = QtWidgets.QLabel("") + self.scan_status.setStyleSheet("color:#9bb0d1") + self.scan_progress = QtWidgets.QProgressBar() + self.scan_progress.setRange(0, 0) + self.scan_progress.setVisible(False) + self.btn_stop_scan = QtWidgets.QPushButton("Stop scan") + self.btn_stop_scan.setCursor(QtCore.Qt.PointingHandCursor) + self.btn_stop_scan.setMinimumHeight(32) + self.btn_stop_scan.setEnabled(False) + + self._scan_proc: QtCore.QProcess | None = None + self._scan_timer = QtCore.QElapsedTimer() + self._scan_tick = QtCore.QTimer(self) + self._scan_tick.setInterval(500) + self._scan_tick.timeout.connect(self._update_scan_elapsed) + + self.status = QtWidgets.QLabel("local") + self.status.setStyleSheet("color:#9bb0d1") + + btn_evidence = QtWidgets.QPushButton("Evidence") + btn_audit = QtWidgets.QPushButton("Audit") + btn_collect = QtWidgets.QPushButton("Collect") + btn_scan = QtWidgets.QPushButton("SCAN") + btn_enable = QtWidgets.QPushButton("Enable") + btn_disable = QtWidgets.QPushButton("Disable…") + btn_refresh = QtWidgets.QPushButton("Refresh") + + for b in [btn_evidence, btn_audit, btn_collect, btn_scan, btn_enable, btn_disable, btn_refresh]: + b.setCursor(QtCore.Qt.PointingHandCursor) + b.setMinimumHeight(36) + + btn_scan.setStyleSheet("background:rgba(45,212,191,0.18);border:1px solid rgba(45,212,191,0.35);") + + btn_disable.setStyleSheet("background:#2b1420;color:#ffd4df;border:1px solid #5b2031;") + + left = QtWidgets.QFrame() + left.setFrameShape(QtWidgets.QFrame.StyledPanel) + ll = QtWidgets.QVBoxLayout(left) + ll.setContentsMargins(12, 12, 12, 12) + ll.setSpacing(10) + + title = QtWidgets.QLabel("Admin checks") + title.setStyleSheet("font-weight:800;font-size:16px") + hint = QtWidgets.QLabel("Кнопки запускают проверки с подтверждением (pkexec).") + hint.setWordWrap(True) + hint.setStyleSheet("color:#9bb0d1;font-size:12px") + + ll.addWidget(title) + ll.addWidget(hint) + ll.addSpacing(4) + ll.addWidget(btn_evidence) + ll.addWidget(btn_audit) + ll.addWidget(btn_collect) + ll.addSpacing(8) + ll.addWidget(btn_scan) + ll.addSpacing(8) + ll.addWidget(btn_enable) + ll.addWidget(btn_disable) + ll.addStretch(1) + ll.addWidget(btn_refresh) + ll.addWidget(self.status) + + splitter = QtWidgets.QSplitter() + splitter.setOrientation(QtCore.Qt.Vertical) + top = QtWidgets.QWidget() + tl = QtWidgets.QVBoxLayout(top) + tl.setContentsMargins(0, 0, 0, 0) + tl.addWidget(self.table) + splitter.addWidget(top) + details_box = QtWidgets.QWidget() + dl = QtWidgets.QVBoxLayout(details_box) + dl.setContentsMargins(0, 0, 0, 0) + dl.setSpacing(8) + + det_head = QtWidgets.QHBoxLayout() + det_title = QtWidgets.QLabel("Detections") + det_title.setStyleSheet("font-weight:800;") + det_head.addWidget(det_title) + det_head.addStretch(1) + det_head.addWidget(self.scan_status) + det_head.addWidget(self.scan_progress) + det_head.addWidget(self.btn_stop_scan) + det_head.addWidget(self.btn_quarantine) + det_head.addWidget(self.btn_delete) + dl.addLayout(det_head) + dl.addWidget(self.det_list) + + raw_title = QtWidgets.QLabel("Report JSON") + raw_title.setStyleSheet("font-weight:800;") + dl.addWidget(raw_title) + dl.addWidget(self.details) + + splitter.addWidget(details_box) + splitter.setStretchFactor(0, 2) + splitter.setStretchFactor(1, 1) + + central = QtWidgets.QWidget() + root = QtWidgets.QHBoxLayout(central) + root.setContentsMargins(16, 16, 16, 16) + root.setSpacing(12) + left.setFixedWidth(300) + root.addWidget(left) + root.addWidget(splitter) + self.setCentralWidget(central) + + self.setStyleSheet( + "QMainWindow{background:#0b1220;color:#e7eefc;}" + "QFrame{background:#0f1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}" + "QTableView{background:#0f1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}" + "QHeaderView::section{background:#0c1628;color:#9bb0d1;border:none;padding:8px;font-weight:700;}" + "QPushButton{background:rgba(96,165,250,0.15);border:1px solid rgba(96,165,250,0.35);border-radius:10px;padding:8px 10px;font-weight:700;}" + "QPushButton:hover{background:rgba(96,165,250,0.22);}" + "QPlainTextEdit{background:#0c1628;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}" + ) + + self.table.selectionModel().selectionChanged.connect(self._on_select) + + btn_refresh.clicked.connect(self.refresh) + btn_evidence.clicked.connect(lambda: self._run_admin("run-evidence")) + btn_audit.clicked.connect(lambda: self._run_admin("run-audit")) + btn_collect.clicked.connect(lambda: self._run_admin("collect-artifacts")) + btn_scan.clicked.connect(self._scan) + btn_enable.clicked.connect(lambda: self._run_admin("enable-protection")) + btn_disable.clicked.connect(self._disable) + + self.btn_quarantine.clicked.connect(self._quarantine_selected) + self.btn_delete.clicked.connect(self._delete_selected) + self.det_list.itemSelectionChanged.connect(self._on_det_select) + self.btn_stop_scan.clicked.connect(self._stop_scan) + + self._setup_tray() + self.refresh() + if self.start_in_tray: + QtCore.QTimer.singleShot(50, self.hide) + + def _setup_tray(self): + self.tray = QtWidgets.QSystemTrayIcon(self.windowIcon(), self) + menu = QtWidgets.QMenu() + act_open = menu.addAction("Open") + act_refresh = menu.addAction("Refresh") + menu.addSeparator() + act_quit = menu.addAction("Quit") + act_open.triggered.connect(self._show) + act_refresh.triggered.connect(self.refresh) + act_quit.triggered.connect(QtWidgets.QApplication.quit) + self.tray.setContextMenu(menu) + self.tray.setToolTip("Saikyo Antivirus") + self.tray.activated.connect(lambda r: self._show() if r == QtWidgets.QSystemTrayIcon.Trigger else None) + self.tray.show() + + def _show(self): + self.showNormal() + self.raise_() + self.activateWindow() + + def closeEvent(self, event: QtGui.QCloseEvent): + event.ignore() + self.hide() + self.tray.showMessage( + "Saikyo Antivirus", + "Свернуто в трей. Откройте из значка.", + QtWidgets.QSystemTrayIcon.Information, + 2000, + ) + + def refresh(self): + self.reports_dir.mkdir(parents=True, exist_ok=True) + items = load_reports(self.reports_dir) + self.model.set_items(items) + # systemctl status doesn't require root for is-enabled/is-active. + enabled = "unknown" + active = "unknown" + try: + enabled = subprocess.run( + ["systemctl", "is-enabled", "saikyo-avd.timer"], + capture_output=True, + text=True, + ).stdout.strip() or enabled + except Exception: + pass + try: + active = subprocess.run( + ["systemctl", "is-active", "saikyo-avd.timer"], + capture_output=True, + text=True, + ).stdout.strip() or active + except Exception: + pass + + self.status.setText(f"protection: {enabled}/{active} | reports: {len(items)} | {utc_now()}") + if items: + self.table.selectRow(0) + else: + self.details.setPlainText("No reports yet. Use the buttons to generate reports.") + + def _selected(self): + rows = self.table.selectionModel().selectedRows() + if not rows: + return None + i = rows[0].row() + if i < 0 or i >= len(self.model.items): + return None + return self.model.items[i] + + def _on_select(self, *_): + it = self._selected() + if not it: + return + raw = it.get("raw") or {} + self.details.setPlainText(json.dumps(raw, ensure_ascii=False, indent=2)) + + self.det_list.clear() + self.btn_quarantine.setEnabled(False) + self.btn_delete.setEnabled(False) + + dets = [] + try: + dets = (raw.get("details") or {}).get("detections") or [] + except Exception: + dets = [] + + for d in dets: + p = d.get("path") + n = d.get("name") + if not p: + continue + item = QtWidgets.QListWidgetItem(f"{p} [{n or 'unknown'}]") + item.setData(QtCore.Qt.UserRole, {"path": p, "name": n}) + self.det_list.addItem(item) + + def _on_det_select(self): + sel = self.det_list.selectedItems() + ok = bool(sel) + self.btn_quarantine.setEnabled(ok) + self.btn_delete.setEnabled(ok) + + def _scan(self): + if self._scan_proc is not None: + QtWidgets.QMessageBox.information(self, "Scan running", "Scan is already running.") + return + + r = QtWidgets.QMessageBox.question( + self, + "Full system scan", + "Run full ClamAV scan of '/'? This may take a long time.\n\nProceed?", + ) + if r != QtWidgets.QMessageBox.Yes: + return + + self.scan_progress.setVisible(True) + self.btn_stop_scan.setEnabled(True) + self.scan_status.setText("Scanning… 0s") + self._scan_timer.start() + self._scan_tick.start() + + proc = QtCore.QProcess(self) + proc.setProgram("pkexec") + proc.setArguments(["/usr/sbin/saikyo-av-admin", "scan"]) + proc.setProcessChannelMode(QtCore.QProcess.MergedChannels) + + buf: list[str] = [] + + def on_ready(): + data = bytes(proc.readAllStandardOutput()).decode("utf-8", errors="replace") + if data: + buf.append(data) + + def on_finished(exit_code, _status): + self._scan_tick.stop() + self.scan_progress.setVisible(False) + self.btn_stop_scan.setEnabled(False) + + out = "".join(buf).strip() + self._scan_proc = None + self.scan_status.setText("") + + # Refresh reports list after scan. + self.refresh() + + if exit_code == 0: + if out: + QtWidgets.QMessageBox.information(self, "Scan complete", out) + else: + msg = out or "Scan failed" + QtWidgets.QMessageBox.warning(self, "Scan failed", msg) + + proc.readyReadStandardOutput.connect(on_ready) + proc.finished.connect(on_finished) + + self._scan_proc = proc + proc.start() + + def _selected_detection_path(self) -> str | None: + sel = self.det_list.selectedItems() + if not sel: + return None + data = sel[0].data(QtCore.Qt.UserRole) or {} + p = data.get("path") + if not p: + return None + return str(p) + + def _quarantine_selected(self): + p = self._selected_detection_path() + if not p: + return + r = QtWidgets.QMessageBox.question( + self, + "Quarantine file", + f"Move file to quarantine?\n\n{p}", + ) + if r != QtWidgets.QMessageBox.Yes: + return + self._run_admin_with_arg("quarantine", p) + + def _delete_selected(self): + p = self._selected_detection_path() + if not p: + return + r1 = QtWidgets.QMessageBox.warning( + self, + "Delete infected file", + f"This will permanently delete the file:\n\n{p}\n\nContinue?", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, + ) + if r1 != QtWidgets.QMessageBox.Yes: + return + r2 = QtWidgets.QMessageBox.warning( + self, + "Confirm delete", + "Are you absolutely sure? This cannot be undone.", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, + ) + if r2 != QtWidgets.QMessageBox.Yes: + return + self._run_admin_with_arg("delete", p) + + def _disable(self): + r = QtWidgets.QMessageBox.question( + self, + "Disable protection", + "Disable protection requires explicit operator consent. Action will be logged.", + ) + if r != QtWidgets.QMessageBox.Yes: + return + self._run_admin("disable-protection") + + def _run_admin(self, action: str): + cmd = ["pkexec", "/usr/sbin/saikyo-av-admin", action] + proc = subprocess.run(cmd, capture_output=True, text=True) + if proc.returncode == 0: + self.refresh() + out = (proc.stdout or "").strip() + if out: + QtWidgets.QMessageBox.information(self, "Done", out) + else: + msg = (proc.stderr or proc.stdout or "failed").strip() + QtWidgets.QMessageBox.warning(self, "Failed", msg) + + def _update_scan_elapsed(self): + if self._scan_proc is None: + return + ms = self._scan_timer.elapsed() + self.scan_status.setText(f"Scanning… {int(ms/1000)}s") + + def _stop_scan(self): + if self._scan_proc is None: + return + r = QtWidgets.QMessageBox.question( + self, + "Stop scan", + "Stop the running scan?", + ) + if r != QtWidgets.QMessageBox.Yes: + return + try: + self._scan_proc.kill() + except Exception: + pass + + def _run_admin_with_arg(self, action: str, arg: str): + cmd = ["pkexec", "/usr/sbin/saikyo-av-admin", action, arg] + proc = subprocess.run(cmd, capture_output=True, text=True) + if proc.returncode == 0: + self.refresh() + out = (proc.stdout or "").strip() + if out: + QtWidgets.QMessageBox.information(self, "Done", out) + else: + msg = (proc.stderr or proc.stdout or "failed").strip() + QtWidgets.QMessageBox.warning(self, "Failed", msg) + + +def main() -> int: + ap = argparse.ArgumentParser(prog="saikyo-av-gui") + ap.add_argument("--tray", action="store_true") + ap.add_argument("--reports-dir", default=REPORTS_DIR_DEFAULT) + args = ap.parse_args() + + app = QtWidgets.QApplication(sys.argv) + w = MainWindow(pathlib.Path(args.reports_dir), start_in_tray=args.tray) + w.show() + return app.exec_() + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/saikyo-av-gui/debian/.debhelper/generated/saikyo-av-gui/dh_installchangelogs.dch.trimmed b/saikyo-av-gui/debian/.debhelper/generated/saikyo-av-gui/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..a9f87ba --- /dev/null +++ b/saikyo-av-gui/debian/.debhelper/generated/saikyo-av-gui/dh_installchangelogs.dch.trimmed @@ -0,0 +1,5 @@ +saikyo-av-gui (0.1.4) stable; urgency=medium + + * Make scan asynchronous: progress indicator and stop button. + + -- SAIKYO OS Mon, 20 Jan 2026 17:40:00 +0000 diff --git a/saikyo-av-gui/debian/.debhelper/generated/saikyo-av-gui/installed-by-dh_install b/saikyo-av-gui/debian/.debhelper/generated/saikyo-av-gui/installed-by-dh_install new file mode 100644 index 0000000..636dcc8 --- /dev/null +++ b/saikyo-av-gui/debian/.debhelper/generated/saikyo-av-gui/installed-by-dh_install @@ -0,0 +1,6 @@ +./bin/saikyo-av-gui +./bin/saikyo-av-admin +./desktop/saikyo-av-gui.desktop +./autostart/saikyo-av-gui.desktop +./polkit/org.saikyo.av.admin.policy +./icons/saikyo-av.svg diff --git a/saikyo-av-gui/debian/.debhelper/generated/saikyo-av-gui/installed-by-dh_installdocs b/saikyo-av-gui/debian/.debhelper/generated/saikyo-av-gui/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-av-gui/debian/changelog b/saikyo-av-gui/debian/changelog new file mode 100644 index 0000000..a9f87ba --- /dev/null +++ b/saikyo-av-gui/debian/changelog @@ -0,0 +1,5 @@ +saikyo-av-gui (0.1.4) stable; urgency=medium + + * Make scan asynchronous: progress indicator and stop button. + + -- SAIKYO OS Mon, 20 Jan 2026 17:40:00 +0000 diff --git a/saikyo-av-gui/debian/control b/saikyo-av-gui/debian/control new file mode 100644 index 0000000..1d38b3f --- /dev/null +++ b/saikyo-av-gui/debian/control @@ -0,0 +1,14 @@ +Source: saikyo-av-gui +Section: admin +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-av-gui +Architecture: all +Depends: ${misc:Depends}, python3, python3-pyqt5, xdg-utils, polkitd, pkexec +Recommends: saikyo-audit-report +Description: Saikyo Antivirus desktop admin panel (local) + Native desktop UI (system tray) for Saikyo Antivirus and registry verification actions. diff --git a/saikyo-av-gui/debian/copyright b/saikyo-av-gui/debian/copyright new file mode 100644 index 0000000..10e3619 --- /dev/null +++ b/saikyo-av-gui/debian/copyright @@ -0,0 +1,26 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-av-gui +Source: https://saikyo-os.ru/ + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-av-gui/debian/debhelper-build-stamp b/saikyo-av-gui/debian/debhelper-build-stamp new file mode 100644 index 0000000..e727704 --- /dev/null +++ b/saikyo-av-gui/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-av-gui diff --git a/saikyo-av-gui/debian/files b/saikyo-av-gui/debian/files new file mode 100644 index 0000000..10d8d80 --- /dev/null +++ b/saikyo-av-gui/debian/files @@ -0,0 +1,2 @@ +saikyo-av-gui_0.1.4_all.deb admin optional +saikyo-av-gui_0.1.4_amd64.buildinfo admin optional diff --git a/saikyo-av-gui/debian/install b/saikyo-av-gui/debian/install new file mode 100644 index 0000000..1f9e26a --- /dev/null +++ b/saikyo-av-gui/debian/install @@ -0,0 +1,6 @@ +bin/saikyo-av-gui usr/bin/ +bin/saikyo-av-admin usr/sbin/ +desktop/saikyo-av-gui.desktop usr/share/applications/ +autostart/saikyo-av-gui.desktop etc/xdg/autostart/ +polkit/org.saikyo.av.admin.policy usr/share/polkit-1/actions/ +icons/saikyo-av.svg usr/share/icons/hicolor/scalable/apps/ diff --git a/saikyo-av-gui/debian/rules b/saikyo-av-gui/debian/rules new file mode 100755 index 0000000..3fc6ab5 --- /dev/null +++ b/saikyo-av-gui/debian/rules @@ -0,0 +1,16 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: + +override_dh_fixperms: + dh_fixperms + chmod 0755 debian/saikyo-av-gui/usr/bin/saikyo-av-gui + chmod 0755 debian/saikyo-av-gui/usr/sbin/saikyo-av-admin + chmod 0755 debian/saikyo-av-gui.preinst + chmod 0755 debian/saikyo-av-gui.postinst + chmod 0755 debian/saikyo-av-gui.prerm diff --git a/saikyo-av-gui/debian/saikyo-av-gui.debhelper.log b/saikyo-av-gui/debian/saikyo-av-gui.debhelper.log new file mode 100644 index 0000000..93c5512 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui.debhelper.log @@ -0,0 +1 @@ +dh_fixperms diff --git a/saikyo-av-gui/debian/saikyo-av-gui.postinst b/saikyo-av-gui/debian/saikyo-av-gui.postinst new file mode 100755 index 0000000..079fb0f --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui.postinst @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +case "$1" in + configure) + mkdir -p /var/lib/saikyo-av/reports /var/log/saikyo-av || true + chmod 0755 /var/lib/saikyo-av /var/lib/saikyo-av/reports /var/log/saikyo-av 2>/dev/null || true + ;; +esac + +exit 0 diff --git a/saikyo-av-gui/debian/saikyo-av-gui.preinst b/saikyo-av-gui/debian/saikyo-av-gui.preinst new file mode 100755 index 0000000..871f878 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui.preinst @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Fixup for packaging mistake (0.1.0/0.1.1) where dh_install created directories: +# /usr/sbin/saikyo-av-admin/saikyo-av-admin +# /etc/xdg/autostart/saikyo-av-gui.desktop/saikyo-av-gui.desktop +# Remove them before unpacking corrected package. + +if [[ "$1" == "upgrade" || "$1" == "install" ]]; then + for d in \ + /usr/sbin/saikyo-av-admin \ + /etc/xdg/autostart/saikyo-av-gui.desktop + do + if [[ -d "$d" ]]; then + rm -rf "$d" 2>/dev/null || true + fi + done +fi + +exit 0 diff --git a/saikyo-av-gui/debian/saikyo-av-gui.prerm b/saikyo-av-gui/debian/saikyo-av-gui.prerm new file mode 100755 index 0000000..2a53d75 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui.prerm @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exit 0 diff --git a/saikyo-av-gui/debian/saikyo-av-gui.substvars b/saikyo-av-gui/debian/saikyo-av-gui.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/conffiles b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/conffiles new file mode 100644 index 0000000..f21dd9d --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/conffiles @@ -0,0 +1 @@ +/etc/xdg/autostart/saikyo-av-gui.desktop diff --git a/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/control b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/control new file mode 100644 index 0000000..6bddac3 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/control @@ -0,0 +1,11 @@ +Package: saikyo-av-gui +Version: 0.1.4 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 59 +Depends: python3, python3-pyqt5, xdg-utils, polkitd, pkexec +Recommends: saikyo-audit-report +Section: admin +Priority: optional +Description: Saikyo Antivirus desktop admin panel (local) + Native desktop UI (system tray) for Saikyo Antivirus and registry verification actions. diff --git a/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/md5sums b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/md5sums new file mode 100644 index 0000000..1b9e6fb --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/md5sums @@ -0,0 +1,7 @@ +df448873f75684e84718315a3036d104 usr/bin/saikyo-av-gui +2cf2d7c3465f90e05e9e3c34a8d1ede2 usr/sbin/saikyo-av-admin +5bdebcd69cfeabc37b34f22edb72c851 usr/share/applications/saikyo-av-gui.desktop +390713466d3468f6733a8e1b3f20a231 usr/share/doc/saikyo-av-gui/changelog.gz +7b9b58e66ef873f2c38b3a148fa3e513 usr/share/doc/saikyo-av-gui/copyright +5161807a723b0a180d3d0e29fee68753 usr/share/icons/hicolor/scalable/apps/saikyo-av.svg +1522d63a46b6d96da78f1742361b6b8b usr/share/polkit-1/actions/org.saikyo.av.admin.policy diff --git a/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/postinst b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/postinst new file mode 100755 index 0000000..079fb0f --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/postinst @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +case "$1" in + configure) + mkdir -p /var/lib/saikyo-av/reports /var/log/saikyo-av || true + chmod 0755 /var/lib/saikyo-av /var/lib/saikyo-av/reports /var/log/saikyo-av 2>/dev/null || true + ;; +esac + +exit 0 diff --git a/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/preinst b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/preinst new file mode 100755 index 0000000..871f878 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/preinst @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Fixup for packaging mistake (0.1.0/0.1.1) where dh_install created directories: +# /usr/sbin/saikyo-av-admin/saikyo-av-admin +# /etc/xdg/autostart/saikyo-av-gui.desktop/saikyo-av-gui.desktop +# Remove them before unpacking corrected package. + +if [[ "$1" == "upgrade" || "$1" == "install" ]]; then + for d in \ + /usr/sbin/saikyo-av-admin \ + /etc/xdg/autostart/saikyo-av-gui.desktop + do + if [[ -d "$d" ]]; then + rm -rf "$d" 2>/dev/null || true + fi + done +fi + +exit 0 diff --git a/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/prerm b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/prerm new file mode 100755 index 0000000..2a53d75 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/prerm @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exit 0 diff --git a/saikyo-av-gui/debian/saikyo-av-gui/etc/xdg/autostart/saikyo-av-gui.desktop b/saikyo-av-gui/debian/saikyo-av-gui/etc/xdg/autostart/saikyo-av-gui.desktop new file mode 100644 index 0000000..d01cb75 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/etc/xdg/autostart/saikyo-av-gui.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Saikyo Antivirus (Tray) +Comment=Start Saikyo Antivirus tray app +Exec=/usr/bin/saikyo-av-gui --tray +Icon=saikyo-av +Terminal=false +X-GNOME-Autostart-enabled=true diff --git a/saikyo-av-gui/debian/saikyo-av-gui/usr/bin/saikyo-av-gui b/saikyo-av-gui/debian/saikyo-av-gui/usr/bin/saikyo-av-gui new file mode 100755 index 0000000..ef60e07 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/usr/bin/saikyo-av-gui @@ -0,0 +1,532 @@ +#!/usr/bin/env python3 +import argparse +import json +import pathlib +import subprocess +import sys +from datetime import datetime, timezone + +from PyQt5 import QtCore, QtGui, QtWidgets + +REPORTS_DIR_DEFAULT = "/var/lib/saikyo-av/reports" + + +def utc_now() -> str: + return datetime.now(tz=timezone.utc).isoformat(timespec="seconds") + + +def load_reports(reports_dir: pathlib.Path): + items = [] + if not reports_dir.exists(): + return items + for p in sorted(reports_dir.glob("*.json"), key=lambda x: x.stat().st_mtime, reverse=True): + try: + data = json.loads(p.read_text(encoding="utf-8")) + except Exception: + data = {} + items.append( + { + "id": p.stem, + "path": str(p), + "summary": data.get("summary") or "(no summary)", + "severity": data.get("severity") or "unknown", + "created_utc": data.get("created_utc") or "", + "raw": data, + } + ) + return items + + +class ReportsModel(QtCore.QAbstractTableModel): + def __init__(self): + super().__init__() + self.items = [] + + def rowCount(self, parent=QtCore.QModelIndex()): + return len(self.items) + + def columnCount(self, parent=QtCore.QModelIndex()): + return 3 + + def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole): + if role != QtCore.Qt.DisplayRole: + return None + if orientation == QtCore.Qt.Horizontal: + return ["Summary", "Severity", "Created (UTC)"][section] + return str(section + 1) + + def data(self, index, role=QtCore.Qt.DisplayRole): + if not index.isValid(): + return None + item = self.items[index.row()] + col = index.column() + + if role == QtCore.Qt.DisplayRole: + if col == 0: + return item.get("summary") + if col == 1: + return item.get("severity") + if col == 2: + return item.get("created_utc") + + if role == QtCore.Qt.ForegroundRole and col == 1: + sev = (item.get("severity") or "").lower() + if sev in {"ok", "info", "low"}: + return QtGui.QBrush(QtGui.QColor("#2dd4bf")) + if sev in {"medium", "warn", "warning"}: + return QtGui.QBrush(QtGui.QColor("#fbbf24")) + if sev in {"high", "critical", "bad"}: + return QtGui.QBrush(QtGui.QColor("#fb7185")) + return QtGui.QBrush(QtGui.QColor("#9bb0d1")) + + return None + + def set_items(self, items): + self.beginResetModel() + self.items = items + self.endResetModel() + + +class MainWindow(QtWidgets.QMainWindow): + def __init__(self, reports_dir: pathlib.Path, start_in_tray: bool): + super().__init__() + self.reports_dir = reports_dir + self.start_in_tray = start_in_tray + + self.setWindowTitle("Saikyo Antivirus") + self.setWindowIcon(QtGui.QIcon.fromTheme("saikyo-av", QtGui.QIcon.fromTheme("security-high"))) + self.setMinimumSize(980, 620) + + self.model = ReportsModel() + self.table = QtWidgets.QTableView() + self.table.setModel(self.model) + self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) + self.table.horizontalHeader().setStretchLastSection(True) + self.table.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) + self.table.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) + self.table.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) + + self.details = QtWidgets.QPlainTextEdit() + self.details.setReadOnly(True) + self.details.setFont(QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)) + + self.det_list = QtWidgets.QListWidget() + self.det_list.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) + + self.btn_quarantine = QtWidgets.QPushButton("Quarantine") + self.btn_delete = QtWidgets.QPushButton("Delete") + for b in [self.btn_quarantine, self.btn_delete]: + b.setCursor(QtCore.Qt.PointingHandCursor) + b.setMinimumHeight(32) + self.btn_delete.setStyleSheet("background:#2b1420;color:#ffd4df;border:1px solid #5b2031;") + self.btn_quarantine.setEnabled(False) + self.btn_delete.setEnabled(False) + + self.scan_status = QtWidgets.QLabel("") + self.scan_status.setStyleSheet("color:#9bb0d1") + self.scan_progress = QtWidgets.QProgressBar() + self.scan_progress.setRange(0, 0) + self.scan_progress.setVisible(False) + self.btn_stop_scan = QtWidgets.QPushButton("Stop scan") + self.btn_stop_scan.setCursor(QtCore.Qt.PointingHandCursor) + self.btn_stop_scan.setMinimumHeight(32) + self.btn_stop_scan.setEnabled(False) + + self._scan_proc: QtCore.QProcess | None = None + self._scan_timer = QtCore.QElapsedTimer() + self._scan_tick = QtCore.QTimer(self) + self._scan_tick.setInterval(500) + self._scan_tick.timeout.connect(self._update_scan_elapsed) + + self.status = QtWidgets.QLabel("local") + self.status.setStyleSheet("color:#9bb0d1") + + btn_evidence = QtWidgets.QPushButton("Evidence") + btn_audit = QtWidgets.QPushButton("Audit") + btn_collect = QtWidgets.QPushButton("Collect") + btn_scan = QtWidgets.QPushButton("SCAN") + btn_enable = QtWidgets.QPushButton("Enable") + btn_disable = QtWidgets.QPushButton("Disable…") + btn_refresh = QtWidgets.QPushButton("Refresh") + + for b in [btn_evidence, btn_audit, btn_collect, btn_scan, btn_enable, btn_disable, btn_refresh]: + b.setCursor(QtCore.Qt.PointingHandCursor) + b.setMinimumHeight(36) + + btn_scan.setStyleSheet("background:rgba(45,212,191,0.18);border:1px solid rgba(45,212,191,0.35);") + + btn_disable.setStyleSheet("background:#2b1420;color:#ffd4df;border:1px solid #5b2031;") + + left = QtWidgets.QFrame() + left.setFrameShape(QtWidgets.QFrame.StyledPanel) + ll = QtWidgets.QVBoxLayout(left) + ll.setContentsMargins(12, 12, 12, 12) + ll.setSpacing(10) + + title = QtWidgets.QLabel("Admin checks") + title.setStyleSheet("font-weight:800;font-size:16px") + hint = QtWidgets.QLabel("Кнопки запускают проверки с подтверждением (pkexec).") + hint.setWordWrap(True) + hint.setStyleSheet("color:#9bb0d1;font-size:12px") + + ll.addWidget(title) + ll.addWidget(hint) + ll.addSpacing(4) + ll.addWidget(btn_evidence) + ll.addWidget(btn_audit) + ll.addWidget(btn_collect) + ll.addSpacing(8) + ll.addWidget(btn_scan) + ll.addSpacing(8) + ll.addWidget(btn_enable) + ll.addWidget(btn_disable) + ll.addStretch(1) + ll.addWidget(btn_refresh) + ll.addWidget(self.status) + + splitter = QtWidgets.QSplitter() + splitter.setOrientation(QtCore.Qt.Vertical) + top = QtWidgets.QWidget() + tl = QtWidgets.QVBoxLayout(top) + tl.setContentsMargins(0, 0, 0, 0) + tl.addWidget(self.table) + splitter.addWidget(top) + details_box = QtWidgets.QWidget() + dl = QtWidgets.QVBoxLayout(details_box) + dl.setContentsMargins(0, 0, 0, 0) + dl.setSpacing(8) + + det_head = QtWidgets.QHBoxLayout() + det_title = QtWidgets.QLabel("Detections") + det_title.setStyleSheet("font-weight:800;") + det_head.addWidget(det_title) + det_head.addStretch(1) + det_head.addWidget(self.scan_status) + det_head.addWidget(self.scan_progress) + det_head.addWidget(self.btn_stop_scan) + det_head.addWidget(self.btn_quarantine) + det_head.addWidget(self.btn_delete) + dl.addLayout(det_head) + dl.addWidget(self.det_list) + + raw_title = QtWidgets.QLabel("Report JSON") + raw_title.setStyleSheet("font-weight:800;") + dl.addWidget(raw_title) + dl.addWidget(self.details) + + splitter.addWidget(details_box) + splitter.setStretchFactor(0, 2) + splitter.setStretchFactor(1, 1) + + central = QtWidgets.QWidget() + root = QtWidgets.QHBoxLayout(central) + root.setContentsMargins(16, 16, 16, 16) + root.setSpacing(12) + left.setFixedWidth(300) + root.addWidget(left) + root.addWidget(splitter) + self.setCentralWidget(central) + + self.setStyleSheet( + "QMainWindow{background:#0b1220;color:#e7eefc;}" + "QFrame{background:#0f1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}" + "QTableView{background:#0f1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}" + "QHeaderView::section{background:#0c1628;color:#9bb0d1;border:none;padding:8px;font-weight:700;}" + "QPushButton{background:rgba(96,165,250,0.15);border:1px solid rgba(96,165,250,0.35);border-radius:10px;padding:8px 10px;font-weight:700;}" + "QPushButton:hover{background:rgba(96,165,250,0.22);}" + "QPlainTextEdit{background:#0c1628;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}" + ) + + self.table.selectionModel().selectionChanged.connect(self._on_select) + + btn_refresh.clicked.connect(self.refresh) + btn_evidence.clicked.connect(lambda: self._run_admin("run-evidence")) + btn_audit.clicked.connect(lambda: self._run_admin("run-audit")) + btn_collect.clicked.connect(lambda: self._run_admin("collect-artifacts")) + btn_scan.clicked.connect(self._scan) + btn_enable.clicked.connect(lambda: self._run_admin("enable-protection")) + btn_disable.clicked.connect(self._disable) + + self.btn_quarantine.clicked.connect(self._quarantine_selected) + self.btn_delete.clicked.connect(self._delete_selected) + self.det_list.itemSelectionChanged.connect(self._on_det_select) + self.btn_stop_scan.clicked.connect(self._stop_scan) + + self._setup_tray() + self.refresh() + if self.start_in_tray: + QtCore.QTimer.singleShot(50, self.hide) + + def _setup_tray(self): + self.tray = QtWidgets.QSystemTrayIcon(self.windowIcon(), self) + menu = QtWidgets.QMenu() + act_open = menu.addAction("Open") + act_refresh = menu.addAction("Refresh") + menu.addSeparator() + act_quit = menu.addAction("Quit") + act_open.triggered.connect(self._show) + act_refresh.triggered.connect(self.refresh) + act_quit.triggered.connect(QtWidgets.QApplication.quit) + self.tray.setContextMenu(menu) + self.tray.setToolTip("Saikyo Antivirus") + self.tray.activated.connect(lambda r: self._show() if r == QtWidgets.QSystemTrayIcon.Trigger else None) + self.tray.show() + + def _show(self): + self.showNormal() + self.raise_() + self.activateWindow() + + def closeEvent(self, event: QtGui.QCloseEvent): + event.ignore() + self.hide() + self.tray.showMessage( + "Saikyo Antivirus", + "Свернуто в трей. Откройте из значка.", + QtWidgets.QSystemTrayIcon.Information, + 2000, + ) + + def refresh(self): + self.reports_dir.mkdir(parents=True, exist_ok=True) + items = load_reports(self.reports_dir) + self.model.set_items(items) + # systemctl status doesn't require root for is-enabled/is-active. + enabled = "unknown" + active = "unknown" + try: + enabled = subprocess.run( + ["systemctl", "is-enabled", "saikyo-avd.timer"], + capture_output=True, + text=True, + ).stdout.strip() or enabled + except Exception: + pass + try: + active = subprocess.run( + ["systemctl", "is-active", "saikyo-avd.timer"], + capture_output=True, + text=True, + ).stdout.strip() or active + except Exception: + pass + + self.status.setText(f"protection: {enabled}/{active} | reports: {len(items)} | {utc_now()}") + if items: + self.table.selectRow(0) + else: + self.details.setPlainText("No reports yet. Use the buttons to generate reports.") + + def _selected(self): + rows = self.table.selectionModel().selectedRows() + if not rows: + return None + i = rows[0].row() + if i < 0 or i >= len(self.model.items): + return None + return self.model.items[i] + + def _on_select(self, *_): + it = self._selected() + if not it: + return + raw = it.get("raw") or {} + self.details.setPlainText(json.dumps(raw, ensure_ascii=False, indent=2)) + + self.det_list.clear() + self.btn_quarantine.setEnabled(False) + self.btn_delete.setEnabled(False) + + dets = [] + try: + dets = (raw.get("details") or {}).get("detections") or [] + except Exception: + dets = [] + + for d in dets: + p = d.get("path") + n = d.get("name") + if not p: + continue + item = QtWidgets.QListWidgetItem(f"{p} [{n or 'unknown'}]") + item.setData(QtCore.Qt.UserRole, {"path": p, "name": n}) + self.det_list.addItem(item) + + def _on_det_select(self): + sel = self.det_list.selectedItems() + ok = bool(sel) + self.btn_quarantine.setEnabled(ok) + self.btn_delete.setEnabled(ok) + + def _scan(self): + if self._scan_proc is not None: + QtWidgets.QMessageBox.information(self, "Scan running", "Scan is already running.") + return + + r = QtWidgets.QMessageBox.question( + self, + "Full system scan", + "Run full ClamAV scan of '/'? This may take a long time.\n\nProceed?", + ) + if r != QtWidgets.QMessageBox.Yes: + return + + self.scan_progress.setVisible(True) + self.btn_stop_scan.setEnabled(True) + self.scan_status.setText("Scanning… 0s") + self._scan_timer.start() + self._scan_tick.start() + + proc = QtCore.QProcess(self) + proc.setProgram("pkexec") + proc.setArguments(["/usr/sbin/saikyo-av-admin", "scan"]) + proc.setProcessChannelMode(QtCore.QProcess.MergedChannels) + + buf: list[str] = [] + + def on_ready(): + data = bytes(proc.readAllStandardOutput()).decode("utf-8", errors="replace") + if data: + buf.append(data) + + def on_finished(exit_code, _status): + self._scan_tick.stop() + self.scan_progress.setVisible(False) + self.btn_stop_scan.setEnabled(False) + + out = "".join(buf).strip() + self._scan_proc = None + self.scan_status.setText("") + + # Refresh reports list after scan. + self.refresh() + + if exit_code == 0: + if out: + QtWidgets.QMessageBox.information(self, "Scan complete", out) + else: + msg = out or "Scan failed" + QtWidgets.QMessageBox.warning(self, "Scan failed", msg) + + proc.readyReadStandardOutput.connect(on_ready) + proc.finished.connect(on_finished) + + self._scan_proc = proc + proc.start() + + def _selected_detection_path(self) -> str | None: + sel = self.det_list.selectedItems() + if not sel: + return None + data = sel[0].data(QtCore.Qt.UserRole) or {} + p = data.get("path") + if not p: + return None + return str(p) + + def _quarantine_selected(self): + p = self._selected_detection_path() + if not p: + return + r = QtWidgets.QMessageBox.question( + self, + "Quarantine file", + f"Move file to quarantine?\n\n{p}", + ) + if r != QtWidgets.QMessageBox.Yes: + return + self._run_admin_with_arg("quarantine", p) + + def _delete_selected(self): + p = self._selected_detection_path() + if not p: + return + r1 = QtWidgets.QMessageBox.warning( + self, + "Delete infected file", + f"This will permanently delete the file:\n\n{p}\n\nContinue?", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, + ) + if r1 != QtWidgets.QMessageBox.Yes: + return + r2 = QtWidgets.QMessageBox.warning( + self, + "Confirm delete", + "Are you absolutely sure? This cannot be undone.", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, + ) + if r2 != QtWidgets.QMessageBox.Yes: + return + self._run_admin_with_arg("delete", p) + + def _disable(self): + r = QtWidgets.QMessageBox.question( + self, + "Disable protection", + "Disable protection requires explicit operator consent. Action will be logged.", + ) + if r != QtWidgets.QMessageBox.Yes: + return + self._run_admin("disable-protection") + + def _run_admin(self, action: str): + cmd = ["pkexec", "/usr/sbin/saikyo-av-admin", action] + proc = subprocess.run(cmd, capture_output=True, text=True) + if proc.returncode == 0: + self.refresh() + out = (proc.stdout or "").strip() + if out: + QtWidgets.QMessageBox.information(self, "Done", out) + else: + msg = (proc.stderr or proc.stdout or "failed").strip() + QtWidgets.QMessageBox.warning(self, "Failed", msg) + + def _update_scan_elapsed(self): + if self._scan_proc is None: + return + ms = self._scan_timer.elapsed() + self.scan_status.setText(f"Scanning… {int(ms/1000)}s") + + def _stop_scan(self): + if self._scan_proc is None: + return + r = QtWidgets.QMessageBox.question( + self, + "Stop scan", + "Stop the running scan?", + ) + if r != QtWidgets.QMessageBox.Yes: + return + try: + self._scan_proc.kill() + except Exception: + pass + + def _run_admin_with_arg(self, action: str, arg: str): + cmd = ["pkexec", "/usr/sbin/saikyo-av-admin", action, arg] + proc = subprocess.run(cmd, capture_output=True, text=True) + if proc.returncode == 0: + self.refresh() + out = (proc.stdout or "").strip() + if out: + QtWidgets.QMessageBox.information(self, "Done", out) + else: + msg = (proc.stderr or proc.stdout or "failed").strip() + QtWidgets.QMessageBox.warning(self, "Failed", msg) + + +def main() -> int: + ap = argparse.ArgumentParser(prog="saikyo-av-gui") + ap.add_argument("--tray", action="store_true") + ap.add_argument("--reports-dir", default=REPORTS_DIR_DEFAULT) + args = ap.parse_args() + + app = QtWidgets.QApplication(sys.argv) + w = MainWindow(pathlib.Path(args.reports_dir), start_in_tray=args.tray) + w.show() + return app.exec_() + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/saikyo-av-gui/debian/saikyo-av-gui/usr/sbin/saikyo-av-admin b/saikyo-av-gui/debian/saikyo-av-gui/usr/sbin/saikyo-av-admin new file mode 100755 index 0000000..f2e1b07 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/usr/sbin/saikyo-av-admin @@ -0,0 +1,323 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPORTS_DIR="/var/lib/saikyo-av/reports" +LOG_DIR="/var/log/saikyo-av" +QUAR_DIR="/var/lib/saikyo-av/quarantine" + +mkdir -p "${REPORTS_DIR}" "${LOG_DIR}" "${QUAR_DIR}" || true +chmod 0755 /var/lib/saikyo-av "${REPORTS_DIR}" "${LOG_DIR}" 2>/dev/null || true + +cmd="${1:-}" +shift || true + +utc_ts() { + date -u +%Y-%m-%dT%H:%M:%SZ +} + +write_report() { + local id="$1" + local summary="$2" + local severity="$3" + local outfile="${REPORTS_DIR}/${id}.json" + local created + created="$(utc_ts)" + + # shellcheck disable=SC2129 + { + echo "{" + echo " \"created_utc\": \"${created}\"," + echo " \"severity\": \"${severity}\"," + echo " \"summary\": \"${summary}\"," + echo " \"details\": {" + echo " \"action\": \"${cmd}\"," + echo " \"argv\": \"$*\"" + echo " }," + echo " \"artifacts\": {}," + echo " \"suggested_fixes\": []" + echo "}" + } > "${outfile}" + + echo "${outfile}" +} + +run_to_file() { + local out="$1" + shift + ("$@" 2>&1 || true) | sed -e 's/\r$//' > "${out}" +} + +json_escape() { + # Minimal JSON string escape (no unicode handling needed for our file paths) + sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\t/\\t/g' -e 's/\r/\\r/g' -e 's/\n/\\n/g' +} + +write_scan_report() { + local id="$1" + local summary="$2" + local severity="$3" + local log_file="$4" + local detections_tsv="$5" + local outfile="${REPORTS_DIR}/${id}.json" + local created + created="$(utc_ts)" + + local scanned_count infected_count + scanned_count="$(grep -E '^Scanned files:' "${log_file}" 2>/dev/null | awk -F': ' '{print $2}' | tail -n 1 || true)" + infected_count="$(grep -E '^Infected files:' "${log_file}" 2>/dev/null | awk -F': ' '{print $2}' | tail -n 1 || true)" + scanned_count="${scanned_count:-0}" + infected_count="${infected_count:-0}" + + { + echo "{" + echo " \"created_utc\": \"${created}\"," + echo " \"severity\": \"${severity}\"," + echo " \"summary\": \"${summary}\"," + echo " \"details\": {" + echo " \"action\": \"${cmd}\"," + echo " \"argv\": \"$*\"," + echo " \"scanned_files\": ${scanned_count}," + echo " \"infected_files\": ${infected_count}," + echo " \"detections\": [" + + if [[ -s "${detections_tsv}" ]]; then + first=1 + while IFS=$'\t' read -r pth sig; do + [[ -n "${pth}" ]] || continue + pth_esc="$(printf '%s' "${pth}" | json_escape)" + sig_esc="$(printf '%s' "${sig}" | json_escape)" + if [[ "${first}" -eq 1 ]]; then + first=0 + else + echo "," + fi + printf ' {"path":"%s","name":"%s"}' "${pth_esc}" "${sig_esc}" + done < "${detections_tsv}" + echo + fi + + echo " ]" + echo " }," + echo " \"artifacts\": {" + echo " \"clamav_log\": \"${log_file}\"," + echo " \"detections_tsv\": \"${detections_tsv}\"" + echo " }," + echo " \"suggested_fixes\": [" + echo " {\"id\":\"quarantine\",\"title\":\"Quarantine infected file\",\"description\":\"Move selected file to local quarantine.\",\"requires_consent\":true}," + echo " {\"id\":\"delete\",\"title\":\"Delete infected file\",\"description\":\"Delete selected infected file (dangerous).\",\"requires_consent\":true}" + echo " ]" + echo "}" + } > "${outfile}" + + echo "${outfile}" +} + +svc_is_enabled() { + systemctl is-enabled saikyo-avd.timer 2>/dev/null || true +} + +svc_is_active() { + systemctl is-active saikyo-avd.timer 2>/dev/null || true +} + +case "${cmd}" in + health) + echo "ok" + ;; + + status-protection) + echo "enabled=$(svc_is_enabled) active=$(svc_is_active)" + ;; + + run-evidence) + outdir="${REPORTS_DIR}/artifacts" + mkdir -p "${outdir}" || true + ts="$(date -u +%Y%m%dT%H%M%SZ)" + evidence_out="${outdir}/saikyo-evidence-${ts}.log" + + if command -v saikyo-evidence >/dev/null 2>&1; then + run_to_file "${evidence_out}" saikyo-evidence + elif [ -x /usr/bin/saikyo-evidence ]; then + run_to_file "${evidence_out}" /usr/bin/saikyo-evidence + else + echo "saikyo-evidence not found" > "${evidence_out}" + fi + + rpt="$(write_report "evidence-${ts}" "Evidence report generated" "info" "${cmd}")" + # Append artifacts into report JSON (minimal, without jq dependency). + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"evidence_log\": \"${evidence_out}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + scan) + outdir="${REPORTS_DIR}/artifacts" + mkdir -p "${outdir}" || true + ts="$(date -u +%Y%m%dT%H%M%SZ)" + log_file="${outdir}/clamav-scan-${ts}.log" + det_tsv="${outdir}/clamav-detections-${ts}.tsv" + + if ! command -v clamscan >/dev/null 2>&1; then + echo "clamscan not found (install clamav)" > "${log_file}" + rpt="$(write_report "scan-${ts}" "ClamAV scan failed: clamscan not found" "warn" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"clamav_log\": \"${log_file}\"}#" "${rpt}" || true + echo "${rpt}" + exit 1 + fi + + # Full scan of / with exclusions. + # - exclude hidden paths (/.*/) + # - exclude virtual/system dirs + # - keep a log we can parse + ( + clamscan \ + -r \ + --infected \ + --no-summary \ + --exclude-dir='^/proc/' \ + --exclude-dir='^/sys/' \ + --exclude-dir='^/dev/' \ + --exclude-dir='^/run/' \ + --exclude-dir='^/tmp/' \ + --exclude-dir='/.*/' \ + / \ + 2>&1 + echo + clamscan -r --infected --exclude-dir='^/proc/' --exclude-dir='^/sys/' --exclude-dir='^/dev/' --exclude-dir='^/run/' --exclude-dir='^/tmp/' --exclude-dir='/.*/' / --summary 2>/dev/null || true + ) | sed -e 's/\r$//' > "${log_file}" + + : > "${det_tsv}" + # Parse "PATH: SIGNATURE FOUND" lines. + grep -E ' FOUND$' "${log_file}" | sed -E 's/: (.+) FOUND$//;t;d' >/dev/null 2>&1 || true + while IFS= read -r line; do + # Example: /path/file: Eicar-Test-Signature FOUND + pth="${line%%:*}" + rest="${line#*: }" + sig="${rest% FOUND}" + printf '%s\t%s\n' "${pth}" "${sig}" >> "${det_tsv}" + done < <(grep -E ' FOUND$' "${log_file}" || true) + + infected_count="$(wc -l < "${det_tsv}" 2>/dev/null || echo 0)" + severity="info" + summary="ClamAV scan completed: no threats" + if [[ "${infected_count}" -gt 0 ]]; then + severity="bad" + summary="ClamAV scan completed: ${infected_count} threat(s) found" + fi + + rpt="$(write_scan_report "scan-${ts}" "${summary}" "${severity}" "${log_file}" "${det_tsv}" "${cmd}")" + echo "${rpt}" + ;; + + quarantine) + target="${1:-}" + if [[ -z "${target}" ]]; then + echo "missing file path" >&2 + exit 2 + fi + ts="$(date -u +%Y%m%dT%H%M%SZ)" + bn="$(basename -- "${target}" 2>/dev/null || echo file)" + dest="${QUAR_DIR}/${ts}-${bn}" + if [[ ! -f "${target}" ]]; then + rpt="$(write_report "quarantine-${ts}" "Quarantine failed: file not found" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + fi + mv -f -- "${target}" "${dest}" 2>>"${LOG_DIR}/saikyo-av-admin.log" || { + rpt="$(write_report "quarantine-${ts}" "Quarantine failed: move error" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + } + rpt="$(write_report "quarantine-${ts}" "File quarantined" "warn" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"from\": \"${target}\", \"to\": \"${dest}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + delete) + target="${1:-}" + if [[ -z "${target}" ]]; then + echo "missing file path" >&2 + exit 2 + fi + ts="$(date -u +%Y%m%dT%H%M%SZ)" + if [[ ! -e "${target}" ]]; then + rpt="$(write_report "delete-${ts}" "Delete failed: path not found" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + fi + rm -rf -- "${target}" 2>>"${LOG_DIR}/saikyo-av-admin.log" || { + rpt="$(write_report "delete-${ts}" "Delete failed" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + } + rpt="$(write_report "delete-${ts}" "File deleted" "warn" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"path\": \"${target}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + run-audit) + outdir="${REPORTS_DIR}/artifacts" + mkdir -p "${outdir}" || true + ts="$(date -u +%Y%m%dT%H%M%SZ)" + audit_out="${outdir}/saikyo-audit-report-${ts}.txt" + + if command -v saikyo-audit-report >/dev/null 2>&1; then + run_to_file "${audit_out}" saikyo-audit-report + else + echo "saikyo-audit-report not installed" > "${audit_out}" + fi + + rpt="$(write_report "audit-${ts}" "Audit report executed" "info" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"audit_stdout\": \"${audit_out}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + collect-artifacts) + outdir="${REPORTS_DIR}/artifacts" + mkdir -p "${outdir}" || true + ts="$(date -u +%Y%m%dT%H%M%SZ)" + collect_out="${outdir}/collect-artifacts-${ts}.log" + + if [ -x /usr/share/saikyo-os/forensics/collect-artifacts.sh ]; then + run_to_file "${collect_out}" /usr/share/saikyo-os/forensics/collect-artifacts.sh + else + echo "collect-artifacts.sh not found" > "${collect_out}" + fi + + rpt="$(write_report "collect-${ts}" "Artifacts collection executed" "info" "${cmd}")" + sed -i "s#\"artifacts\": {}#\"artifacts\": {\"collector_log\": \"${collect_out}\"}#" "${rpt}" || true + echo "${rpt}" + ;; + + enable-protection) + ts="$(date -u +%Y%m%dT%H%M%SZ)" + (systemctl daemon-reload 2>&1 || true) >> "${LOG_DIR}/saikyo-av-admin.log" || true + if systemctl enable --now saikyo-avd.timer >/dev/null 2>&1; then + rpt="$(write_report "enable-${ts}" "Protection enabled" "info" "${cmd}")" + echo "${rpt}" + exit 0 + else + rpt="$(write_report "enable-${ts}" "Protection enable failed" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + fi + ;; + + disable-protection) + ts="$(date -u +%Y%m%dT%H%M%SZ)" + (systemctl daemon-reload 2>&1 || true) >> "${LOG_DIR}/saikyo-av-admin.log" || true + if systemctl disable --now saikyo-avd.timer >/dev/null 2>&1; then + rpt="$(write_report "disable-${ts}" "Protection disabled" "warn" "${cmd}")" + echo "${rpt}" + exit 0 + else + rpt="$(write_report "disable-${ts}" "Protection disable failed" "warn" "${cmd}")" + echo "${rpt}" + exit 1 + fi + ;; + + *) + echo "Usage: saikyo-av-admin {health|status-protection|run-evidence|run-audit|collect-artifacts|scan|quarantine |delete |enable-protection|disable-protection}" >&2 + exit 2 + ;; +esac diff --git a/saikyo-av-gui/debian/saikyo-av-gui/usr/share/applications/saikyo-av-gui.desktop b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/applications/saikyo-av-gui.desktop new file mode 100644 index 0000000..d669e10 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/applications/saikyo-av-gui.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Saikyo Antivirus +Comment=Saikyo Antivirus admin panel +Exec=/usr/bin/saikyo-av-gui +Icon=saikyo-av +Terminal=false +Categories=System;Security; diff --git a/saikyo-av-gui/debian/saikyo-av-gui/usr/share/doc/saikyo-av-gui/changelog.gz b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/doc/saikyo-av-gui/changelog.gz new file mode 100644 index 0000000..8ca52c8 Binary files /dev/null and b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/doc/saikyo-av-gui/changelog.gz differ diff --git a/saikyo-av-gui/debian/saikyo-av-gui/usr/share/doc/saikyo-av-gui/copyright b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/doc/saikyo-av-gui/copyright new file mode 100644 index 0000000..10e3619 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/doc/saikyo-av-gui/copyright @@ -0,0 +1,26 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-av-gui +Source: https://saikyo-os.ru/ + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-av-gui/debian/saikyo-av-gui/usr/share/icons/hicolor/scalable/apps/saikyo-av.svg b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/icons/hicolor/scalable/apps/saikyo-av.svg new file mode 100644 index 0000000..1fc23bb --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/icons/hicolor/scalable/apps/saikyo-av.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/saikyo-av-gui/debian/saikyo-av-gui/usr/share/polkit-1/actions/org.saikyo.av.admin.policy b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/polkit-1/actions/org.saikyo.av.admin.policy new file mode 100644 index 0000000..f357953 --- /dev/null +++ b/saikyo-av-gui/debian/saikyo-av-gui/usr/share/polkit-1/actions/org.saikyo.av.admin.policy @@ -0,0 +1,15 @@ + + + + + Run Saikyo Antivirus administrative actions + Authentication is required to run Saikyo Antivirus administrative actions. + + no + no + auth_admin + + /usr/sbin/saikyo-av-admin + true + + diff --git a/saikyo-av-gui/debian/source/format b/saikyo-av-gui/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/saikyo-av-gui/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/saikyo-av-gui/desktop/saikyo-av-gui.desktop b/saikyo-av-gui/desktop/saikyo-av-gui.desktop new file mode 100644 index 0000000..d669e10 --- /dev/null +++ b/saikyo-av-gui/desktop/saikyo-av-gui.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Saikyo Antivirus +Comment=Saikyo Antivirus admin panel +Exec=/usr/bin/saikyo-av-gui +Icon=saikyo-av +Terminal=false +Categories=System;Security; diff --git a/saikyo-av-gui/icons/saikyo-av.svg b/saikyo-av-gui/icons/saikyo-av.svg new file mode 100644 index 0000000..1fc23bb --- /dev/null +++ b/saikyo-av-gui/icons/saikyo-av.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/saikyo-av-gui/polkit/org.saikyo.av.admin.policy b/saikyo-av-gui/polkit/org.saikyo.av.admin.policy new file mode 100644 index 0000000..f357953 --- /dev/null +++ b/saikyo-av-gui/polkit/org.saikyo.av.admin.policy @@ -0,0 +1,15 @@ + + + + + Run Saikyo Antivirus administrative actions + Authentication is required to run Saikyo Antivirus administrative actions. + + no + no + auth_admin + + /usr/sbin/saikyo-av-admin + true + + diff --git a/saikyo-av/bin/saikyo-avd b/saikyo-av/bin/saikyo-avd new file mode 100644 index 0000000..a6419c9 --- /dev/null +++ b/saikyo-av/bin/saikyo-avd @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPORTS_DIR="/var/lib/saikyo-av/reports" +ART_DIR="${REPORTS_DIR}/artifacts" + +mkdir -p "${REPORTS_DIR}" "${ART_DIR}" || true +chmod 0755 /var/lib/saikyo-av "${REPORTS_DIR}" 2>/dev/null || true + +TS="$(date -u +%Y%m%dT%H%M%SZ)" +REPORT_ID="agent-${TS}" +OUT_JSON="${REPORTS_DIR}/${REPORT_ID}.json" + +FAILED_UNITS_FILE="${ART_DIR}/systemctl-failed-${TS}.txt" +JOURNAL_FILE="${ART_DIR}/journal-warn-${TS}.txt" + +(systemctl --failed 2>&1 || true) > "${FAILED_UNITS_FILE}" +(journalctl -b -p warning..alert --no-pager 2>&1 | tail -n 400 || true) > "${JOURNAL_FILE}" + +FAILED_COUNT="$(grep -cE '^[^\s].*\.service' "${FAILED_UNITS_FILE}" 2>/dev/null || echo 0)" + +SEVERITY="info" +SUMMARY="Saikyo AV: periodic health report" +if [[ "${FAILED_COUNT}" -gt 0 ]]; then + SEVERITY="warn" + SUMMARY="Saikyo AV: detected failed systemd units (${FAILED_COUNT})" +fi + +cat > "${OUT_JSON}" < Mon, 20 Jan 2026 16:55:00 +0000 diff --git a/saikyo-av/debian/.debhelper/generated/saikyo-av/installed-by-dh_install b/saikyo-av/debian/.debhelper/generated/saikyo-av/installed-by-dh_install new file mode 100644 index 0000000..1b4fbe1 --- /dev/null +++ b/saikyo-av/debian/.debhelper/generated/saikyo-av/installed-by-dh_install @@ -0,0 +1,3 @@ +./bin/saikyo-avd +./systemd/saikyo-avd.service +./systemd/saikyo-avd.timer diff --git a/saikyo-av/debian/.debhelper/generated/saikyo-av/installed-by-dh_installdocs b/saikyo-av/debian/.debhelper/generated/saikyo-av/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-av/debian/.debhelper/generated/saikyo-av/postinst.service b/saikyo-av/debian/.debhelper/generated/saikyo-av/postinst.service new file mode 100644 index 0000000..5d4badd --- /dev/null +++ b/saikyo-av/debian/.debhelper/generated/saikyo-av/postinst.service @@ -0,0 +1,30 @@ +# Automatically added by dh_installsystemd/13.24.2 +if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then + # The following line should be removed in trixie or trixie+1 + deb-systemd-helper unmask 'saikyo-avd.timer' >/dev/null || true + + # was-enabled defaults to true, so new installations run enable. + if deb-systemd-helper --quiet was-enabled 'saikyo-avd.timer'; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable 'saikyo-avd.timer' >/dev/null || true + else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state 'saikyo-avd.timer' >/dev/null || true + fi +fi +# End automatically added section +# Automatically added by dh_installsystemd/13.24.2 +if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then + if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + if [ -n "$2" ]; then + _dh_action=restart + else + _dh_action=start + fi + deb-systemd-invoke $_dh_action 'saikyo-avd.service' 'saikyo-avd.timer' >/dev/null || true + fi +fi +# End automatically added section diff --git a/saikyo-av/debian/.debhelper/generated/saikyo-av/prerm.service b/saikyo-av/debian/.debhelper/generated/saikyo-av/prerm.service new file mode 100644 index 0000000..d78752e --- /dev/null +++ b/saikyo-av/debian/.debhelper/generated/saikyo-av/prerm.service @@ -0,0 +1,5 @@ +# Automatically added by dh_installsystemd/13.24.2 +if [ -z "$DPKG_ROOT" ] && [ "$1" = remove ] && [ -d /run/systemd/system ] ; then + deb-systemd-invoke stop 'saikyo-avd.service' 'saikyo-avd.timer' >/dev/null || true +fi +# End automatically added section diff --git a/saikyo-av/debian/changelog b/saikyo-av/debian/changelog new file mode 100644 index 0000000..f9ddd37 --- /dev/null +++ b/saikyo-av/debian/changelog @@ -0,0 +1,5 @@ +saikyo-av (0.1.1) stable; urgency=medium + + * Fix packaging: install saikyo-avd and systemd units to correct paths; cleanup broken dirs on upgrade. + + -- SAIKYO OS Mon, 20 Jan 2026 16:55:00 +0000 diff --git a/saikyo-av/debian/control b/saikyo-av/debian/control new file mode 100644 index 0000000..d555219 --- /dev/null +++ b/saikyo-av/debian/control @@ -0,0 +1,13 @@ +Source: saikyo-av +Section: admin +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-av +Architecture: all +Depends: ${misc:Depends}, bash, coreutils, systemd | systemd-sysv, util-linux, grep, sed, gawk +Description: Saikyo Antivirus agent (local) + Local always-on agent for collecting security signals and producing local reports. diff --git a/saikyo-av/debian/copyright b/saikyo-av/debian/copyright new file mode 100644 index 0000000..ac72216 --- /dev/null +++ b/saikyo-av/debian/copyright @@ -0,0 +1,26 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-av +Source: https://saikyo-os.ru/ + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-av/debian/debhelper-build-stamp b/saikyo-av/debian/debhelper-build-stamp new file mode 100644 index 0000000..61a8eb9 --- /dev/null +++ b/saikyo-av/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-av diff --git a/saikyo-av/debian/files b/saikyo-av/debian/files new file mode 100644 index 0000000..5d6ca36 --- /dev/null +++ b/saikyo-av/debian/files @@ -0,0 +1,2 @@ +saikyo-av_0.1.1_all.deb admin optional +saikyo-av_0.1.1_amd64.buildinfo admin optional diff --git a/saikyo-av/debian/install b/saikyo-av/debian/install new file mode 100644 index 0000000..75b1170 --- /dev/null +++ b/saikyo-av/debian/install @@ -0,0 +1,3 @@ +bin/saikyo-avd usr/sbin/ +systemd/saikyo-avd.service lib/systemd/system/ +systemd/saikyo-avd.timer lib/systemd/system/ diff --git a/saikyo-av/debian/rules b/saikyo-av/debian/rules new file mode 100755 index 0000000..7cbee11 --- /dev/null +++ b/saikyo-av/debian/rules @@ -0,0 +1,15 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: + +override_dh_fixperms: + dh_fixperms + chmod 0755 debian/saikyo-av/usr/sbin/saikyo-avd + chmod 0755 debian/saikyo-av.preinst + chmod 0755 debian/saikyo-av.postinst + chmod 0755 debian/saikyo-av.prerm diff --git a/saikyo-av/debian/saikyo-av.debhelper.log b/saikyo-av/debian/saikyo-av.debhelper.log new file mode 100644 index 0000000..93c5512 --- /dev/null +++ b/saikyo-av/debian/saikyo-av.debhelper.log @@ -0,0 +1 @@ +dh_fixperms diff --git a/saikyo-av/debian/saikyo-av.postinst b/saikyo-av/debian/saikyo-av.postinst new file mode 100755 index 0000000..6fbc602 --- /dev/null +++ b/saikyo-av/debian/saikyo-av.postinst @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +case "$1" in + configure) + mkdir -p /var/lib/saikyo-av/reports /var/lib/saikyo-av/reports/artifacts || true + chmod 0755 /var/lib/saikyo-av /var/lib/saikyo-av/reports 2>/dev/null || true + systemctl daemon-reload >/dev/null 2>&1 || true + systemctl enable --now saikyo-avd.timer >/dev/null 2>&1 || true + ;; +esac + +exit 0 diff --git a/saikyo-av/debian/saikyo-av.postrm.debhelper b/saikyo-av/debian/saikyo-av.postrm.debhelper new file mode 100644 index 0000000..5165f03 --- /dev/null +++ b/saikyo-av/debian/saikyo-av.postrm.debhelper @@ -0,0 +1,12 @@ +# Automatically added by dh_installsystemd/13.24.2 +if [ "$1" = remove ] && [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_installsystemd/13.24.2 +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge 'saikyo-avd.timer' >/dev/null || true + fi +fi +# End automatically added section diff --git a/saikyo-av/debian/saikyo-av.preinst b/saikyo-av/debian/saikyo-av.preinst new file mode 100755 index 0000000..9bd3a25 --- /dev/null +++ b/saikyo-av/debian/saikyo-av.preinst @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Fixup for 0.1.0 packaging mistake where dh_install created directories: +# /usr/sbin/saikyo-avd/saikyo-avd +# /lib/systemd/system/saikyo-avd.service/saikyo-avd.service +# /lib/systemd/system/saikyo-avd.timer/saikyo-avd.timer +# Remove them before unpacking the corrected package. + +if [[ "$1" == "upgrade" || "$1" == "install" ]]; then + for d in \ + /usr/sbin/saikyo-avd \ + /lib/systemd/system/saikyo-avd.service \ + /lib/systemd/system/saikyo-avd.timer + do + if [[ -d "$d" ]]; then + rm -rf "$d" 2>/dev/null || true + fi + done +fi + +exit 0 diff --git a/saikyo-av/debian/saikyo-av.prerm b/saikyo-av/debian/saikyo-av.prerm new file mode 100755 index 0000000..f3f388e --- /dev/null +++ b/saikyo-av/debian/saikyo-av.prerm @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +case "$1" in + remove|deconfigure) + systemctl disable --now saikyo-avd.timer >/dev/null 2>&1 || true + ;; +esac + +exit 0 diff --git a/saikyo-av/debian/saikyo-av.substvars b/saikyo-av/debian/saikyo-av.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-av/debian/saikyo-av.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-av/debian/saikyo-av/DEBIAN/control b/saikyo-av/debian/saikyo-av/DEBIAN/control new file mode 100644 index 0000000..679c3ce --- /dev/null +++ b/saikyo-av/debian/saikyo-av/DEBIAN/control @@ -0,0 +1,10 @@ +Package: saikyo-av +Version: 0.1.1 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 21 +Depends: bash, coreutils, systemd | systemd-sysv, util-linux, grep, sed, gawk +Section: admin +Priority: optional +Description: Saikyo Antivirus agent (local) + Local always-on agent for collecting security signals and producing local reports. diff --git a/saikyo-av/debian/saikyo-av/DEBIAN/md5sums b/saikyo-av/debian/saikyo-av/DEBIAN/md5sums new file mode 100644 index 0000000..0033498 --- /dev/null +++ b/saikyo-av/debian/saikyo-av/DEBIAN/md5sums @@ -0,0 +1,5 @@ +170cbb33110db84791e38ef0d265367b lib/systemd/system/saikyo-avd.service +706f24a7d93f1588f6130236a0cc30dc lib/systemd/system/saikyo-avd.timer +428c2feb89dbdabeb07df9c07a55931e usr/sbin/saikyo-avd +1d3486cb4986868f799ac11f32b7c69f usr/share/doc/saikyo-av/changelog.gz +ba541e7320b5e3963eae2a7ad3e12502 usr/share/doc/saikyo-av/copyright diff --git a/saikyo-av/debian/saikyo-av/DEBIAN/postinst b/saikyo-av/debian/saikyo-av/DEBIAN/postinst new file mode 100755 index 0000000..6fbc602 --- /dev/null +++ b/saikyo-av/debian/saikyo-av/DEBIAN/postinst @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +case "$1" in + configure) + mkdir -p /var/lib/saikyo-av/reports /var/lib/saikyo-av/reports/artifacts || true + chmod 0755 /var/lib/saikyo-av /var/lib/saikyo-av/reports 2>/dev/null || true + systemctl daemon-reload >/dev/null 2>&1 || true + systemctl enable --now saikyo-avd.timer >/dev/null 2>&1 || true + ;; +esac + +exit 0 diff --git a/saikyo-av/debian/saikyo-av/DEBIAN/postrm b/saikyo-av/debian/saikyo-av/DEBIAN/postrm new file mode 100755 index 0000000..cf2ed3d --- /dev/null +++ b/saikyo-av/debian/saikyo-av/DEBIAN/postrm @@ -0,0 +1,14 @@ +#!/bin/sh +set -e +# Automatically added by dh_installsystemd/13.24.2 +if [ "$1" = remove ] && [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_installsystemd/13.24.2 +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge 'saikyo-avd.timer' >/dev/null || true + fi +fi +# End automatically added section diff --git a/saikyo-av/debian/saikyo-av/DEBIAN/preinst b/saikyo-av/debian/saikyo-av/DEBIAN/preinst new file mode 100755 index 0000000..9bd3a25 --- /dev/null +++ b/saikyo-av/debian/saikyo-av/DEBIAN/preinst @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Fixup for 0.1.0 packaging mistake where dh_install created directories: +# /usr/sbin/saikyo-avd/saikyo-avd +# /lib/systemd/system/saikyo-avd.service/saikyo-avd.service +# /lib/systemd/system/saikyo-avd.timer/saikyo-avd.timer +# Remove them before unpacking the corrected package. + +if [[ "$1" == "upgrade" || "$1" == "install" ]]; then + for d in \ + /usr/sbin/saikyo-avd \ + /lib/systemd/system/saikyo-avd.service \ + /lib/systemd/system/saikyo-avd.timer + do + if [[ -d "$d" ]]; then + rm -rf "$d" 2>/dev/null || true + fi + done +fi + +exit 0 diff --git a/saikyo-av/debian/saikyo-av/DEBIAN/prerm b/saikyo-av/debian/saikyo-av/DEBIAN/prerm new file mode 100755 index 0000000..f3f388e --- /dev/null +++ b/saikyo-av/debian/saikyo-av/DEBIAN/prerm @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +case "$1" in + remove|deconfigure) + systemctl disable --now saikyo-avd.timer >/dev/null 2>&1 || true + ;; +esac + +exit 0 diff --git a/saikyo-av/debian/saikyo-av/lib/systemd/system/saikyo-avd.service b/saikyo-av/debian/saikyo-av/lib/systemd/system/saikyo-avd.service new file mode 100644 index 0000000..cfdc537 --- /dev/null +++ b/saikyo-av/debian/saikyo-av/lib/systemd/system/saikyo-avd.service @@ -0,0 +1,19 @@ +[Unit] +Description=Saikyo Antivirus Agent (report generator) + +[Service] +Type=oneshot +ExecStart=/usr/sbin/saikyo-avd +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectControlGroups=yes +RestrictSUIDSGID=yes +LockPersonality=yes +MemoryDenyWriteExecute=yes +SystemCallArchitectures=native +SystemCallFilter=@system-service +ReadWritePaths=/var/lib/saikyo-av/reports diff --git a/saikyo-av/debian/saikyo-av/lib/systemd/system/saikyo-avd.timer b/saikyo-av/debian/saikyo-av/lib/systemd/system/saikyo-avd.timer new file mode 100644 index 0000000..0ff87b1 --- /dev/null +++ b/saikyo-av/debian/saikyo-av/lib/systemd/system/saikyo-avd.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Saikyo Antivirus Agent timer + +[Timer] +OnBootSec=2min +OnUnitActiveSec=5min +AccuracySec=30s +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/saikyo-av/debian/saikyo-av/usr/sbin/saikyo-avd b/saikyo-av/debian/saikyo-av/usr/sbin/saikyo-avd new file mode 100755 index 0000000..a6419c9 --- /dev/null +++ b/saikyo-av/debian/saikyo-av/usr/sbin/saikyo-avd @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPORTS_DIR="/var/lib/saikyo-av/reports" +ART_DIR="${REPORTS_DIR}/artifacts" + +mkdir -p "${REPORTS_DIR}" "${ART_DIR}" || true +chmod 0755 /var/lib/saikyo-av "${REPORTS_DIR}" 2>/dev/null || true + +TS="$(date -u +%Y%m%dT%H%M%SZ)" +REPORT_ID="agent-${TS}" +OUT_JSON="${REPORTS_DIR}/${REPORT_ID}.json" + +FAILED_UNITS_FILE="${ART_DIR}/systemctl-failed-${TS}.txt" +JOURNAL_FILE="${ART_DIR}/journal-warn-${TS}.txt" + +(systemctl --failed 2>&1 || true) > "${FAILED_UNITS_FILE}" +(journalctl -b -p warning..alert --no-pager 2>&1 | tail -n 400 || true) > "${JOURNAL_FILE}" + +FAILED_COUNT="$(grep -cE '^[^\s].*\.service' "${FAILED_UNITS_FILE}" 2>/dev/null || echo 0)" + +SEVERITY="info" +SUMMARY="Saikyo AV: periodic health report" +if [[ "${FAILED_COUNT}" -gt 0 ]]; then + SEVERITY="warn" + SUMMARY="Saikyo AV: detected failed systemd units (${FAILED_COUNT})" +fi + +cat > "${OUT_JSON}" </dev/null || true +chmod 0755 "${OUT_DIR}" 2>/dev/null || true + +dumpln() { + printf '\n==== %s ====\n' "$1" +} + +{ + echo "saikyo_evidence_version=1" + echo "timestamp_utc=${TS}" + + dumpln "os-release" + cat /etc/os-release 2>/dev/null || true + + dumpln "lsb-release" + cat /etc/lsb-release 2>/dev/null || true + + dumpln "uname" + uname -a 2>/dev/null || true + + dumpln "dpkg saikyo packages" + dpkg -l 2>/dev/null | grep -i saikyo || true + + dumpln "apt sources" + if [ -f /etc/apt/sources.list ]; then + echo "--- /etc/apt/sources.list ---" + sed -n '1,200p' /etc/apt/sources.list 2>/dev/null || true + fi + if [ -d /etc/apt/sources.list.d ]; then + echo "--- /etc/apt/sources.list.d ---" + ls -la /etc/apt/sources.list.d 2>/dev/null || true + for f in /etc/apt/sources.list.d/*; do + [ -e "$f" ] || continue + echo "--- $f ---" + sed -n '1,200p' "$f" 2>/dev/null || true + done + fi + + dumpln "apt sources forbidden patterns" + grep -RIn --line-number -E 'deb\.debian\.org|security\.debian\.org|cdrom:|archive\.ubuntu\.com' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null || true + + dumpln "apt periodic config" + if [ -f /etc/apt/apt.conf.d/20saikyo-periodic ]; then + echo "20saikyo-periodic=present" + cat /etc/apt/apt.conf.d/20saikyo-periodic 2>/dev/null || true + else + echo "20saikyo-periodic=MISSING" + fi + + dumpln "auto update units state" + for u in apt-daily.timer apt-daily-upgrade.timer unattended-upgrades.service packagekit-offline-update.timer packagekit-offline-update.service; do + echo "unit=${u} enabled=$(systemctl is-enabled "$u" 2>/dev/null || echo unknown) active=$(systemctl is-active "$u" 2>/dev/null || echo unknown) masked=$(systemctl is-masked "$u" 2>/dev/null || echo unknown)" + done + + dumpln "packagekit state" + echo "packagekit enabled=$(systemctl is-enabled packagekit.service 2>/dev/null || echo unknown) active=$(systemctl is-active packagekit.service 2>/dev/null || echo unknown) masked=$(systemctl is-masked packagekit.service 2>/dev/null || echo unknown)" + + dumpln "system timers (filtered)" + systemctl list-timers --all 2>/dev/null | grep -E 'apt|unattended|packagekit|flatpak|snap' || true + + dumpln "license" + if command -v saikyo-license >/dev/null 2>&1; then + echo "saikyo-license=present" + if /usr/sbin/saikyo-license verify >/dev/null 2>&1; then + echo "license_verify=ok" + else + echo "license_verify=fail" + fi + else + echo "saikyo-license=missing" + fi + + dumpln "secure boot" + if command -v mokutil >/dev/null 2>&1; then + mokutil --sb-state 2>&1 || true + mokutil --list-enrolled 2>/dev/null || true + else + echo "mokutil=missing" + fi + + dumpln "branding files" + ls -la /usr/share/saikyo-os 2>/dev/null || true + ls -la /usr/share/wallpapers/Saikyo/contents/images 2>/dev/null || true + ls -la /usr/share/sddm/themes/Saikyo 2>/dev/null || true + + dumpln "visible Debian/Plasma strings (best effort)" + grep -RIn --line-number -E '\bDebian\b|\bPlasma\b' /usr/share/applications /usr/share/metainfo 2>/dev/null | head -n 50 || true + +} >"${OUT_FILE}" 2>/dev/null + +chmod 0644 "${OUT_FILE}" 2>/dev/null || true +ln -sfn "$(basename "${OUT_FILE}")" "${OUT_DIR}/latest.log" 2>/dev/null || true + +echo "Wrote ${OUT_FILE}" +exit 0 diff --git a/saikyo-branding/assets/bin/saikyo-first-login-kde b/saikyo-branding/assets/bin/saikyo-first-login-kde new file mode 100644 index 0000000..181169f --- /dev/null +++ b/saikyo-branding/assets/bin/saikyo-first-login-kde @@ -0,0 +1,185 @@ +#!/usr/bin/env bash +set -euo pipefail + +MARK_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/saikyo-os" +MARK_FILE="${MARK_DIR}/first-login-kde.done" +WALLPAPER_PATH="/usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg" +LOGO_PATH="/usr/share/saikyo-os/logos/saikyo-logo.svg" +AVATAR_PNG="/usr/share/icons/hicolor/512x512/apps/distributor-logo.png" +COLOR_SCHEME="SaikyoDark" + +mkdir -p "${MARK_DIR}" 2>/dev/null || true + +if [[ -f "${MARK_FILE}" ]]; then + exit 0 +fi + +if [[ ! -r "${WALLPAPER_PATH}" ]]; then + exit 0 +fi + +have_cmd() { command -v "$1" >/dev/null 2>&1; } + +apply_look_and_feel() { + if have_cmd lookandfeeltool; then + lookandfeeltool -a org.saikyo.desktop >/dev/null 2>&1 || true + elif have_cmd lookandfeeltool5; then + lookandfeeltool5 -a org.saikyo.desktop >/dev/null 2>&1 || true + fi +} + +apply_color_scheme() { + if have_cmd plasma-apply-colorscheme; then + plasma-apply-colorscheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true + fi + + if have_cmd kwriteconfig5; then + kwriteconfig5 --file kdeglobals --group General --key ColorScheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true + elif have_cmd kwriteconfig6; then + kwriteconfig6 --file kdeglobals --group General --key ColorScheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true + fi +} + +restart_plasma_shell() { + if have_cmd kquitapp5 && have_cmd kstart5; then + kquitapp5 plasmashell >/dev/null 2>&1 || true + nohup kstart5 plasmashell >/dev/null 2>&1 & + elif have_cmd kquitapp6 && have_cmd kstart6; then + kquitapp6 plasmashell >/dev/null 2>&1 || true + nohup kstart6 plasmashell >/dev/null 2>&1 & + fi +} + +set_wallpaper() { + local qdbus_cmd="$1" + "${qdbus_cmd}" org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript " + var Desktops = desktops(); + for (i = 0; i < Desktops.length; i++) { + d = Desktops[i]; + d.wallpaperPlugin = 'org.kde.image'; + d.currentConfigGroup = ['Wallpaper', 'org.kde.image', 'General']; + d.writeConfig('Image', 'file://${WALLPAPER_PATH}'); + } + " >/dev/null 2>&1 || return 1 + return 0 +} + +if have_cmd qdbus; then + set_wallpaper qdbus || true +elif have_cmd qdbus6; then + set_wallpaper qdbus6 || true +fi + +apply_color_scheme +apply_look_and_feel +restart_plasma_shell || true + +add_panel_widgets_and_pins() { + local qdbus_cmd="$1" + "${qdbus_cmd}" org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript " + function firstPanel() { + var ps = panels(); + if (!ps || ps.length === 0) return null; + return ps[0]; + } + + function ensureWidget(panel, pluginId) { + try { + var ws = panel.widgets(); + for (var i = 0; i < ws.length; i++) { + if (ws[i].type === pluginId) return; + } + panel.addWidget(pluginId); + } catch (e) { + } + } + + function ensureTaskManagerPins(panel, launchersStr) { + try { + var ws = panel.widgets(); + for (var i = 0; i < ws.length; i++) { + if (ws[i].type === 'org.kde.plasma.taskmanager') { + var tm = ws[i]; + tm.currentConfigGroup = ['General']; + tm.writeConfig('launchers', launchersStr); + return; + } + } + } catch (e) { + } + } + + var p = firstPanel(); + if (!p) return; + + // License status widget + ensureWidget(p, 'org.saikyo.license'); + + // Pinned launchers in task manager + var launchers = [ + 'applications:org.kde.discover.desktop', + 'applications:systemsettings.desktop', + 'applications:saikyo-license.desktop' + ].join(','); + ensureTaskManagerPins(p, launchers); + " >/dev/null 2>&1 || true +} + +if have_cmd qdbus; then + add_panel_widgets_and_pins qdbus || true +elif have_cmd qdbus6; then + add_panel_widgets_and_pins qdbus6 || true +fi + +desktop_dir="${HOME}/Desktop" +if [[ -r "${HOME}/.config/user-dirs.dirs" ]]; then + d=$(grep -E '^XDG_DESKTOP_DIR=' "${HOME}/.config/user-dirs.dirs" | head -n 1 | cut -d= -f2- | tr -d '"') + d=${d//\$HOME/${HOME}} + if [[ -n "${d}" ]]; then desktop_dir="${d}"; fi +fi + +# Ensure KWallet doesn't require a pre-existing GPG key by default. +# This prevents first-run failures in apps that request KWallet integration. +mkdir -p "${XDG_CONFIG_HOME:-${HOME}/.config}" 2>/dev/null || true +{ + echo "[Wallet]" + echo "UseGpg=false" +} > "${XDG_CONFIG_HOME:-${HOME}/.config}/kwalletrc" 2>/dev/null || true + +# Set a default user avatar for new sessions (shown in KDE menus/panel). +# Prefer PNG (KDE reliably picks it up); fallback to SVG. +if [[ -r "${AVATAR_PNG}" ]]; then + cp -f "${AVATAR_PNG}" "${HOME}/.face.icon" 2>/dev/null || true + chmod 0644 "${HOME}/.face.icon" 2>/dev/null || true +elif [[ -r "${LOGO_PATH}" ]]; then + cp -f "${LOGO_PATH}" "${HOME}/.face.icon" 2>/dev/null || true + chmod 0644 "${HOME}/.face.icon" 2>/dev/null || true +fi + +mkdir -p "${desktop_dir}" 2>/dev/null || true +if [[ -r /usr/share/applications/saikyo-license.desktop ]]; then + cp -f /usr/share/applications/saikyo-license.desktop "${desktop_dir}/SAIKYO OS License.desktop" 2>/dev/null || true + chmod 0755 "${desktop_dir}/SAIKYO OS License.desktop" 2>/dev/null || true +fi + +if [[ -r /usr/share/applications/org.kde.discover.desktop ]]; then + cp -f /usr/share/applications/org.kde.discover.desktop "${desktop_dir}/SAIKYO Discover.desktop" 2>/dev/null || true + chmod 0755 "${desktop_dir}/SAIKYO Discover.desktop" 2>/dev/null || true +fi + +if [[ -r /usr/share/applications/systemsettings.desktop ]]; then + cp -f /usr/share/applications/systemsettings.desktop "${desktop_dir}/System Settings.desktop" 2>/dev/null || true + chmod 0755 "${desktop_dir}/System Settings.desktop" 2>/dev/null || true +fi + +if have_cmd kdialog; then + msg="Добро пожаловать в SAIKYO OS.\n\nНа этой системе действует пробный период 7 дней.\nПосле истечения пробного периода потребуется полная активация лицензии.\n\nРекомендуется выполнить активацию заранее." + if kdialog --title "SAIKYO OS" --icon "${LOGO_PATH}" --yes-label "Открыть активацию" --no-label "Позже" --yesno "${msg}" 2>/dev/null; then + if have_cmd saikyo-license-kde; then + nohup saikyo-license-kde >/dev/null 2>&1 & + fi + fi +fi + +touch "${MARK_FILE}" 2>/dev/null || true +exit 0 diff --git a/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-588e4226ca6a9ca0fe3790f5b4ed5ff286a80d131849f572a200592463676c1b.crt b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-588e4226ca6a9ca0fe3790f5b4ed5ff286a80d131849f572a200592463676c1b.crt new file mode 100644 index 0000000..ca7be59 --- /dev/null +++ b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-588e4226ca6a9ca0fe3790f5b4ed5ff286a80d131849f572a200592463676c1b.crt @@ -0,0 +1,43 @@ +-----BEGIN CERTIFICATE----- +MIIHmDCCB0WgAwIBAgIKYqt5lQAAAAADtjAKBggqhQMHAQEDAjCCASQxHjAcBgkq +hkiG9w0BCQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxGDAWBgNVBAgM +Dzc3INCc0L7RgdC60LLQsDEZMBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQsDEuMCwG +A1UECQwl0YPQu9C40YbQsCDQotCy0LXRgNGB0LrQsNGPLCDQtNC+0LwgNzEsMCoG +A1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40LgxGDAWBgUq +hQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0NzQzNzUx +LDAqBgNVBAMMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MB4X +DTIwMDIwNTE0MDI0N1oXDTM1MDIwNTE0MDI0N1owggFtMSAwHgYJKoZIhvcNAQkB +FhF1Y19ma0Byb3NrYXpuYS5ydTEZMBcGA1UECAwQ0LMuINCc0L7RgdC60LLQsDEa +MBgGCCqFAwOBAwEBEgwwMDc3MTA1Njg3NjAxGDAWBgUqhQNkARINMTA0Nzc5NzAx +OTgzMDFgMF4GA1UECQxX0JHQvtC70YzRiNC+0Lkg0JfQu9Cw0YLQvtGD0YHRgtC4 +0L3RgdC60LjQuSDQv9C10YDQtdGD0LvQvtC6LCDQtC4gNiwg0YHRgtGA0L7QtdC9 +0LjQtSAxMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAxCzAJBgNVBAYTAlJVMTgwNgYD +VQQKDC/QpNC10LTQtdGA0LDQu9GM0L3QvtC1INC60LDQt9C90LDRh9C10LnRgdGC +0LLQvjE4MDYGA1UEAwwv0KTQtdC00LXRgNCw0LvRjNC90L7QtSDQutCw0LfQvdCw +0YfQtdC50YHRgtCy0L4wZjAfBggqhQMHAQEBATATBgcqhQMCAiMBBggqhQMHAQEC +AgNDAARA/G1/DjI9Hw+IV16X9pm0vTEj7uE8rv88OAAdmD7ddmRuYD6FLl491Zqv +AxF94s7j5C2i/XC5OMkEhJQ6nKUI9KOCBAMwggP/MBIGA1UdEwEB/wQIMAYBAf8C +AQAwUgYFKoUDZG8ESQxHItCa0YDQuNC/0YLQvtCf0YDQviBDU1AiINCy0LXRgNGB +0LjRjyA0LjAgKNC40YHQv9C+0LvQvdC10L3QuNC1IDItQmFzZSkwJQYDVR0gBB4w +HDAIBgYqhQNkcQEwCAYGKoUDZHECMAYGBFUdIAAwDgYDVR0PAQH/BAQDAgHGMIIB +ZQYDVR0jBIIBXDCCAViAFMJU8bRr1Ey34G02tCOQ8f7DPJsGoYIBLKSCASgwggEk +MR4wHAYJKoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgw +FgYDVQQIDA83NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy +0LAxLjAsBgNVBAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8 +IDcxLDAqBgNVBAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4 +MRgwFgYFKoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEw +NDc0Mzc1MSwwKgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB +0LjQuIIQTm1HiybyfWV/do4CXOPTkzAdBgNVHQ4EFgQU0GSWbXJA61h9JH+7IFvP +w45setQwgZgGA1UdHwSBkDCBjTAtoCugKYYnaHR0cDovL3JlZXN0ci1wa2kucnUv +Y2RwL2d1Y19nb3N0MTIuY3JsMC2gK6AphidodHRwOi8vY29tcGFueS5ydC5ydS9j +ZHAvZ3VjX2dvc3QxMi5jcmwwLaAroCmGJ2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2Nk +cC9ndWNfZ29zdDEyLmNybDBDBggrBgEFBQcBAQQ3MDUwMwYIKwYBBQUHMAKGJ2h0 +dHA6Ly9yZWVzdHItcGtpLnJ1L2NkcC9ndWNfZ29zdDEyLmNydDCB9QYFKoUDZHAE +geswgegMNNCf0JDQmtCcIMKr0JrRgNC40L/RgtC+0J/RgNC+IEhTTcK7INCy0LXR +gNGB0LjQuCAyLjAMQ9Cf0JDQmiDCq9CT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+0YHR +gtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAwrsMNdCX0LDQutC70Y7Rh9C1 +0L3QuNC1IOKEliAxNDkvMy8yLzIvMjMg0L7RgiAwMi4wMy4yMDE4DDTQl9Cw0LrQ +u9GO0YfQtdC90LjQtSDihJYgMTQ5LzcvNi8xMDUg0L7RgiAyNy4wNi4yMDE4MAoG +CCqFAwcBAQMCA0EAYO7/gtED6JgCLK7ffDDrnggByLOMGafo2r5TakOU/aBSidSD +a17cnrTflUw6REP8SrOa7o/tfCwpkbLJF2l6CQ== +-----END CERTIFICATE----- diff --git a/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-5b51db721b7c34958ed7432ae917a91297dd37508b2cae4f858ffbac6bc525ef.crt b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-5b51db721b7c34958ed7432ae917a91297dd37508b2cae4f858ffbac6bc525ef.crt new file mode 100644 index 0000000..404a79a --- /dev/null +++ b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-5b51db721b7c34958ed7432ae917a91297dd37508b2cae4f858ffbac6bc525ef.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEizCCA/egAwIBAgIVALl/bbS8diQY7sdeMXod8B1oPUzeMAoGCCqFAwcBAQMD +MIIBMDEVMBMGBSqFA2QEEgo3NzEwNDc0Mzc1MRgwFgYFKoUDZAESDTEwNDc3MDIw +MjY3MDExUzBRBgNVBAkMStCf0YDQtdGB0L3QtdC90YHQutCw0Y8g0L3QsNCx0LXR +gNC10LbQvdCw0Y8sINC00L7QvCAxMCwg0YHRgtGA0L7QtdC90LjQtSAyMRkwFwYD +VQQHDBDQsy4g0JzQvtGB0LrQstCwMRgwFgYDVQQIDA83NyDQnNC+0YHQutCy0LAx +CzAJBgNVBAYTAlJVMSYwJAYDVQQKDB3QnNC40L3RhtC40YTRgNGLINCg0L7RgdGB +0LjQuDE+MDwGA1UEAww10JzQuNC90YbQuNGE0YDRiyDQoNC+0YHRgdC40Lgg0J3Q +o9CmINC60L7RgNC90LXQstC+0LkwHhcNMjUwNTI4MTU0MTQxWhcNNDAwNTI0MTU0 +MTQxWjCCATAxFTATBgUqhQNkBBIKNzcxMDQ3NDM3NTEYMBYGBSqFA2QBEg0xMDQ3 +NzAyMDI2NzAxMVMwUQYDVQQJDErQn9GA0LXRgdC90LXQvdGB0LrQsNGPINC90LDQ +sdC10YDQtdC20L3QsNGPLCDQtNC+0LwgMTAsINGB0YLRgNC+0LXQvdC40LUgMjEZ +MBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQsDEYMBYGA1UECAwPNzcg0JzQvtGB0LrQ +stCwMQswCQYDVQQGEwJSVTEmMCQGA1UECgwd0JzQuNC90YbQuNGE0YDRiyDQoNC+ +0YHRgdC40LgxPjA8BgNVBAMMNdCc0LjQvdGG0LjRhNGA0Ysg0KDQvtGB0YHQuNC4 +INCd0KPQpiDQutC+0YDQvdC10LLQvtC5MIGqMCEGCCqFAwcBAQECMBUGCSqFAwcB +AgECAQYIKoUDBwEBAgMDgYQABIGAdCxt4xzP9ZCep1FqKXymiUBX5GVp9KNV6bCV +sw+nV+E2/i2QKoTXkSrZwNdTAGfHaxRj0dNZjwljPyGRARTmBSG1CLvZvuwrla4O +ki2HXmLtLmdZCmk0MVG/0qyK2Hg/cG9VU/YvHNSxbdWgfBpaIKEprR3511tD1Sui +xEPhknSjgZcwgZQwEgYDVR0TAQH/BAgwBgEB/wIBBDAdBgNVHQ4EFgQU2EhWFsjb +gUKVDHySi+o35xGlveowDgYDVR0PAQH/BAQDAgEGME8GA1UdIARIMEYwCgYIKoUD +AhkBDgMwBgYEVR0gADAIBgYqhQNkcQEwCAYGKoUDZHECMAgGBiqFA2RxAzAIBgYq +hQNkcQQwCAYGKoUDZHEFMAoGCCqFAwcBAQMDA4GBAG6v+ir5U37uMA9IEkhNMl95 +PSa6X0XsLrmxWFjHVQcPMGP5niDjF0pNDqxps40Ig2qqzGZXlFLxuu0w+xeuDfMn +/AokEYCkWsMRhmzNEmVkLWggEaqBfHO+JEmAjwvyo6ggyvBdX8xQRpbcw9/Ugxx5 +h7F9deB2Q1ZcoFgcZmFl +-----END CERTIFICATE----- diff --git a/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-71be2cef4964fda5a6302e2b55b741e40d25cef17df527f8abc350094973fa34.crt b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-71be2cef4964fda5a6302e2b55b741e40d25cef17df527f8abc350094973fa34.crt new file mode 100644 index 0000000..6ecbff0 --- /dev/null +++ b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-71be2cef4964fda5a6302e2b55b741e40d25cef17df527f8abc350094973fa34.crt @@ -0,0 +1,43 @@ +-----BEGIN CERTIFICATE----- +MIIHhjCCBzOgAwIBAgILALXxMtMAAAAAAVowCgYIKoUDBwEBAwIwggEkMR4wHAYJ +KoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQI +DA83NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAs +BgNVBAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAq +BgNVBAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYF +KoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1 +MSwwKgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuDAe +Fw0xODExMTkxNTU2MDFaFw0zMzExMTkxNTU2MDFaMIIBbTEgMB4GCSqGSIb3DQEJ +ARYRdWNfZmtAcm9za2F6bmEucnUxGTAXBgNVBAgMENCzLiDQnNC+0YHQutCy0LAx +GjAYBggqhQMDgQMBARIMMDA3NzEwNTY4NzYwMRgwFgYFKoUDZAESDTEwNDc3OTcw +MTk4MzAxYDBeBgNVBAkMV9CR0L7Qu9GM0YjQvtC5INCX0LvQsNGC0L7Rg9GB0YLQ +uNC90YHQutC40Lkg0L/QtdGA0LXRg9C70L7Quiwg0LQuIDYsINGB0YLRgNC+0LXQ +vdC40LUgMTEVMBMGA1UEBwwM0JzQvtGB0LrQstCwMQswCQYDVQQGEwJSVTE4MDYG +A1UECgwv0KTQtdC00LXRgNCw0LvRjNC90L7QtSDQutCw0LfQvdCw0YfQtdC50YHR +gtCy0L4xODA2BgNVBAMML9Ck0LXQtNC10YDQsNC70YzQvdC+0LUg0LrQsNC30L3Q +sNGH0LXQudGB0YLQstC+MGYwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEB +AgIDQwAEQBHF4BLi2ABpI2cZNBIql/rippw/0qqoi4S2NvJHSGbC694tvfJqOmGG +UCD0b1kUlEq6ueFAtF7gOICquPj+DcmjggPwMIID7DASBgNVHRMBAf8ECDAGAQH/ +AgEAMD8GBSqFA2RvBDYMNNCh0JrQl9CYICLQmtGA0LjQv9GC0L7Qn9GA0L4gQ1NQ +IiAo0LLQtdGA0YHQuNGPIDQuMCkwJQYDVR0gBB4wHDAIBgYqhQNkcQEwCAYGKoUD +ZHECMAYGBFUdIAAwDgYDVR0PAQH/BAQDAgHGMIIBZQYDVR0jBIIBXDCCAViAFMJU +8bRr1Ey34G02tCOQ8f7DPJsGoYIBLKSCASgwggEkMR4wHAYJKoZIhvcNAQkBFg9k +aXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQIDA83NyDQnNC+0YHQ +utCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAsBgNVBAkMJdGD0LvQ +uNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAqBgNVBAoMI9Cc0LjQ +vdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUDZAESDTEwNDc3 +MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1MSwwKgYDVQQDDCPQ +nNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuIIQTm1HiybyfWV/do4C +XOPTkzAdBgNVHQ4EFgQUwNbWCn1rfsmOObzaifqvlCxYWo0wgZgGA1UdHwSBkDCB +jTAtoCugKYYnaHR0cDovL3JlZXN0ci1wa2kucnUvY2RwL2d1Y19nb3N0MTIuY3Js +MC2gK6AphidodHRwOi8vY29tcGFueS5ydC5ydS9jZHAvZ3VjX2dvc3QxMi5jcmww +LaAroCmGJ2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2NkcC9ndWNfZ29zdDEyLmNybDBD +BggrBgEFBQcBAQQ3MDUwMwYIKwYBBQUHMAKGJ2h0dHA6Ly9yZWVzdHItcGtpLnJ1 +L2NkcC9ndWNfZ29zdDEyLmNydDCB9QYFKoUDZHAEgeswgegMNNCf0JDQmtCcIMKr +0JrRgNC40L/RgtC+0J/RgNC+IEhTTcK7INCy0LXRgNGB0LjQuCAyLjAMQ9Cf0JDQ +miDCq9CT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQ +uSDRhtC10L3RgtGAwrsMNdCX0LDQutC70Y7Rh9C10L3QuNC1IOKEliAxNDkvMy8y +LzIvMjMg0L7RgiAwMi4wMy4yMDE4DDTQl9Cw0LrQu9GO0YfQtdC90LjQtSDihJYg +MTQ5LzcvNi8xMDUg0L7RgiAyNy4wNi4yMDE4MAoGCCqFAwcBAQMCA0EALtT076gA +jyE3aXhaCfJUQCH8xyUy4/It0btS24K3tKJZYLaYahNjbNNqcEFptDfn6I0c89xz +oKhFJDPHvjy/sg== +-----END CERTIFICATE----- diff --git a/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-936a43fea6e8e525bcc0f81acd9c3d21b4fc4b9b68acea7906d698005afc6504.crt b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-936a43fea6e8e525bcc0f81acd9c3d21b4fc4b9b68acea7906d698005afc6504.crt new file mode 100644 index 0000000..4c143a2 --- /dev/null +++ b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-936a43fea6e8e525bcc0f81acd9c3d21b4fc4b9b68acea7906d698005afc6504.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFwjCCA6qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx +PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu +ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg +Q0EwHhcNMjIwMzAxMjEwNDE1WhcNMzIwMjI3MjEwNDE1WjBwMQswCQYDVQQGEwJS +VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg +YW5kIENvbW11bmljYXRpb25zMSAwHgYDVQQDDBdSdXNzaWFuIFRydXN0ZWQgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMfFOZ8pUAL3+r2n +qqE0Zp52selXsKGFYoG0GM5bwz1bSFtCt+AZQMhkWQheI3poZAToYJu69pHLKS6Q +XBiwBC1cvzYmUYKMYZC7jE5YhEU2bSL0mX7NaMxMDmH2/NwuOVRj8OImVa5s1F4U +zn4Kv3PFlDBjjSjXKVY9kmjUBsXQrIHeaqmUIsPIlNWUnimXS0I0abExqkbdrXbX +YwCOXhOO2pDUx3ckmJlCMUGacUTnylyQW2VsJIyIGA8V0xzdaeUXg0VZ6ZmNUr5Y +Ber/EAOLPb8NYpsAhJe2mXjMB/J9HNsoFMBFJ0lLOT/+dQvjbdRZoOT8eqJpWnVD +U+QL/qEZnz57N88OWM3rabJkRNdU/Z7x5SFIM9FrqtN8xewsiBWBI0K6XFuOBOTD +4V08o4TzJ8+Ccq5XlCUW2L48pZNCYuBDfBh7FxkB7qDgGDiaftEkZZfApRg2E+M9 +G8wkNKTPLDc4wH0FDTijhgxR3Y4PiS1HL2Zhw7bD3CbslmEGgfnnZojNkJtcLeBH +BLa52/dSwNU4WWLubaYSiAmA9IUMX1/RpfpxOxd4Ykmhz97oFbUaDJFipIggx5sX +ePAlkTdWnv+RWBxlJwMQ25oEHmRguNYf4Zr/Rxr9cS93Y+mdXIZaBEE0KS2iLRqa +OiWBki9IMQU4phqPOBAaG7A+eP8PAgMBAAGjZjBkMB0GA1UdDgQWBBTh0YHlzlpf +BKrS6badZrHF+qwshzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzAS +BgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF +AAOCAgEAALIY1wkilt/urfEVM5vKzr6utOeDWCUczmWX/RX4ljpRdgF+5fAIS4vH +tmXkqpSCOVeWUrJV9QvZn6L227ZwuE15cWi8DCDal3Ue90WgAJJZMfTshN4OI8cq +W9E4EG9wglbEtMnObHlms8F3CHmrw3k6KmUkWGoa+/ENmcVl68u/cMRl1JbW2bM+ +/3A+SAg2c6iPDlehczKx2oa95QW0SkPPWGuNA/CE8CpyANIhu9XFrj3RQ3EqeRcS +AQQod1RNuHpfETLU/A2gMmvn/w/sx7TB3W5BPs6rprOA37tutPq9u6FTZOcG1Oqj +C/B7yTqgI7rbyvox7DEXoX7rIiEqyNNUguTk/u3SZ4VXE2kmxdmSh3TQvybfbnXV +4JbCZVaqiZraqc7oZMnRoWrXRG3ztbnbes/9qhRGI7PqXqeKJBztxRTEVj8ONs1d +WN5szTwaPIvhkhO3CO5ErU2rVdUr89wKpNXbBODFKRtgxUT70YpmJ46VVaqdAhOZ +D9EUUn4YaeLaS8AjSF/h7UkjOibNc4qVDiPP+rkehFWM66PVnP1Msh93tc+taIfC +EYVMxjh8zNbFuoc7fzvvrFILLe7ifvEIUqSVIC/AzplM/Jxw7buXFeGP1qVCBEHq +391d/9RAfaZ12zkwFsl+IKwE/OZxW8AHa9i1p4GO0YSNuczzEm4= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-bae62b5b7bede326b06856fb67a2a471268f9f404e5b18fdf40261c3e63010b1.crt b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-bae62b5b7bede326b06856fb67a2a471268f9f404e5b18fdf40261c3e63010b1.crt new file mode 100644 index 0000000..9831bee --- /dev/null +++ b/saikyo-branding/assets/ca-certificates/saikyo/saikyo-extra-bae62b5b7bede326b06856fb67a2a471268f9f404e5b18fdf40261c3e63010b1.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFDCCBMGgAwIBAgIQTm1HiybyfWV/do4CXOPTkzAKBggqhQMHAQEDAjCCASQx +HjAcBgkqhkiG9w0BCQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxGDAW +BgNVBAgMDzc3INCc0L7RgdC60LLQsDEZMBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQ +sDEuMCwGA1UECQwl0YPQu9C40YbQsCDQotCy0LXRgNGB0LrQsNGPLCDQtNC+0Lwg +NzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40Lgx +GDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0 +NzQzNzUxLDAqBgNVBAMMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQ +uNC4MB4XDTE4MDcwNjEyMTgwNloXDTM2MDcwMTEyMTgwNlowggEkMR4wHAYJKoZI +hvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQIDA83 +NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAsBgNV +BAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAqBgNV +BAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUD +ZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1MSww +KgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuDBmMB8G +CCqFAwcBAQEBMBMGByqFAwICIwEGCCqFAwcBAQICA0MABEB1OSpFp7milX33EP0i +kge6HbZacYp9fVj8sUa5RWFXrB27SKX5SvtIGepqKev69RSYeHHKR+jT9YX2NuSK +9wONo4IBwjCCAb4wgfUGBSqFA2RwBIHrMIHoDDTQn9CQ0JrQnCDCq9Ca0YDQuNC/ +0YLQvtCf0YDQviBIU03CuyDQstC10YDRgdC40LggMi4wDEPQn9CQ0JogwqvQk9C+ +0LvQvtCy0L3QvtC5INGD0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC9 +0YLRgMK7DDXQl9Cw0LrQu9GO0YfQtdC90LjQtSDihJYgMTQ5LzMvMi8yLzIzINC+ +0YIgMDIuMDMuMjAxOAw00JfQsNC60LvRjtGH0LXQvdC40LUg4oSWIDE0OS83LzYv +MTA1INC+0YIgMjcuMDYuMjAxODA/BgUqhQNkbwQ2DDTQn9CQ0JrQnCDCq9Ca0YDQ +uNC/0YLQvtCf0YDQviBIU03CuyDQstC10YDRgdC40LggMi4wMEMGA1UdIAQ8MDow +CAYGKoUDZHEBMAgGBiqFA2RxAjAIBgYqhQNkcQMwCAYGKoUDZHEEMAgGBiqFA2Rx +BTAGBgRVHSAAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBTCVPG0a9RMt+BtNrQjkPH+wzybBjAKBggqhQMHAQEDAgNBAJr6/eI7rHL7 ++FsQnoH2i6DVxqalbIxLKj05edpZGPLLb6B2PTAMya7pSt9hb8QnFABgsR4IE5gT +4VVkDWbX/n4= +-----END CERTIFICATE----- diff --git a/saikyo-branding/assets/chromium/policies/managed/saikyo.json b/saikyo-branding/assets/chromium/policies/managed/saikyo.json new file mode 100644 index 0000000..1bb2d0b --- /dev/null +++ b/saikyo-branding/assets/chromium/policies/managed/saikyo.json @@ -0,0 +1,7 @@ +{ + "DisableQUIC": true, + "SSLVersionMin": "tls1", + "SSLErrorOverrideAllowed": false, + "EnableOnlineRevocationChecks": true, + "NetworkPredictionOptions": 2 +} diff --git a/saikyo-branding/assets/icons/hicolor/512x512/apps/distributor-logo.png b/saikyo-branding/assets/icons/hicolor/512x512/apps/distributor-logo.png new file mode 100644 index 0000000..cbd394c Binary files /dev/null and b/saikyo-branding/assets/icons/hicolor/512x512/apps/distributor-logo.png differ diff --git a/saikyo-branding/assets/icons/hicolor/512x512/places/start-here-kde.png b/saikyo-branding/assets/icons/hicolor/512x512/places/start-here-kde.png new file mode 100644 index 0000000..cbd394c Binary files /dev/null and b/saikyo-branding/assets/icons/hicolor/512x512/places/start-here-kde.png differ diff --git a/saikyo-branding/assets/kde/SaikyoDark.colors b/saikyo-branding/assets/kde/SaikyoDark.colors new file mode 100644 index 0000000..60f140b --- /dev/null +++ b/saikyo-branding/assets/kde/SaikyoDark.colors @@ -0,0 +1,99 @@ +[General] +ColorScheme=SAIKYO Dark +Name=SAIKYO Dark +shadeSortColumn=true + +[Colors:Button] +BackgroundAlternate=29,85,184 +BackgroundNormal=11,29,68 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[Colors:Complementary] +BackgroundAlternate=16,34,68 +BackgroundNormal=9,18,35 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[Colors:Selection] +BackgroundAlternate=30,85,184 +BackgroundNormal=30,85,184 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=255,255,255 +ForegroundInactive=255,255,255 +ForegroundLink=255,255,255 +ForegroundNegative=255,255,255 +ForegroundNeutral=255,255,255 +ForegroundNormal=255,255,255 +ForegroundPositive=255,255,255 +ForegroundVisited=255,255,255 + +[Colors:Tooltip] +BackgroundAlternate=16,34,68 +BackgroundNormal=11,29,68 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[Colors:View] +BackgroundAlternate=9,18,35 +BackgroundNormal=5,10,20 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[Colors:Window] +BackgroundAlternate=16,34,68 +BackgroundNormal=7,26,58 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[KDE] +contrast=4 + +[WM] +activeBackground=7,26,58 +activeBlend=7,26,58 +activeForeground=232,241,255 +inactiveBackground=5,10,20 +inactiveBlend=5,10,20 +inactiveForeground=183,208,255 diff --git a/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/contents/defaults b/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/contents/defaults new file mode 100644 index 0000000..13d46be --- /dev/null +++ b/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/contents/defaults @@ -0,0 +1,18 @@ +[kdeglobals][General] +ColorScheme=SaikyoDark + +[kdeglobals][KDE] +SingleClick=false + +[plasmarc][Theme] +name=breeze-dark + +[ksplashrc][KSplash] +Engine=KSplashQML +Theme=Saikyo + +[sddm.conf][Theme] +Current=Saikyo + +[Wallpaper][org.kde.image][General] +Image=file:///usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg diff --git a/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/contents/layouts/org.kde.plasma.desktop-layout.js b/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/contents/layouts/org.kde.plasma.desktop-layout.js new file mode 100644 index 0000000..92a71e7 --- /dev/null +++ b/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/contents/layouts/org.kde.plasma.desktop-layout.js @@ -0,0 +1 @@ +// Intentionally minimal: rely on defaults + saikyo-first-login-kde for wallpaper. diff --git a/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/metadata.desktop b/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/metadata.desktop new file mode 100644 index 0000000..40bd248 --- /dev/null +++ b/saikyo-branding/assets/kde/look-and-feel/org.saikyo.desktop/metadata.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=SAIKYO +Comment=SAIKYO OS Plasma Look-and-Feel +X-KDE-PluginInfo-Author=SAIKYO OS +X-KDE-PluginInfo-Email=support@saikyo-os.ru +X-KDE-PluginInfo-Name=org.saikyo.desktop +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=https://saikyo-os.ru +X-KDE-PluginInfo-Category=Plasma Look And Feel +X-KDE-PluginInfo-License=proprietary +X-KDE-ServiceTypes=Plasma/LookAndFeel diff --git a/saikyo-branding/assets/kde/xdg/kcmfonts b/saikyo-branding/assets/kde/xdg/kcmfonts new file mode 100644 index 0000000..a919e29 --- /dev/null +++ b/saikyo-branding/assets/kde/xdg/kcmfonts @@ -0,0 +1,2 @@ +[General] +forceFontDPI=0 diff --git a/saikyo-branding/assets/kde/xdg/kcminputrc b/saikyo-branding/assets/kde/xdg/kcminputrc new file mode 100644 index 0000000..e88aae7 --- /dev/null +++ b/saikyo-branding/assets/kde/xdg/kcminputrc @@ -0,0 +1,3 @@ +[Mouse] +X11LibInputXAccelProfileFlat=true +X11LibInputXAccelSpeed=0 diff --git a/saikyo-branding/assets/kde/xdg/kdeglobals b/saikyo-branding/assets/kde/xdg/kdeglobals new file mode 100644 index 0000000..7a555d5 --- /dev/null +++ b/saikyo-branding/assets/kde/xdg/kdeglobals @@ -0,0 +1,12 @@ +[KDE] +LookAndFeelPackage=org.saikyo.desktop +SingleClick=false + +[General] +ColorScheme=SaikyoDark + +[WM] +activeBackground=7,26,58 +activeForeground=232,241,255 +inactiveBackground=5,10,20 +inactiveForeground=183,208,255 diff --git a/saikyo-branding/assets/kde/xdg/klaunchrc b/saikyo-branding/assets/kde/xdg/klaunchrc new file mode 100644 index 0000000..e51727e --- /dev/null +++ b/saikyo-branding/assets/kde/xdg/klaunchrc @@ -0,0 +1,2 @@ +[BusyCursorSettings] +Bouncing=false diff --git a/saikyo-branding/assets/kde/xdg/plasmarc b/saikyo-branding/assets/kde/xdg/plasmarc new file mode 100644 index 0000000..4399546 --- /dev/null +++ b/saikyo-branding/assets/kde/xdg/plasmarc @@ -0,0 +1,5 @@ +[Theme] +name=breeze-dark + +[Wallpapers] +usersWallpapers=/usr/share/wallpapers/Saikyo/contents/images/ diff --git a/saikyo-branding/assets/ksplash/Saikyo/contents/splash/Splash.qml b/saikyo-branding/assets/ksplash/Saikyo/contents/splash/Splash.qml new file mode 100644 index 0000000..7ca0362 --- /dev/null +++ b/saikyo-branding/assets/ksplash/Saikyo/contents/splash/Splash.qml @@ -0,0 +1,90 @@ +import QtQuick 2.15 + +Rectangle { + id: root + color: "#050505" + + Image { + id: backgroundImage + source: "saikyo_boot_background.png" + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + opacity: 0.8 + } + + Rectangle { + anchors.fill: parent + color: "#000000" + opacity: 0.4 + } + + Image { + id: saikyoLogo + source: "saikyo_logo_white.png" + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: root.height * 0.15 + width: root.width * 0.25 + height: root.height * 0.15 + fillMode: Image.PreserveAspectFit + smooth: true + } + + Text { + id: titleText + text: "SAIKYO OS" + color: "#00ff66" + font.pixelSize: 48 + font.bold: true + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: saikyoLogo.bottom + anchors.topMargin: 20 + } + + Rectangle { + id: progressBarBg + width: root.width * 0.4 + height: 8 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 100 + color: "#333333" + radius: 4 + + Rectangle { + id: progressBar + height: parent.height + color: "#00ff66" + radius: 4 + + SequentialAnimation on width { + running: true + loops: Animation.Infinite + + PropertyAnimation { + from: 0 + to: progressBarBg.width + duration: 2000 + easing.type: Easing.InOutQuad + } + PropertyAnimation { + from: progressBarBg.width + to: 0 + duration: 2000 + easing.type: Easing.InOutQuad + } + } + } + } + + Text { + id: loadingText + text: "Загрузка..." + color: "#ffffff" + font.pixelSize: 18 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: progressBarBg.top + anchors.bottomMargin: 20 + opacity: 0.8 + } +} diff --git a/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_boot_background.png b/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_boot_background.png new file mode 100644 index 0000000..54b92ef Binary files /dev/null and b/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_boot_background.png differ diff --git a/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_logo_pixel.png b/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_logo_pixel.png new file mode 100644 index 0000000..b664d4d Binary files /dev/null and b/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_logo_pixel.png differ diff --git a/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_logo_white.png b/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_logo_white.png new file mode 100644 index 0000000..cbed9a0 Binary files /dev/null and b/saikyo-branding/assets/ksplash/Saikyo/contents/splash/saikyo_logo_white.png differ diff --git a/saikyo-branding/assets/ksplash/Saikyo/metadata.desktop b/saikyo-branding/assets/ksplash/Saikyo/metadata.desktop new file mode 100644 index 0000000..12ce70c --- /dev/null +++ b/saikyo-branding/assets/ksplash/Saikyo/metadata.desktop @@ -0,0 +1,15 @@ +[Desktop Entry] +Name=SAIKYO +Comment=SAIKYO OS Splash Screen +X-KDE-PluginInfo-Author=SAIKYO OS +X-KDE-PluginInfo-Email=support@saikyo-os.ru +X-KDE-PluginInfo-Name=Saikyo +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=https://saikyo-os.ru +X-KDE-PluginInfo-Category=KSplash +X-KDE-PluginInfo-License=proprietary +X-KDE-ServiceTypes=Plasma/KSplash + +X-KSplash-Mode=QML +X-KSplash-Engine=KSplashQML +X-KSplash-Theme=Saikyo diff --git a/saikyo-branding/assets/ksplash/ksplashrc b/saikyo-branding/assets/ksplash/ksplashrc new file mode 100644 index 0000000..a86b60e --- /dev/null +++ b/saikyo-branding/assets/ksplash/ksplashrc @@ -0,0 +1,3 @@ +[KSplash] +Engine=KSplashQML +Theme=Saikyo diff --git a/saikyo-branding/assets/logos/saikyo-logo.svg b/saikyo-branding/assets/logos/saikyo-logo.svg new file mode 100644 index 0000000..a0cbab6 --- /dev/null +++ b/saikyo-branding/assets/logos/saikyo-logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + S + diff --git a/saikyo-branding/assets/logos/saikyo-logo.txt b/saikyo-branding/assets/logos/saikyo-logo.txt new file mode 100644 index 0000000..4d56ce8 --- /dev/null +++ b/saikyo-branding/assets/logos/saikyo-logo.txt @@ -0,0 +1,2 @@ +Placeholder for Saikyo OS logo asset. +Replace with real SVG/PNG and update debian/install accordingly. diff --git a/saikyo-branding/assets/plasmoids/org.saikyo.license/contents/ui/main.qml b/saikyo-branding/assets/plasmoids/org.saikyo.license/contents/ui/main.qml new file mode 100644 index 0000000..beb0eed --- /dev/null +++ b/saikyo-branding/assets/plasmoids/org.saikyo.license/contents/ui/main.qml @@ -0,0 +1,150 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PC3 + +Item { + id: root + width: 160 + height: 32 + + function parseKv(text) { + var obj = {} + var lines = text.split("\n") + for (var i = 0; i < lines.length; i++) { + var line = lines[i] + if (!line) continue + var idx = line.indexOf("=") + if (idx <= 0) continue + var k = line.substring(0, idx) + var v = line.substring(idx + 1) + obj[k] = v + } + return obj + } + + function humanDuration(sec) { + var s = parseInt(sec) + if (isNaN(s) || s < 0) return "—" + var d = Math.floor(s / 86400) + var h = Math.floor((s % 86400) / 3600) + var m = Math.floor((s % 3600) / 60) + if (d > 0) return d + "д " + h + "ч" + if (h > 0) return h + "ч " + m + "м" + return m + "м" + } + + function compute() { + var kv = parseKv(execDS.data["stdout"] || "") + + var st = kv["status"] || "" + var expired = kv["expired"] || "" + var validUntil = kv["valid_until"] || "" + var trialRem = kv["trial_remaining_seconds"] || "" + + var state = "UNKNOWN" + var rem = "—" + + if (st === "trial") { + state = "TRIAL" + rem = humanDuration(trialRem) + } else if (st === "missing") { + state = "MISSING" + rem = "—" + } else { + if (expired === "yes") state = "EXPIRED" + else if (validUntil !== "") state = "ACTIVE" + } + + labelState.text = state + labelRem.text = rem + + if (state === "ACTIVE") { + badge.color = "#2ecc71" + } else if (state === "TRIAL") { + badge.color = "#f1c40f" + } else if (state === "EXPIRED") { + badge.color = "#e74c3c" + } else if (state === "MISSING") { + badge.color = "#e67e22" + } else { + badge.color = "#95a5a6" + } + } + + PlasmaCore.DataSource { + id: execDS + engine: "executable" + connectedSources: ["/usr/sbin/saikyo-license status"] + interval: 60000 + onNewData: { + compute() + } + } + + Component.onCompleted: { + compute() + } + + Rectangle { + id: badge + anchors.fill: parent + radius: 6 + color: "#95a5a6" + opacity: 0.18 + + border.width: 1 + border.color: badge.color + } + + RowLayout { + anchors.fill: parent + anchors.margins: 6 + spacing: 6 + + Rectangle { + width: 10 + height: 10 + radius: 5 + color: badge.color + opacity: 0.95 + Layout.alignment: Qt.AlignVCenter + } + + PC3.Label { + id: labelState + text: "…" + font.bold: true + Layout.alignment: Qt.AlignVCenter + } + + PC3.Label { + id: labelRem + text: "—" + opacity: 0.85 + Layout.alignment: Qt.AlignVCenter + } + + Item { Layout.fillWidth: true } + + PC3.ToolButton { + text: "⚙" + display: PC3.AbstractButton.TextOnly + onClicked: { + // best-effort open GUI + execDS.connectedSources = ["/usr/bin/saikyo-license-kde"] + execDS.connectedSources = ["/usr/sbin/saikyo-license status"] + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + execDS.connectedSources = ["/usr/bin/saikyo-license-kde"] + execDS.connectedSources = ["/usr/sbin/saikyo-license status"] + } + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + } +} diff --git a/saikyo-branding/assets/plasmoids/org.saikyo.license/metadata.json b/saikyo-branding/assets/plasmoids/org.saikyo.license/metadata.json new file mode 100644 index 0000000..1419e80 --- /dev/null +++ b/saikyo-branding/assets/plasmoids/org.saikyo.license/metadata.json @@ -0,0 +1,22 @@ +{ + "KPackageStructure": "Plasma/Applet", + "KPlugin": { + "Id": "org.saikyo.license", + "Name": "SAIKYO OS License", + "Name[ru]": "Лицензия SAIKYO OS", + "Description": "Shows SAIKYO OS license status and remaining time", + "Description[ru]": "Показывает статус лицензии SAIKYO OS и оставшееся время", + "Icon": "preferences-system", + "Version": "1.0", + "License": "Proprietary", + "Authors": [ + { + "Name": "SAIKYO OS", + "Email": "support@saikyo-os.ru" + } + ] + }, + "X-Plasma-API": "declarativeappletscript", + "X-Plasma-MainScript": "ui/main.qml", + "X-Plasma-NotificationArea": false +} diff --git a/saikyo-branding/assets/sddm/10-saikyo-theme.conf b/saikyo-branding/assets/sddm/10-saikyo-theme.conf new file mode 100644 index 0000000..c78d6a3 --- /dev/null +++ b/saikyo-branding/assets/sddm/10-saikyo-theme.conf @@ -0,0 +1,2 @@ +[Theme] +Current=Saikyo diff --git a/saikyo-branding/assets/sddm/Saikyo/Main.qml b/saikyo-branding/assets/sddm/Saikyo/Main.qml new file mode 100644 index 0000000..8ef8e8c --- /dev/null +++ b/saikyo-branding/assets/sddm/Saikyo/Main.qml @@ -0,0 +1,186 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.15 +import SddmComponents 2.0 + +Item { + id: root + width: 1920 + height: 1080 + + property string backgroundPath: config.String("Theme", "Background", "") + property string logoPath: config.String("Theme", "Logo", "") + + Image { + id: bg + anchors.fill: parent + source: root.backgroundPath + fillMode: Image.PreserveAspectCrop + smooth: true + cache: true + } + + Rectangle { + anchors.fill: parent + color: "#050a14" + opacity: 0.55 + } + + ColumnLayout { + id: panel + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + spacing: 18 + width: Math.min(parent.width * 0.42, 560) + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 180 + Layout.preferredWidth: 180 + + Image { + anchors.fill: parent + source: root.logoPath + fillMode: Image.PreserveAspectFit + smooth: true + cache: true + } + } + + Label { + Layout.alignment: Qt.AlignHCenter + text: "SAIKYO OS" + font.pixelSize: 38 + font.weight: Font.DemiBold + color: "#e8f1ff" + } + + ComboBox { + id: userBox + Layout.fillWidth: true + model: userModel + textRole: "name" + currentIndex: userModel.lastIndex + focus: true + background: Rectangle { + radius: 10 + color: "#0b1d44" + border.color: "#153a85" + border.width: 1 + } + contentItem: Text { + leftPadding: 14 + rightPadding: 14 + text: userBox.displayText + color: "#e8f1ff" + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + + TextField { + id: password + Layout.fillWidth: true + echoMode: TextInput.Password + placeholderText: "Пароль" + inputMethodHints: Qt.ImhNoPredictiveText + background: Rectangle { + radius: 10 + color: "#0b1d44" + border.color: "#153a85" + border.width: 1 + } + color: "#e8f1ff" + padding: 14 + onAccepted: loginButton.clicked() + } + + Button { + id: loginButton + Layout.fillWidth: true + text: "Войти" + enabled: password.text.length > 0 + background: Rectangle { + radius: 10 + color: loginButton.enabled ? "#1e55b8" : "#163b7a" + } + contentItem: Text { + text: loginButton.text + color: "#ffffff" + font.pixelSize: 18 + font.weight: Font.DemiBold + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + sddm.login(userModel.get(userBox.currentIndex).name, password.text, sessionModel.lastIndex) + } + } + + Label { + id: msg + Layout.fillWidth: true + text: (sddm.loginFailed ? "Неверный логин или пароль" : "") + color: "#ff8a8a" + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + + RowLayout { + Layout.fillWidth: true + spacing: 12 + + ComboBox { + id: sessionBox + Layout.fillWidth: true + model: sessionModel + textRole: "name" + currentIndex: sessionModel.lastIndex + background: Rectangle { + radius: 10 + color: "#0b1d44" + border.color: "#153a85" + border.width: 1 + } + contentItem: Text { + leftPadding: 14 + rightPadding: 14 + text: sessionBox.displayText + color: "#e8f1ff" + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + + Button { + text: "Выключить" + Layout.preferredWidth: 160 + background: Rectangle { + radius: 10 + color: "#0b1d44" + border.color: "#153a85" + border.width: 1 + } + contentItem: Text { + text: parent.text + color: "#e8f1ff" + font.pixelSize: 16 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: sddm.powerOff() + } + } + } + + Label { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + text: "SAIKYO" + color: "#b7d0ff" + opacity: 0.55 + font.pixelSize: 14 + letterSpacing: 4 + } +} diff --git a/saikyo-branding/assets/sddm/Saikyo/theme.conf b/saikyo-branding/assets/sddm/Saikyo/theme.conf new file mode 100644 index 0000000..fd5c962 --- /dev/null +++ b/saikyo-branding/assets/sddm/Saikyo/theme.conf @@ -0,0 +1,15 @@ +[General] +Type=sddm-theme +Name=SAIKYO +Description=SAIKYO OS login screen +Author=SAIKYO OS +Copyright=SAIKYO OS +License=proprietary +Version=1.0 +Website=https://saikyo-os.ru +MainScript=Main.qml +ConfigFile=theme.conf + +[Theme] +Background=/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_boot_background.png +Logo=/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_white.png diff --git a/saikyo-branding/assets/secure-boot/.keep b/saikyo-branding/assets/secure-boot/.keep new file mode 100644 index 0000000..48cdce8 --- /dev/null +++ b/saikyo-branding/assets/secure-boot/.keep @@ -0,0 +1 @@ +placeholder diff --git a/saikyo-branding/assets/secure-boot/saikyo-mok.der b/saikyo-branding/assets/secure-boot/saikyo-mok.der new file mode 100644 index 0000000..adcd5a2 Binary files /dev/null and b/saikyo-branding/assets/secure-boot/saikyo-mok.der differ diff --git a/saikyo-branding/assets/wallpapers/custom/1479028260138356477.jpg b/saikyo-branding/assets/wallpapers/custom/1479028260138356477.jpg new file mode 100644 index 0000000..0346cca Binary files /dev/null and b/saikyo-branding/assets/wallpapers/custom/1479028260138356477.jpg differ diff --git a/saikyo-branding/assets/wallpapers/custom/1646353466_1-abrakadabra-fun-p-piksel-art-yaponiya-3.jpg b/saikyo-branding/assets/wallpapers/custom/1646353466_1-abrakadabra-fun-p-piksel-art-yaponiya-3.jpg new file mode 100644 index 0000000..29fc3a7 Binary files /dev/null and b/saikyo-branding/assets/wallpapers/custom/1646353466_1-abrakadabra-fun-p-piksel-art-yaponiya-3.jpg differ diff --git a/saikyo-branding/assets/wallpapers/custom/1671067058_14-zefirka-club-p-piksel-oboi-15.png b/saikyo-branding/assets/wallpapers/custom/1671067058_14-zefirka-club-p-piksel-oboi-15.png new file mode 100644 index 0000000..eeee4f7 Binary files /dev/null and b/saikyo-branding/assets/wallpapers/custom/1671067058_14-zefirka-club-p-piksel-oboi-15.png differ diff --git a/saikyo-branding/assets/wallpapers/custom/dark-pixel-s57l9jttoenhhcs2.jpg b/saikyo-branding/assets/wallpapers/custom/dark-pixel-s57l9jttoenhhcs2.jpg new file mode 100644 index 0000000..9559881 Binary files /dev/null and b/saikyo-branding/assets/wallpapers/custom/dark-pixel-s57l9jttoenhhcs2.jpg differ diff --git a/saikyo-branding/assets/wallpapers/custom/images.jpg b/saikyo-branding/assets/wallpapers/custom/images.jpg new file mode 100644 index 0000000..9470bc7 Binary files /dev/null and b/saikyo-branding/assets/wallpapers/custom/images.jpg differ diff --git a/saikyo-branding/assets/wallpapers/custom/yjhgn.png b/saikyo-branding/assets/wallpapers/custom/yjhgn.png new file mode 100644 index 0000000..61f6b71 Binary files /dev/null and b/saikyo-branding/assets/wallpapers/custom/yjhgn.png differ diff --git a/saikyo-branding/assets/wallpapers/saikyo-default.svg b/saikyo-branding/assets/wallpapers/saikyo-default.svg new file mode 100644 index 0000000..488937f --- /dev/null +++ b/saikyo-branding/assets/wallpapers/saikyo-default.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + S + + + + + + diff --git a/saikyo-branding/assets/wallpapers/saikyo-default.txt b/saikyo-branding/assets/wallpapers/saikyo-default.txt new file mode 100644 index 0000000..e444106 --- /dev/null +++ b/saikyo-branding/assets/wallpapers/saikyo-default.txt @@ -0,0 +1,2 @@ +Placeholder for Saikyo OS default wallpaper. +Replace this file with an actual image (e.g. PNG/JPG) and update debian/install accordingly. diff --git a/saikyo-branding/assets/wayland-sessions/saikyo-plasma.desktop b/saikyo-branding/assets/wayland-sessions/saikyo-plasma.desktop new file mode 100644 index 0000000..cfc81e4 --- /dev/null +++ b/saikyo-branding/assets/wayland-sessions/saikyo-plasma.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Exec=startplasma-wayland +TryExec=startplasma-wayland +DesktopNames=KDE +Name=SAIKYO OS +Name[ru]=SAIKYO OS +Comment=SAIKYO OS Desktop Environment +Comment[ru]=Рабочая среда SAIKYO OS diff --git a/saikyo-branding/debian/.debhelper/generated/saikyo-branding/dh_installchangelogs.dch.trimmed b/saikyo-branding/debian/.debhelper/generated/saikyo-branding/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..be2ba1e --- /dev/null +++ b/saikyo-branding/debian/.debhelper/generated/saikyo-branding/dh_installchangelogs.dch.trimmed @@ -0,0 +1,9 @@ +saikyo-branding (1.0.8) stable; urgency=medium + + * Full Plasma rebrand defaults (Look-and-Feel, /etc/xdg defaults, color scheme, first-login apply). + * Update branding assets (autostart phase, kwalletrc defaults, default avatar). + * Add KDE panel license widget (plasmoid) and auto-pin key apps. + * Add Chromium managed policies for improved compatibility (GOST sites). + * Bundle additional Russian CA certificates for system trust store. + + -- SAIKYO OS Mon, 20 Jan 2026 16:30:00 +0000 diff --git a/saikyo-branding/debian/.debhelper/generated/saikyo-branding/installed-by-dh_install b/saikyo-branding/debian/.debhelper/generated/saikyo-branding/installed-by-dh_install new file mode 100644 index 0000000..5f2b02a --- /dev/null +++ b/saikyo-branding/debian/.debhelper/generated/saikyo-branding/installed-by-dh_install @@ -0,0 +1,29 @@ +./assets/wallpapers/saikyo-default.svg +./assets/wallpapers/saikyo-default.svg +./assets/logos/saikyo-logo.svg +./assets/secure-boot/saikyo-mok.der +./skel/.config/saikyo-os/README +./assets/icons/hicolor/512x512/apps/distributor-logo.png +./assets/icons/hicolor/512x512/places/start-here-kde.png +./assets/kde/SaikyoDark.colors +./assets/kde/xdg/kdeglobals +./assets/kde/xdg/plasmarc +./assets/kde/xdg/kcminputrc +./assets/kde/xdg/klaunchrc +./assets/kde/xdg/kcmfonts +./assets/kde/look-and-feel/org.saikyo.desktop/ +./assets/bin/saikyo-first-login-kde +./assets/bin/saikyo-evidence +./assets/autostart/saikyo-first-login-kde.desktop +./assets/sddm/Saikyo/theme.conf +./assets/sddm/Saikyo/Main.qml +./assets/sddm/10-saikyo-theme.conf +./assets/ksplash/Saikyo/metadata.desktop +./assets/ksplash/Saikyo/contents/splash/Splash.qml +./assets/ksplash/Saikyo/contents/splash/saikyo_boot_background.png +./assets/ksplash/Saikyo/contents/splash/saikyo_logo_pixel.png +./assets/ksplash/Saikyo/contents/splash/saikyo_logo_white.png +./assets/ksplash/ksplashrc +./assets/plasmoids/org.saikyo.license/ +./assets/chromium/policies/managed/saikyo.json +./assets/ca-certificates/saikyo/ diff --git a/saikyo-branding/debian/.debhelper/generated/saikyo-branding/installed-by-dh_installdocs b/saikyo-branding/debian/.debhelper/generated/saikyo-branding/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-branding/debian/changelog b/saikyo-branding/debian/changelog new file mode 100644 index 0000000..be2ba1e --- /dev/null +++ b/saikyo-branding/debian/changelog @@ -0,0 +1,9 @@ +saikyo-branding (1.0.8) stable; urgency=medium + + * Full Plasma rebrand defaults (Look-and-Feel, /etc/xdg defaults, color scheme, first-login apply). + * Update branding assets (autostart phase, kwalletrc defaults, default avatar). + * Add KDE panel license widget (plasmoid) and auto-pin key apps. + * Add Chromium managed policies for improved compatibility (GOST sites). + * Bundle additional Russian CA certificates for system trust store. + + -- SAIKYO OS Mon, 20 Jan 2026 16:30:00 +0000 diff --git a/saikyo-branding/debian/control b/saikyo-branding/debian/control new file mode 100644 index 0000000..0dc86cb --- /dev/null +++ b/saikyo-branding/debian/control @@ -0,0 +1,13 @@ +Source: saikyo-branding +Section: misc +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-branding +Architecture: all +Depends: ${misc:Depends}, ca-certificates +Description: SAIKYO OS branding + Wallpapers, logos and basic desktop defaults for SAIKYO OS branding. diff --git a/saikyo-branding/debian/copyright b/saikyo-branding/debian/copyright new file mode 100644 index 0000000..3157e37 --- /dev/null +++ b/saikyo-branding/debian/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-branding +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-branding/debian/debhelper-build-stamp b/saikyo-branding/debian/debhelper-build-stamp new file mode 100644 index 0000000..c462874 --- /dev/null +++ b/saikyo-branding/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-branding diff --git a/saikyo-branding/debian/files b/saikyo-branding/debian/files new file mode 100644 index 0000000..7d5111d --- /dev/null +++ b/saikyo-branding/debian/files @@ -0,0 +1,2 @@ +saikyo-branding_1.0.8_all.deb misc optional +saikyo-branding_1.0.8_amd64.buildinfo misc optional diff --git a/saikyo-branding/debian/install b/saikyo-branding/debian/install new file mode 100644 index 0000000..66b24b3 --- /dev/null +++ b/saikyo-branding/debian/install @@ -0,0 +1,38 @@ +assets/wallpapers/saikyo-default.svg usr/share/saikyo-os/wallpapers/ +assets/wallpapers/saikyo-default.svg usr/share/wallpapers/Saikyo/contents/images/ +assets/logos/saikyo-logo.svg usr/share/saikyo-os/logos/ +assets/secure-boot/saikyo-mok.der usr/share/saikyo-os/secure-boot/ +skel/.config/saikyo-os/README usr/share/saikyo-os/skel/.config/saikyo-os/ + +assets/icons/hicolor/512x512/apps/distributor-logo.png usr/share/icons/hicolor/512x512/apps/ +assets/icons/hicolor/512x512/places/start-here-kde.png usr/share/icons/hicolor/512x512/places/ +assets/kde/SaikyoDark.colors usr/share/color-schemes/ + +assets/kde/xdg/kdeglobals etc/xdg/ +assets/kde/xdg/plasmarc etc/xdg/ +assets/kde/xdg/kcminputrc etc/xdg/ +assets/kde/xdg/klaunchrc etc/xdg/ +assets/kde/xdg/kcmfonts etc/xdg/ + +assets/kde/look-and-feel/org.saikyo.desktop/ usr/share/plasma/look-and-feel/ + +assets/bin/saikyo-first-login-kde usr/bin/ +assets/bin/saikyo-evidence usr/bin/ +assets/autostart/saikyo-first-login-kde.desktop etc/xdg/autostart/ + +assets/sddm/Saikyo/theme.conf usr/share/sddm/themes/Saikyo/ +assets/sddm/Saikyo/Main.qml usr/share/sddm/themes/Saikyo/ +assets/sddm/10-saikyo-theme.conf etc/sddm.conf.d/ + +assets/ksplash/Saikyo/metadata.desktop usr/share/ksplash/Themes/Saikyo/ +assets/ksplash/Saikyo/contents/splash/Splash.qml usr/share/ksplash/Themes/Saikyo/contents/splash/ +assets/ksplash/Saikyo/contents/splash/saikyo_boot_background.png usr/share/ksplash/Themes/Saikyo/contents/splash/ +assets/ksplash/Saikyo/contents/splash/saikyo_logo_pixel.png usr/share/ksplash/Themes/Saikyo/contents/splash/ +assets/ksplash/Saikyo/contents/splash/saikyo_logo_white.png usr/share/ksplash/Themes/Saikyo/contents/splash/ +assets/ksplash/ksplashrc etc/xdg/ + +assets/plasmoids/org.saikyo.license/ usr/share/plasma/plasmoids/ + +assets/chromium/policies/managed/saikyo.json etc/chromium/policies/managed/ + +assets/ca-certificates/saikyo/ usr/share/saikyo-os/ca-certificates/ diff --git a/saikyo-branding/debian/postinst b/saikyo-branding/debian/postinst new file mode 100644 index 0000000..f847c2c --- /dev/null +++ b/saikyo-branding/debian/postinst @@ -0,0 +1,18 @@ +#!/bin/sh +set -e + +if [ "${1:-}" = "configure" ]; then + SRC_DIR="/usr/share/saikyo-os/ca-certificates/saikyo" + DST_DIR="/usr/local/share/ca-certificates/saikyo" + + if [ -d "${SRC_DIR}" ]; then + mkdir -p "${DST_DIR}" 2>/dev/null || true + cp -f "${SRC_DIR}"/*.crt "${DST_DIR}/" 2>/dev/null || true + fi + + if command -v update-ca-certificates >/dev/null 2>&1; then + update-ca-certificates >/dev/null 2>&1 || true + fi +fi + +exit 0 diff --git a/saikyo-branding/debian/rules b/saikyo-branding/debian/rules new file mode 100755 index 0000000..0fc3651 --- /dev/null +++ b/saikyo-branding/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: diff --git a/saikyo-branding/debian/saikyo-branding.substvars b/saikyo-branding/debian/saikyo-branding.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-branding/debian/saikyo-branding/DEBIAN/conffiles b/saikyo-branding/debian/saikyo-branding/DEBIAN/conffiles new file mode 100644 index 0000000..b0136cd --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/DEBIAN/conffiles @@ -0,0 +1,9 @@ +/etc/chromium/policies/managed/saikyo.json +/etc/sddm.conf.d/10-saikyo-theme.conf +/etc/xdg/autostart/saikyo-first-login-kde.desktop +/etc/xdg/kcmfonts +/etc/xdg/kcminputrc +/etc/xdg/kdeglobals +/etc/xdg/klaunchrc +/etc/xdg/ksplashrc +/etc/xdg/plasmarc diff --git a/saikyo-branding/debian/saikyo-branding/DEBIAN/control b/saikyo-branding/debian/saikyo-branding/DEBIAN/control new file mode 100644 index 0000000..04a0c45 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/DEBIAN/control @@ -0,0 +1,10 @@ +Package: saikyo-branding +Version: 1.0.8 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 2202 +Depends: ca-certificates +Section: misc +Priority: optional +Description: SAIKYO OS branding + Wallpapers, logos and basic desktop defaults for SAIKYO OS branding. diff --git a/saikyo-branding/debian/saikyo-branding/DEBIAN/md5sums b/saikyo-branding/debian/saikyo-branding/DEBIAN/md5sums new file mode 100644 index 0000000..1336449 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/DEBIAN/md5sums @@ -0,0 +1,29 @@ +b28caae5c96d0f70b0afd87a503edea6 usr/bin/saikyo-evidence +0ae82fc7aa71a2911eae85b39d86d3fb usr/bin/saikyo-first-login-kde +344a60b8200e4226ba5862bdc5f4ae89 usr/share/color-schemes/SaikyoDark.colors +6b113864f5eedae7560a5c9a84326798 usr/share/doc/saikyo-branding/changelog.gz +ebcec325ce2ae80755d1544697494241 usr/share/doc/saikyo-branding/copyright +6278667dae4af96e82385f142be365fa usr/share/icons/hicolor/512x512/apps/distributor-logo.png +6278667dae4af96e82385f142be365fa usr/share/icons/hicolor/512x512/places/start-here-kde.png +fbd78eb2e7142b8b3361db012c0c8221 usr/share/ksplash/Themes/Saikyo/contents/splash/Splash.qml +439b3c7802aec8020d36a2d22c74b1b1 usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_boot_background.png +fb3ba0941ce35eb7ab9a846671bbcf46 usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_pixel.png +d8f77eb5c3553b7293e5c84e471b01af usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_white.png +d7d07e27420f0a68077143cb515ed501 usr/share/ksplash/Themes/Saikyo/metadata.desktop +8ad99d15fe6760fd6b816b3dd1cac20a usr/share/plasma/look-and-feel/org.saikyo.desktop/contents/defaults +94936bb6c4fa7f7fa9800451465450ef usr/share/plasma/look-and-feel/org.saikyo.desktop/contents/layouts/org.kde.plasma.desktop-layout.js +bf5ec49c3f8098634d940c382b7a7153 usr/share/plasma/look-and-feel/org.saikyo.desktop/metadata.desktop +cb248615a22cd230e29687e7f1f2be33 usr/share/plasma/plasmoids/org.saikyo.license/contents/ui/main.qml +a9b2e41e1b5e840ddbbb87cd7aa664bc usr/share/plasma/plasmoids/org.saikyo.license/metadata.json +23d0ab62e26991f71b9d34875a18460e usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-588e4226ca6a9ca0fe3790f5b4ed5ff286a80d131849f572a200592463676c1b.crt +c6e18b50bd93e276ddb9528b4e45d48a usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-5b51db721b7c34958ed7432ae917a91297dd37508b2cae4f858ffbac6bc525ef.crt +8783c09da939fa1d53d98bfee83b9784 usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-71be2cef4964fda5a6302e2b55b741e40d25cef17df527f8abc350094973fa34.crt +07ecf619d50f80d67ccb5c00a7ad0d59 usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-936a43fea6e8e525bcc0f81acd9c3d21b4fc4b9b68acea7906d698005afc6504.crt +c2ff76f07082275d8e5a64a2b33a5cbc usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-bae62b5b7bede326b06856fb67a2a471268f9f404e5b18fdf40261c3e63010b1.crt +76db85a8ffdafe93e1b90a12dffef88b usr/share/saikyo-os/logos/saikyo-logo.svg +87d08841d6decd6e62b39793008b496b usr/share/saikyo-os/secure-boot/saikyo-mok.der +fa56ca1dce96a70ab64857e7a6196eb5 usr/share/saikyo-os/skel/.config/saikyo-os/README +ab6f2b2fc14928cf41801c5e4556eb3b usr/share/saikyo-os/wallpapers/saikyo-default.svg +d55c15ed6adc60f70a60472947ddd492 usr/share/sddm/themes/Saikyo/Main.qml +4d124cab323147c2b84c8542edfebb88 usr/share/sddm/themes/Saikyo/theme.conf +ab6f2b2fc14928cf41801c5e4556eb3b usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg diff --git a/saikyo-branding/debian/saikyo-branding/DEBIAN/postinst b/saikyo-branding/debian/saikyo-branding/DEBIAN/postinst new file mode 100755 index 0000000..f847c2c --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/DEBIAN/postinst @@ -0,0 +1,18 @@ +#!/bin/sh +set -e + +if [ "${1:-}" = "configure" ]; then + SRC_DIR="/usr/share/saikyo-os/ca-certificates/saikyo" + DST_DIR="/usr/local/share/ca-certificates/saikyo" + + if [ -d "${SRC_DIR}" ]; then + mkdir -p "${DST_DIR}" 2>/dev/null || true + cp -f "${SRC_DIR}"/*.crt "${DST_DIR}/" 2>/dev/null || true + fi + + if command -v update-ca-certificates >/dev/null 2>&1; then + update-ca-certificates >/dev/null 2>&1 || true + fi +fi + +exit 0 diff --git a/saikyo-branding/debian/saikyo-branding/etc/chromium/policies/managed/saikyo.json b/saikyo-branding/debian/saikyo-branding/etc/chromium/policies/managed/saikyo.json new file mode 100644 index 0000000..1bb2d0b --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/chromium/policies/managed/saikyo.json @@ -0,0 +1,7 @@ +{ + "DisableQUIC": true, + "SSLVersionMin": "tls1", + "SSLErrorOverrideAllowed": false, + "EnableOnlineRevocationChecks": true, + "NetworkPredictionOptions": 2 +} diff --git a/saikyo-branding/debian/saikyo-branding/etc/sddm.conf.d/10-saikyo-theme.conf b/saikyo-branding/debian/saikyo-branding/etc/sddm.conf.d/10-saikyo-theme.conf new file mode 100644 index 0000000..c78d6a3 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/sddm.conf.d/10-saikyo-theme.conf @@ -0,0 +1,2 @@ +[Theme] +Current=Saikyo diff --git a/saikyo-branding/debian/saikyo-branding/etc/xdg/autostart/saikyo-first-login-kde.desktop b/saikyo-branding/debian/saikyo-branding/etc/xdg/autostart/saikyo-first-login-kde.desktop new file mode 100644 index 0000000..c349dfc --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/xdg/autostart/saikyo-first-login-kde.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=SAIKYO OS First Login (KDE) +Exec=/usr/bin/saikyo-first-login-kde +Terminal=false +OnlyShowIn=KDE; +X-KDE-autostart-phase=2 diff --git a/saikyo-branding/debian/saikyo-branding/etc/xdg/kcmfonts b/saikyo-branding/debian/saikyo-branding/etc/xdg/kcmfonts new file mode 100644 index 0000000..a919e29 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/xdg/kcmfonts @@ -0,0 +1,2 @@ +[General] +forceFontDPI=0 diff --git a/saikyo-branding/debian/saikyo-branding/etc/xdg/kcminputrc b/saikyo-branding/debian/saikyo-branding/etc/xdg/kcminputrc new file mode 100644 index 0000000..e88aae7 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/xdg/kcminputrc @@ -0,0 +1,3 @@ +[Mouse] +X11LibInputXAccelProfileFlat=true +X11LibInputXAccelSpeed=0 diff --git a/saikyo-branding/debian/saikyo-branding/etc/xdg/kdeglobals b/saikyo-branding/debian/saikyo-branding/etc/xdg/kdeglobals new file mode 100644 index 0000000..7a555d5 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/xdg/kdeglobals @@ -0,0 +1,12 @@ +[KDE] +LookAndFeelPackage=org.saikyo.desktop +SingleClick=false + +[General] +ColorScheme=SaikyoDark + +[WM] +activeBackground=7,26,58 +activeForeground=232,241,255 +inactiveBackground=5,10,20 +inactiveForeground=183,208,255 diff --git a/saikyo-branding/debian/saikyo-branding/etc/xdg/klaunchrc b/saikyo-branding/debian/saikyo-branding/etc/xdg/klaunchrc new file mode 100644 index 0000000..e51727e --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/xdg/klaunchrc @@ -0,0 +1,2 @@ +[BusyCursorSettings] +Bouncing=false diff --git a/saikyo-branding/debian/saikyo-branding/etc/xdg/ksplashrc b/saikyo-branding/debian/saikyo-branding/etc/xdg/ksplashrc new file mode 100644 index 0000000..a86b60e --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/xdg/ksplashrc @@ -0,0 +1,3 @@ +[KSplash] +Engine=KSplashQML +Theme=Saikyo diff --git a/saikyo-branding/debian/saikyo-branding/etc/xdg/plasmarc b/saikyo-branding/debian/saikyo-branding/etc/xdg/plasmarc new file mode 100644 index 0000000..4399546 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/etc/xdg/plasmarc @@ -0,0 +1,5 @@ +[Theme] +name=breeze-dark + +[Wallpapers] +usersWallpapers=/usr/share/wallpapers/Saikyo/contents/images/ diff --git a/saikyo-branding/debian/saikyo-branding/usr/bin/saikyo-evidence b/saikyo-branding/debian/saikyo-branding/usr/bin/saikyo-evidence new file mode 100755 index 0000000..eef7885 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/bin/saikyo-evidence @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +set -euo pipefail + +OUT_DIR="/var/log/saikyo-evidence" +TS="$(date -u +%Y%m%dT%H%M%SZ 2>/dev/null || date +%s)" +OUT_FILE="${OUT_DIR}/saikyo-evidence-${TS}.log" + +mkdir -p "${OUT_DIR}" 2>/dev/null || true +chmod 0755 "${OUT_DIR}" 2>/dev/null || true + +dumpln() { + printf '\n==== %s ====\n' "$1" +} + +{ + echo "saikyo_evidence_version=1" + echo "timestamp_utc=${TS}" + + dumpln "os-release" + cat /etc/os-release 2>/dev/null || true + + dumpln "lsb-release" + cat /etc/lsb-release 2>/dev/null || true + + dumpln "uname" + uname -a 2>/dev/null || true + + dumpln "dpkg saikyo packages" + dpkg -l 2>/dev/null | grep -i saikyo || true + + dumpln "apt sources" + if [ -f /etc/apt/sources.list ]; then + echo "--- /etc/apt/sources.list ---" + sed -n '1,200p' /etc/apt/sources.list 2>/dev/null || true + fi + if [ -d /etc/apt/sources.list.d ]; then + echo "--- /etc/apt/sources.list.d ---" + ls -la /etc/apt/sources.list.d 2>/dev/null || true + for f in /etc/apt/sources.list.d/*; do + [ -e "$f" ] || continue + echo "--- $f ---" + sed -n '1,200p' "$f" 2>/dev/null || true + done + fi + + dumpln "apt sources forbidden patterns" + grep -RIn --line-number -E 'deb\.debian\.org|security\.debian\.org|cdrom:|archive\.ubuntu\.com' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null || true + + dumpln "apt periodic config" + if [ -f /etc/apt/apt.conf.d/20saikyo-periodic ]; then + echo "20saikyo-periodic=present" + cat /etc/apt/apt.conf.d/20saikyo-periodic 2>/dev/null || true + else + echo "20saikyo-periodic=MISSING" + fi + + dumpln "auto update units state" + for u in apt-daily.timer apt-daily-upgrade.timer unattended-upgrades.service packagekit-offline-update.timer packagekit-offline-update.service; do + echo "unit=${u} enabled=$(systemctl is-enabled "$u" 2>/dev/null || echo unknown) active=$(systemctl is-active "$u" 2>/dev/null || echo unknown) masked=$(systemctl is-masked "$u" 2>/dev/null || echo unknown)" + done + + dumpln "packagekit state" + echo "packagekit enabled=$(systemctl is-enabled packagekit.service 2>/dev/null || echo unknown) active=$(systemctl is-active packagekit.service 2>/dev/null || echo unknown) masked=$(systemctl is-masked packagekit.service 2>/dev/null || echo unknown)" + + dumpln "system timers (filtered)" + systemctl list-timers --all 2>/dev/null | grep -E 'apt|unattended|packagekit|flatpak|snap' || true + + dumpln "license" + if command -v saikyo-license >/dev/null 2>&1; then + echo "saikyo-license=present" + if /usr/sbin/saikyo-license verify >/dev/null 2>&1; then + echo "license_verify=ok" + else + echo "license_verify=fail" + fi + else + echo "saikyo-license=missing" + fi + + dumpln "secure boot" + if command -v mokutil >/dev/null 2>&1; then + mokutil --sb-state 2>&1 || true + mokutil --list-enrolled 2>/dev/null || true + else + echo "mokutil=missing" + fi + + dumpln "branding files" + ls -la /usr/share/saikyo-os 2>/dev/null || true + ls -la /usr/share/wallpapers/Saikyo/contents/images 2>/dev/null || true + ls -la /usr/share/sddm/themes/Saikyo 2>/dev/null || true + + dumpln "visible Debian/Plasma strings (best effort)" + grep -RIn --line-number -E '\bDebian\b|\bPlasma\b' /usr/share/applications /usr/share/metainfo 2>/dev/null | head -n 50 || true + +} >"${OUT_FILE}" 2>/dev/null + +chmod 0644 "${OUT_FILE}" 2>/dev/null || true +ln -sfn "$(basename "${OUT_FILE}")" "${OUT_DIR}/latest.log" 2>/dev/null || true + +echo "Wrote ${OUT_FILE}" +exit 0 diff --git a/saikyo-branding/debian/saikyo-branding/usr/bin/saikyo-first-login-kde b/saikyo-branding/debian/saikyo-branding/usr/bin/saikyo-first-login-kde new file mode 100755 index 0000000..181169f --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/bin/saikyo-first-login-kde @@ -0,0 +1,185 @@ +#!/usr/bin/env bash +set -euo pipefail + +MARK_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/saikyo-os" +MARK_FILE="${MARK_DIR}/first-login-kde.done" +WALLPAPER_PATH="/usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg" +LOGO_PATH="/usr/share/saikyo-os/logos/saikyo-logo.svg" +AVATAR_PNG="/usr/share/icons/hicolor/512x512/apps/distributor-logo.png" +COLOR_SCHEME="SaikyoDark" + +mkdir -p "${MARK_DIR}" 2>/dev/null || true + +if [[ -f "${MARK_FILE}" ]]; then + exit 0 +fi + +if [[ ! -r "${WALLPAPER_PATH}" ]]; then + exit 0 +fi + +have_cmd() { command -v "$1" >/dev/null 2>&1; } + +apply_look_and_feel() { + if have_cmd lookandfeeltool; then + lookandfeeltool -a org.saikyo.desktop >/dev/null 2>&1 || true + elif have_cmd lookandfeeltool5; then + lookandfeeltool5 -a org.saikyo.desktop >/dev/null 2>&1 || true + fi +} + +apply_color_scheme() { + if have_cmd plasma-apply-colorscheme; then + plasma-apply-colorscheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true + fi + + if have_cmd kwriteconfig5; then + kwriteconfig5 --file kdeglobals --group General --key ColorScheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true + elif have_cmd kwriteconfig6; then + kwriteconfig6 --file kdeglobals --group General --key ColorScheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true + fi +} + +restart_plasma_shell() { + if have_cmd kquitapp5 && have_cmd kstart5; then + kquitapp5 plasmashell >/dev/null 2>&1 || true + nohup kstart5 plasmashell >/dev/null 2>&1 & + elif have_cmd kquitapp6 && have_cmd kstart6; then + kquitapp6 plasmashell >/dev/null 2>&1 || true + nohup kstart6 plasmashell >/dev/null 2>&1 & + fi +} + +set_wallpaper() { + local qdbus_cmd="$1" + "${qdbus_cmd}" org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript " + var Desktops = desktops(); + for (i = 0; i < Desktops.length; i++) { + d = Desktops[i]; + d.wallpaperPlugin = 'org.kde.image'; + d.currentConfigGroup = ['Wallpaper', 'org.kde.image', 'General']; + d.writeConfig('Image', 'file://${WALLPAPER_PATH}'); + } + " >/dev/null 2>&1 || return 1 + return 0 +} + +if have_cmd qdbus; then + set_wallpaper qdbus || true +elif have_cmd qdbus6; then + set_wallpaper qdbus6 || true +fi + +apply_color_scheme +apply_look_and_feel +restart_plasma_shell || true + +add_panel_widgets_and_pins() { + local qdbus_cmd="$1" + "${qdbus_cmd}" org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript " + function firstPanel() { + var ps = panels(); + if (!ps || ps.length === 0) return null; + return ps[0]; + } + + function ensureWidget(panel, pluginId) { + try { + var ws = panel.widgets(); + for (var i = 0; i < ws.length; i++) { + if (ws[i].type === pluginId) return; + } + panel.addWidget(pluginId); + } catch (e) { + } + } + + function ensureTaskManagerPins(panel, launchersStr) { + try { + var ws = panel.widgets(); + for (var i = 0; i < ws.length; i++) { + if (ws[i].type === 'org.kde.plasma.taskmanager') { + var tm = ws[i]; + tm.currentConfigGroup = ['General']; + tm.writeConfig('launchers', launchersStr); + return; + } + } + } catch (e) { + } + } + + var p = firstPanel(); + if (!p) return; + + // License status widget + ensureWidget(p, 'org.saikyo.license'); + + // Pinned launchers in task manager + var launchers = [ + 'applications:org.kde.discover.desktop', + 'applications:systemsettings.desktop', + 'applications:saikyo-license.desktop' + ].join(','); + ensureTaskManagerPins(p, launchers); + " >/dev/null 2>&1 || true +} + +if have_cmd qdbus; then + add_panel_widgets_and_pins qdbus || true +elif have_cmd qdbus6; then + add_panel_widgets_and_pins qdbus6 || true +fi + +desktop_dir="${HOME}/Desktop" +if [[ -r "${HOME}/.config/user-dirs.dirs" ]]; then + d=$(grep -E '^XDG_DESKTOP_DIR=' "${HOME}/.config/user-dirs.dirs" | head -n 1 | cut -d= -f2- | tr -d '"') + d=${d//\$HOME/${HOME}} + if [[ -n "${d}" ]]; then desktop_dir="${d}"; fi +fi + +# Ensure KWallet doesn't require a pre-existing GPG key by default. +# This prevents first-run failures in apps that request KWallet integration. +mkdir -p "${XDG_CONFIG_HOME:-${HOME}/.config}" 2>/dev/null || true +{ + echo "[Wallet]" + echo "UseGpg=false" +} > "${XDG_CONFIG_HOME:-${HOME}/.config}/kwalletrc" 2>/dev/null || true + +# Set a default user avatar for new sessions (shown in KDE menus/panel). +# Prefer PNG (KDE reliably picks it up); fallback to SVG. +if [[ -r "${AVATAR_PNG}" ]]; then + cp -f "${AVATAR_PNG}" "${HOME}/.face.icon" 2>/dev/null || true + chmod 0644 "${HOME}/.face.icon" 2>/dev/null || true +elif [[ -r "${LOGO_PATH}" ]]; then + cp -f "${LOGO_PATH}" "${HOME}/.face.icon" 2>/dev/null || true + chmod 0644 "${HOME}/.face.icon" 2>/dev/null || true +fi + +mkdir -p "${desktop_dir}" 2>/dev/null || true +if [[ -r /usr/share/applications/saikyo-license.desktop ]]; then + cp -f /usr/share/applications/saikyo-license.desktop "${desktop_dir}/SAIKYO OS License.desktop" 2>/dev/null || true + chmod 0755 "${desktop_dir}/SAIKYO OS License.desktop" 2>/dev/null || true +fi + +if [[ -r /usr/share/applications/org.kde.discover.desktop ]]; then + cp -f /usr/share/applications/org.kde.discover.desktop "${desktop_dir}/SAIKYO Discover.desktop" 2>/dev/null || true + chmod 0755 "${desktop_dir}/SAIKYO Discover.desktop" 2>/dev/null || true +fi + +if [[ -r /usr/share/applications/systemsettings.desktop ]]; then + cp -f /usr/share/applications/systemsettings.desktop "${desktop_dir}/System Settings.desktop" 2>/dev/null || true + chmod 0755 "${desktop_dir}/System Settings.desktop" 2>/dev/null || true +fi + +if have_cmd kdialog; then + msg="Добро пожаловать в SAIKYO OS.\n\nНа этой системе действует пробный период 7 дней.\nПосле истечения пробного периода потребуется полная активация лицензии.\n\nРекомендуется выполнить активацию заранее." + if kdialog --title "SAIKYO OS" --icon "${LOGO_PATH}" --yes-label "Открыть активацию" --no-label "Позже" --yesno "${msg}" 2>/dev/null; then + if have_cmd saikyo-license-kde; then + nohup saikyo-license-kde >/dev/null 2>&1 & + fi + fi +fi + +touch "${MARK_FILE}" 2>/dev/null || true +exit 0 diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/color-schemes/SaikyoDark.colors b/saikyo-branding/debian/saikyo-branding/usr/share/color-schemes/SaikyoDark.colors new file mode 100644 index 0000000..60f140b --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/color-schemes/SaikyoDark.colors @@ -0,0 +1,99 @@ +[General] +ColorScheme=SAIKYO Dark +Name=SAIKYO Dark +shadeSortColumn=true + +[Colors:Button] +BackgroundAlternate=29,85,184 +BackgroundNormal=11,29,68 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[Colors:Complementary] +BackgroundAlternate=16,34,68 +BackgroundNormal=9,18,35 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[Colors:Selection] +BackgroundAlternate=30,85,184 +BackgroundNormal=30,85,184 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=255,255,255 +ForegroundInactive=255,255,255 +ForegroundLink=255,255,255 +ForegroundNegative=255,255,255 +ForegroundNeutral=255,255,255 +ForegroundNormal=255,255,255 +ForegroundPositive=255,255,255 +ForegroundVisited=255,255,255 + +[Colors:Tooltip] +BackgroundAlternate=16,34,68 +BackgroundNormal=11,29,68 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[Colors:View] +BackgroundAlternate=9,18,35 +BackgroundNormal=5,10,20 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[Colors:Window] +BackgroundAlternate=16,34,68 +BackgroundNormal=7,26,58 +DecorationFocus=30,85,184 +DecorationHover=36,110,220 +ForegroundActive=232,241,255 +ForegroundInactive=183,208,255 +ForegroundLink=110,160,255 +ForegroundNegative=255,138,138 +ForegroundNeutral=255,210,110 +ForegroundNormal=232,241,255 +ForegroundPositive=120,230,170 +ForegroundVisited=150,120,255 + +[KDE] +contrast=4 + +[WM] +activeBackground=7,26,58 +activeBlend=7,26,58 +activeForeground=232,241,255 +inactiveBackground=5,10,20 +inactiveBlend=5,10,20 +inactiveForeground=183,208,255 diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/doc/saikyo-branding/changelog.gz b/saikyo-branding/debian/saikyo-branding/usr/share/doc/saikyo-branding/changelog.gz new file mode 100644 index 0000000..265d094 Binary files /dev/null and b/saikyo-branding/debian/saikyo-branding/usr/share/doc/saikyo-branding/changelog.gz differ diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/doc/saikyo-branding/copyright b/saikyo-branding/debian/saikyo-branding/usr/share/doc/saikyo-branding/copyright new file mode 100644 index 0000000..3157e37 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/doc/saikyo-branding/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-branding +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/icons/hicolor/512x512/apps/distributor-logo.png b/saikyo-branding/debian/saikyo-branding/usr/share/icons/hicolor/512x512/apps/distributor-logo.png new file mode 100644 index 0000000..cbd394c Binary files /dev/null and b/saikyo-branding/debian/saikyo-branding/usr/share/icons/hicolor/512x512/apps/distributor-logo.png differ diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/icons/hicolor/512x512/places/start-here-kde.png b/saikyo-branding/debian/saikyo-branding/usr/share/icons/hicolor/512x512/places/start-here-kde.png new file mode 100644 index 0000000..cbd394c Binary files /dev/null and b/saikyo-branding/debian/saikyo-branding/usr/share/icons/hicolor/512x512/places/start-here-kde.png differ diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/Splash.qml b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/Splash.qml new file mode 100644 index 0000000..7ca0362 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/Splash.qml @@ -0,0 +1,90 @@ +import QtQuick 2.15 + +Rectangle { + id: root + color: "#050505" + + Image { + id: backgroundImage + source: "saikyo_boot_background.png" + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + opacity: 0.8 + } + + Rectangle { + anchors.fill: parent + color: "#000000" + opacity: 0.4 + } + + Image { + id: saikyoLogo + source: "saikyo_logo_white.png" + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: root.height * 0.15 + width: root.width * 0.25 + height: root.height * 0.15 + fillMode: Image.PreserveAspectFit + smooth: true + } + + Text { + id: titleText + text: "SAIKYO OS" + color: "#00ff66" + font.pixelSize: 48 + font.bold: true + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: saikyoLogo.bottom + anchors.topMargin: 20 + } + + Rectangle { + id: progressBarBg + width: root.width * 0.4 + height: 8 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 100 + color: "#333333" + radius: 4 + + Rectangle { + id: progressBar + height: parent.height + color: "#00ff66" + radius: 4 + + SequentialAnimation on width { + running: true + loops: Animation.Infinite + + PropertyAnimation { + from: 0 + to: progressBarBg.width + duration: 2000 + easing.type: Easing.InOutQuad + } + PropertyAnimation { + from: progressBarBg.width + to: 0 + duration: 2000 + easing.type: Easing.InOutQuad + } + } + } + } + + Text { + id: loadingText + text: "Загрузка..." + color: "#ffffff" + font.pixelSize: 18 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: progressBarBg.top + anchors.bottomMargin: 20 + opacity: 0.8 + } +} diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_boot_background.png b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_boot_background.png new file mode 100644 index 0000000..54b92ef Binary files /dev/null and b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_boot_background.png differ diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_pixel.png b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_pixel.png new file mode 100644 index 0000000..b664d4d Binary files /dev/null and b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_pixel.png differ diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_white.png b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_white.png new file mode 100644 index 0000000..cbed9a0 Binary files /dev/null and b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_white.png differ diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/metadata.desktop b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/metadata.desktop new file mode 100644 index 0000000..12ce70c --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/ksplash/Themes/Saikyo/metadata.desktop @@ -0,0 +1,15 @@ +[Desktop Entry] +Name=SAIKYO +Comment=SAIKYO OS Splash Screen +X-KDE-PluginInfo-Author=SAIKYO OS +X-KDE-PluginInfo-Email=support@saikyo-os.ru +X-KDE-PluginInfo-Name=Saikyo +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=https://saikyo-os.ru +X-KDE-PluginInfo-Category=KSplash +X-KDE-PluginInfo-License=proprietary +X-KDE-ServiceTypes=Plasma/KSplash + +X-KSplash-Mode=QML +X-KSplash-Engine=KSplashQML +X-KSplash-Theme=Saikyo diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/contents/defaults b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/contents/defaults new file mode 100644 index 0000000..13d46be --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/contents/defaults @@ -0,0 +1,18 @@ +[kdeglobals][General] +ColorScheme=SaikyoDark + +[kdeglobals][KDE] +SingleClick=false + +[plasmarc][Theme] +name=breeze-dark + +[ksplashrc][KSplash] +Engine=KSplashQML +Theme=Saikyo + +[sddm.conf][Theme] +Current=Saikyo + +[Wallpaper][org.kde.image][General] +Image=file:///usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/contents/layouts/org.kde.plasma.desktop-layout.js b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/contents/layouts/org.kde.plasma.desktop-layout.js new file mode 100644 index 0000000..92a71e7 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/contents/layouts/org.kde.plasma.desktop-layout.js @@ -0,0 +1 @@ +// Intentionally minimal: rely on defaults + saikyo-first-login-kde for wallpaper. diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/metadata.desktop b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/metadata.desktop new file mode 100644 index 0000000..40bd248 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/look-and-feel/org.saikyo.desktop/metadata.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=SAIKYO +Comment=SAIKYO OS Plasma Look-and-Feel +X-KDE-PluginInfo-Author=SAIKYO OS +X-KDE-PluginInfo-Email=support@saikyo-os.ru +X-KDE-PluginInfo-Name=org.saikyo.desktop +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=https://saikyo-os.ru +X-KDE-PluginInfo-Category=Plasma Look And Feel +X-KDE-PluginInfo-License=proprietary +X-KDE-ServiceTypes=Plasma/LookAndFeel diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/plasma/plasmoids/org.saikyo.license/contents/ui/main.qml b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/plasmoids/org.saikyo.license/contents/ui/main.qml new file mode 100644 index 0000000..beb0eed --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/plasmoids/org.saikyo.license/contents/ui/main.qml @@ -0,0 +1,150 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PC3 + +Item { + id: root + width: 160 + height: 32 + + function parseKv(text) { + var obj = {} + var lines = text.split("\n") + for (var i = 0; i < lines.length; i++) { + var line = lines[i] + if (!line) continue + var idx = line.indexOf("=") + if (idx <= 0) continue + var k = line.substring(0, idx) + var v = line.substring(idx + 1) + obj[k] = v + } + return obj + } + + function humanDuration(sec) { + var s = parseInt(sec) + if (isNaN(s) || s < 0) return "—" + var d = Math.floor(s / 86400) + var h = Math.floor((s % 86400) / 3600) + var m = Math.floor((s % 3600) / 60) + if (d > 0) return d + "д " + h + "ч" + if (h > 0) return h + "ч " + m + "м" + return m + "м" + } + + function compute() { + var kv = parseKv(execDS.data["stdout"] || "") + + var st = kv["status"] || "" + var expired = kv["expired"] || "" + var validUntil = kv["valid_until"] || "" + var trialRem = kv["trial_remaining_seconds"] || "" + + var state = "UNKNOWN" + var rem = "—" + + if (st === "trial") { + state = "TRIAL" + rem = humanDuration(trialRem) + } else if (st === "missing") { + state = "MISSING" + rem = "—" + } else { + if (expired === "yes") state = "EXPIRED" + else if (validUntil !== "") state = "ACTIVE" + } + + labelState.text = state + labelRem.text = rem + + if (state === "ACTIVE") { + badge.color = "#2ecc71" + } else if (state === "TRIAL") { + badge.color = "#f1c40f" + } else if (state === "EXPIRED") { + badge.color = "#e74c3c" + } else if (state === "MISSING") { + badge.color = "#e67e22" + } else { + badge.color = "#95a5a6" + } + } + + PlasmaCore.DataSource { + id: execDS + engine: "executable" + connectedSources: ["/usr/sbin/saikyo-license status"] + interval: 60000 + onNewData: { + compute() + } + } + + Component.onCompleted: { + compute() + } + + Rectangle { + id: badge + anchors.fill: parent + radius: 6 + color: "#95a5a6" + opacity: 0.18 + + border.width: 1 + border.color: badge.color + } + + RowLayout { + anchors.fill: parent + anchors.margins: 6 + spacing: 6 + + Rectangle { + width: 10 + height: 10 + radius: 5 + color: badge.color + opacity: 0.95 + Layout.alignment: Qt.AlignVCenter + } + + PC3.Label { + id: labelState + text: "…" + font.bold: true + Layout.alignment: Qt.AlignVCenter + } + + PC3.Label { + id: labelRem + text: "—" + opacity: 0.85 + Layout.alignment: Qt.AlignVCenter + } + + Item { Layout.fillWidth: true } + + PC3.ToolButton { + text: "⚙" + display: PC3.AbstractButton.TextOnly + onClicked: { + // best-effort open GUI + execDS.connectedSources = ["/usr/bin/saikyo-license-kde"] + execDS.connectedSources = ["/usr/sbin/saikyo-license status"] + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + execDS.connectedSources = ["/usr/bin/saikyo-license-kde"] + execDS.connectedSources = ["/usr/sbin/saikyo-license status"] + } + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + } +} diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/plasma/plasmoids/org.saikyo.license/metadata.json b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/plasmoids/org.saikyo.license/metadata.json new file mode 100644 index 0000000..1419e80 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/plasma/plasmoids/org.saikyo.license/metadata.json @@ -0,0 +1,22 @@ +{ + "KPackageStructure": "Plasma/Applet", + "KPlugin": { + "Id": "org.saikyo.license", + "Name": "SAIKYO OS License", + "Name[ru]": "Лицензия SAIKYO OS", + "Description": "Shows SAIKYO OS license status and remaining time", + "Description[ru]": "Показывает статус лицензии SAIKYO OS и оставшееся время", + "Icon": "preferences-system", + "Version": "1.0", + "License": "Proprietary", + "Authors": [ + { + "Name": "SAIKYO OS", + "Email": "support@saikyo-os.ru" + } + ] + }, + "X-Plasma-API": "declarativeappletscript", + "X-Plasma-MainScript": "ui/main.qml", + "X-Plasma-NotificationArea": false +} diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-588e4226ca6a9ca0fe3790f5b4ed5ff286a80d131849f572a200592463676c1b.crt b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-588e4226ca6a9ca0fe3790f5b4ed5ff286a80d131849f572a200592463676c1b.crt new file mode 100644 index 0000000..ca7be59 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-588e4226ca6a9ca0fe3790f5b4ed5ff286a80d131849f572a200592463676c1b.crt @@ -0,0 +1,43 @@ +-----BEGIN CERTIFICATE----- +MIIHmDCCB0WgAwIBAgIKYqt5lQAAAAADtjAKBggqhQMHAQEDAjCCASQxHjAcBgkq +hkiG9w0BCQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxGDAWBgNVBAgM +Dzc3INCc0L7RgdC60LLQsDEZMBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQsDEuMCwG +A1UECQwl0YPQu9C40YbQsCDQotCy0LXRgNGB0LrQsNGPLCDQtNC+0LwgNzEsMCoG +A1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40LgxGDAWBgUq +hQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0NzQzNzUx +LDAqBgNVBAMMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MB4X +DTIwMDIwNTE0MDI0N1oXDTM1MDIwNTE0MDI0N1owggFtMSAwHgYJKoZIhvcNAQkB +FhF1Y19ma0Byb3NrYXpuYS5ydTEZMBcGA1UECAwQ0LMuINCc0L7RgdC60LLQsDEa +MBgGCCqFAwOBAwEBEgwwMDc3MTA1Njg3NjAxGDAWBgUqhQNkARINMTA0Nzc5NzAx +OTgzMDFgMF4GA1UECQxX0JHQvtC70YzRiNC+0Lkg0JfQu9Cw0YLQvtGD0YHRgtC4 +0L3RgdC60LjQuSDQv9C10YDQtdGD0LvQvtC6LCDQtC4gNiwg0YHRgtGA0L7QtdC9 +0LjQtSAxMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAxCzAJBgNVBAYTAlJVMTgwNgYD +VQQKDC/QpNC10LTQtdGA0LDQu9GM0L3QvtC1INC60LDQt9C90LDRh9C10LnRgdGC +0LLQvjE4MDYGA1UEAwwv0KTQtdC00LXRgNCw0LvRjNC90L7QtSDQutCw0LfQvdCw +0YfQtdC50YHRgtCy0L4wZjAfBggqhQMHAQEBATATBgcqhQMCAiMBBggqhQMHAQEC +AgNDAARA/G1/DjI9Hw+IV16X9pm0vTEj7uE8rv88OAAdmD7ddmRuYD6FLl491Zqv +AxF94s7j5C2i/XC5OMkEhJQ6nKUI9KOCBAMwggP/MBIGA1UdEwEB/wQIMAYBAf8C +AQAwUgYFKoUDZG8ESQxHItCa0YDQuNC/0YLQvtCf0YDQviBDU1AiINCy0LXRgNGB +0LjRjyA0LjAgKNC40YHQv9C+0LvQvdC10L3QuNC1IDItQmFzZSkwJQYDVR0gBB4w +HDAIBgYqhQNkcQEwCAYGKoUDZHECMAYGBFUdIAAwDgYDVR0PAQH/BAQDAgHGMIIB +ZQYDVR0jBIIBXDCCAViAFMJU8bRr1Ey34G02tCOQ8f7DPJsGoYIBLKSCASgwggEk +MR4wHAYJKoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgw +FgYDVQQIDA83NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy +0LAxLjAsBgNVBAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8 +IDcxLDAqBgNVBAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4 +MRgwFgYFKoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEw +NDc0Mzc1MSwwKgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB +0LjQuIIQTm1HiybyfWV/do4CXOPTkzAdBgNVHQ4EFgQU0GSWbXJA61h9JH+7IFvP +w45setQwgZgGA1UdHwSBkDCBjTAtoCugKYYnaHR0cDovL3JlZXN0ci1wa2kucnUv +Y2RwL2d1Y19nb3N0MTIuY3JsMC2gK6AphidodHRwOi8vY29tcGFueS5ydC5ydS9j +ZHAvZ3VjX2dvc3QxMi5jcmwwLaAroCmGJ2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2Nk +cC9ndWNfZ29zdDEyLmNybDBDBggrBgEFBQcBAQQ3MDUwMwYIKwYBBQUHMAKGJ2h0 +dHA6Ly9yZWVzdHItcGtpLnJ1L2NkcC9ndWNfZ29zdDEyLmNydDCB9QYFKoUDZHAE +geswgegMNNCf0JDQmtCcIMKr0JrRgNC40L/RgtC+0J/RgNC+IEhTTcK7INCy0LXR +gNGB0LjQuCAyLjAMQ9Cf0JDQmiDCq9CT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+0YHR +gtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAwrsMNdCX0LDQutC70Y7Rh9C1 +0L3QuNC1IOKEliAxNDkvMy8yLzIvMjMg0L7RgiAwMi4wMy4yMDE4DDTQl9Cw0LrQ +u9GO0YfQtdC90LjQtSDihJYgMTQ5LzcvNi8xMDUg0L7RgiAyNy4wNi4yMDE4MAoG +CCqFAwcBAQMCA0EAYO7/gtED6JgCLK7ffDDrnggByLOMGafo2r5TakOU/aBSidSD +a17cnrTflUw6REP8SrOa7o/tfCwpkbLJF2l6CQ== +-----END CERTIFICATE----- diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-5b51db721b7c34958ed7432ae917a91297dd37508b2cae4f858ffbac6bc525ef.crt b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-5b51db721b7c34958ed7432ae917a91297dd37508b2cae4f858ffbac6bc525ef.crt new file mode 100644 index 0000000..404a79a --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-5b51db721b7c34958ed7432ae917a91297dd37508b2cae4f858ffbac6bc525ef.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEizCCA/egAwIBAgIVALl/bbS8diQY7sdeMXod8B1oPUzeMAoGCCqFAwcBAQMD +MIIBMDEVMBMGBSqFA2QEEgo3NzEwNDc0Mzc1MRgwFgYFKoUDZAESDTEwNDc3MDIw +MjY3MDExUzBRBgNVBAkMStCf0YDQtdGB0L3QtdC90YHQutCw0Y8g0L3QsNCx0LXR +gNC10LbQvdCw0Y8sINC00L7QvCAxMCwg0YHRgtGA0L7QtdC90LjQtSAyMRkwFwYD +VQQHDBDQsy4g0JzQvtGB0LrQstCwMRgwFgYDVQQIDA83NyDQnNC+0YHQutCy0LAx +CzAJBgNVBAYTAlJVMSYwJAYDVQQKDB3QnNC40L3RhtC40YTRgNGLINCg0L7RgdGB +0LjQuDE+MDwGA1UEAww10JzQuNC90YbQuNGE0YDRiyDQoNC+0YHRgdC40Lgg0J3Q +o9CmINC60L7RgNC90LXQstC+0LkwHhcNMjUwNTI4MTU0MTQxWhcNNDAwNTI0MTU0 +MTQxWjCCATAxFTATBgUqhQNkBBIKNzcxMDQ3NDM3NTEYMBYGBSqFA2QBEg0xMDQ3 +NzAyMDI2NzAxMVMwUQYDVQQJDErQn9GA0LXRgdC90LXQvdGB0LrQsNGPINC90LDQ +sdC10YDQtdC20L3QsNGPLCDQtNC+0LwgMTAsINGB0YLRgNC+0LXQvdC40LUgMjEZ +MBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQsDEYMBYGA1UECAwPNzcg0JzQvtGB0LrQ +stCwMQswCQYDVQQGEwJSVTEmMCQGA1UECgwd0JzQuNC90YbQuNGE0YDRiyDQoNC+ +0YHRgdC40LgxPjA8BgNVBAMMNdCc0LjQvdGG0LjRhNGA0Ysg0KDQvtGB0YHQuNC4 +INCd0KPQpiDQutC+0YDQvdC10LLQvtC5MIGqMCEGCCqFAwcBAQECMBUGCSqFAwcB +AgECAQYIKoUDBwEBAgMDgYQABIGAdCxt4xzP9ZCep1FqKXymiUBX5GVp9KNV6bCV +sw+nV+E2/i2QKoTXkSrZwNdTAGfHaxRj0dNZjwljPyGRARTmBSG1CLvZvuwrla4O +ki2HXmLtLmdZCmk0MVG/0qyK2Hg/cG9VU/YvHNSxbdWgfBpaIKEprR3511tD1Sui +xEPhknSjgZcwgZQwEgYDVR0TAQH/BAgwBgEB/wIBBDAdBgNVHQ4EFgQU2EhWFsjb +gUKVDHySi+o35xGlveowDgYDVR0PAQH/BAQDAgEGME8GA1UdIARIMEYwCgYIKoUD +AhkBDgMwBgYEVR0gADAIBgYqhQNkcQEwCAYGKoUDZHECMAgGBiqFA2RxAzAIBgYq +hQNkcQQwCAYGKoUDZHEFMAoGCCqFAwcBAQMDA4GBAG6v+ir5U37uMA9IEkhNMl95 +PSa6X0XsLrmxWFjHVQcPMGP5niDjF0pNDqxps40Ig2qqzGZXlFLxuu0w+xeuDfMn +/AokEYCkWsMRhmzNEmVkLWggEaqBfHO+JEmAjwvyo6ggyvBdX8xQRpbcw9/Ugxx5 +h7F9deB2Q1ZcoFgcZmFl +-----END CERTIFICATE----- diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-71be2cef4964fda5a6302e2b55b741e40d25cef17df527f8abc350094973fa34.crt b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-71be2cef4964fda5a6302e2b55b741e40d25cef17df527f8abc350094973fa34.crt new file mode 100644 index 0000000..6ecbff0 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-71be2cef4964fda5a6302e2b55b741e40d25cef17df527f8abc350094973fa34.crt @@ -0,0 +1,43 @@ +-----BEGIN CERTIFICATE----- +MIIHhjCCBzOgAwIBAgILALXxMtMAAAAAAVowCgYIKoUDBwEBAwIwggEkMR4wHAYJ +KoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQI +DA83NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAs +BgNVBAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAq +BgNVBAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYF +KoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1 +MSwwKgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuDAe +Fw0xODExMTkxNTU2MDFaFw0zMzExMTkxNTU2MDFaMIIBbTEgMB4GCSqGSIb3DQEJ +ARYRdWNfZmtAcm9za2F6bmEucnUxGTAXBgNVBAgMENCzLiDQnNC+0YHQutCy0LAx +GjAYBggqhQMDgQMBARIMMDA3NzEwNTY4NzYwMRgwFgYFKoUDZAESDTEwNDc3OTcw +MTk4MzAxYDBeBgNVBAkMV9CR0L7Qu9GM0YjQvtC5INCX0LvQsNGC0L7Rg9GB0YLQ +uNC90YHQutC40Lkg0L/QtdGA0LXRg9C70L7Quiwg0LQuIDYsINGB0YLRgNC+0LXQ +vdC40LUgMTEVMBMGA1UEBwwM0JzQvtGB0LrQstCwMQswCQYDVQQGEwJSVTE4MDYG +A1UECgwv0KTQtdC00LXRgNCw0LvRjNC90L7QtSDQutCw0LfQvdCw0YfQtdC50YHR +gtCy0L4xODA2BgNVBAMML9Ck0LXQtNC10YDQsNC70YzQvdC+0LUg0LrQsNC30L3Q +sNGH0LXQudGB0YLQstC+MGYwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEB +AgIDQwAEQBHF4BLi2ABpI2cZNBIql/rippw/0qqoi4S2NvJHSGbC694tvfJqOmGG +UCD0b1kUlEq6ueFAtF7gOICquPj+DcmjggPwMIID7DASBgNVHRMBAf8ECDAGAQH/ +AgEAMD8GBSqFA2RvBDYMNNCh0JrQl9CYICLQmtGA0LjQv9GC0L7Qn9GA0L4gQ1NQ +IiAo0LLQtdGA0YHQuNGPIDQuMCkwJQYDVR0gBB4wHDAIBgYqhQNkcQEwCAYGKoUD +ZHECMAYGBFUdIAAwDgYDVR0PAQH/BAQDAgHGMIIBZQYDVR0jBIIBXDCCAViAFMJU +8bRr1Ey34G02tCOQ8f7DPJsGoYIBLKSCASgwggEkMR4wHAYJKoZIhvcNAQkBFg9k +aXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQIDA83NyDQnNC+0YHQ +utCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAsBgNVBAkMJdGD0LvQ +uNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAqBgNVBAoMI9Cc0LjQ +vdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUDZAESDTEwNDc3 +MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1MSwwKgYDVQQDDCPQ +nNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuIIQTm1HiybyfWV/do4C +XOPTkzAdBgNVHQ4EFgQUwNbWCn1rfsmOObzaifqvlCxYWo0wgZgGA1UdHwSBkDCB +jTAtoCugKYYnaHR0cDovL3JlZXN0ci1wa2kucnUvY2RwL2d1Y19nb3N0MTIuY3Js +MC2gK6AphidodHRwOi8vY29tcGFueS5ydC5ydS9jZHAvZ3VjX2dvc3QxMi5jcmww +LaAroCmGJ2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2NkcC9ndWNfZ29zdDEyLmNybDBD +BggrBgEFBQcBAQQ3MDUwMwYIKwYBBQUHMAKGJ2h0dHA6Ly9yZWVzdHItcGtpLnJ1 +L2NkcC9ndWNfZ29zdDEyLmNydDCB9QYFKoUDZHAEgeswgegMNNCf0JDQmtCcIMKr +0JrRgNC40L/RgtC+0J/RgNC+IEhTTcK7INCy0LXRgNGB0LjQuCAyLjAMQ9Cf0JDQ +miDCq9CT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQ +uSDRhtC10L3RgtGAwrsMNdCX0LDQutC70Y7Rh9C10L3QuNC1IOKEliAxNDkvMy8y +LzIvMjMg0L7RgiAwMi4wMy4yMDE4DDTQl9Cw0LrQu9GO0YfQtdC90LjQtSDihJYg +MTQ5LzcvNi8xMDUg0L7RgiAyNy4wNi4yMDE4MAoGCCqFAwcBAQMCA0EALtT076gA +jyE3aXhaCfJUQCH8xyUy4/It0btS24K3tKJZYLaYahNjbNNqcEFptDfn6I0c89xz +oKhFJDPHvjy/sg== +-----END CERTIFICATE----- diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-936a43fea6e8e525bcc0f81acd9c3d21b4fc4b9b68acea7906d698005afc6504.crt b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-936a43fea6e8e525bcc0f81acd9c3d21b4fc4b9b68acea7906d698005afc6504.crt new file mode 100644 index 0000000..4c143a2 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-936a43fea6e8e525bcc0f81acd9c3d21b4fc4b9b68acea7906d698005afc6504.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFwjCCA6qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx +PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu +ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg +Q0EwHhcNMjIwMzAxMjEwNDE1WhcNMzIwMjI3MjEwNDE1WjBwMQswCQYDVQQGEwJS +VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg +YW5kIENvbW11bmljYXRpb25zMSAwHgYDVQQDDBdSdXNzaWFuIFRydXN0ZWQgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMfFOZ8pUAL3+r2n +qqE0Zp52selXsKGFYoG0GM5bwz1bSFtCt+AZQMhkWQheI3poZAToYJu69pHLKS6Q +XBiwBC1cvzYmUYKMYZC7jE5YhEU2bSL0mX7NaMxMDmH2/NwuOVRj8OImVa5s1F4U +zn4Kv3PFlDBjjSjXKVY9kmjUBsXQrIHeaqmUIsPIlNWUnimXS0I0abExqkbdrXbX +YwCOXhOO2pDUx3ckmJlCMUGacUTnylyQW2VsJIyIGA8V0xzdaeUXg0VZ6ZmNUr5Y +Ber/EAOLPb8NYpsAhJe2mXjMB/J9HNsoFMBFJ0lLOT/+dQvjbdRZoOT8eqJpWnVD +U+QL/qEZnz57N88OWM3rabJkRNdU/Z7x5SFIM9FrqtN8xewsiBWBI0K6XFuOBOTD +4V08o4TzJ8+Ccq5XlCUW2L48pZNCYuBDfBh7FxkB7qDgGDiaftEkZZfApRg2E+M9 +G8wkNKTPLDc4wH0FDTijhgxR3Y4PiS1HL2Zhw7bD3CbslmEGgfnnZojNkJtcLeBH +BLa52/dSwNU4WWLubaYSiAmA9IUMX1/RpfpxOxd4Ykmhz97oFbUaDJFipIggx5sX +ePAlkTdWnv+RWBxlJwMQ25oEHmRguNYf4Zr/Rxr9cS93Y+mdXIZaBEE0KS2iLRqa +OiWBki9IMQU4phqPOBAaG7A+eP8PAgMBAAGjZjBkMB0GA1UdDgQWBBTh0YHlzlpf +BKrS6badZrHF+qwshzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzAS +BgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF +AAOCAgEAALIY1wkilt/urfEVM5vKzr6utOeDWCUczmWX/RX4ljpRdgF+5fAIS4vH +tmXkqpSCOVeWUrJV9QvZn6L227ZwuE15cWi8DCDal3Ue90WgAJJZMfTshN4OI8cq +W9E4EG9wglbEtMnObHlms8F3CHmrw3k6KmUkWGoa+/ENmcVl68u/cMRl1JbW2bM+ +/3A+SAg2c6iPDlehczKx2oa95QW0SkPPWGuNA/CE8CpyANIhu9XFrj3RQ3EqeRcS +AQQod1RNuHpfETLU/A2gMmvn/w/sx7TB3W5BPs6rprOA37tutPq9u6FTZOcG1Oqj +C/B7yTqgI7rbyvox7DEXoX7rIiEqyNNUguTk/u3SZ4VXE2kmxdmSh3TQvybfbnXV +4JbCZVaqiZraqc7oZMnRoWrXRG3ztbnbes/9qhRGI7PqXqeKJBztxRTEVj8ONs1d +WN5szTwaPIvhkhO3CO5ErU2rVdUr89wKpNXbBODFKRtgxUT70YpmJ46VVaqdAhOZ +D9EUUn4YaeLaS8AjSF/h7UkjOibNc4qVDiPP+rkehFWM66PVnP1Msh93tc+taIfC +EYVMxjh8zNbFuoc7fzvvrFILLe7ifvEIUqSVIC/AzplM/Jxw7buXFeGP1qVCBEHq +391d/9RAfaZ12zkwFsl+IKwE/OZxW8AHa9i1p4GO0YSNuczzEm4= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-bae62b5b7bede326b06856fb67a2a471268f9f404e5b18fdf40261c3e63010b1.crt b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-bae62b5b7bede326b06856fb67a2a471268f9f404e5b18fdf40261c3e63010b1.crt new file mode 100644 index 0000000..9831bee --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/ca-certificates/saikyo/saikyo-extra-bae62b5b7bede326b06856fb67a2a471268f9f404e5b18fdf40261c3e63010b1.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFDCCBMGgAwIBAgIQTm1HiybyfWV/do4CXOPTkzAKBggqhQMHAQEDAjCCASQx +HjAcBgkqhkiG9w0BCQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxGDAW +BgNVBAgMDzc3INCc0L7RgdC60LLQsDEZMBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQ +sDEuMCwGA1UECQwl0YPQu9C40YbQsCDQotCy0LXRgNGB0LrQsNGPLCDQtNC+0Lwg +NzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40Lgx +GDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0 +NzQzNzUxLDAqBgNVBAMMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQ +uNC4MB4XDTE4MDcwNjEyMTgwNloXDTM2MDcwMTEyMTgwNlowggEkMR4wHAYJKoZI +hvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQIDA83 +NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAsBgNV +BAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAqBgNV +BAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUD +ZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1MSww +KgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuDBmMB8G +CCqFAwcBAQEBMBMGByqFAwICIwEGCCqFAwcBAQICA0MABEB1OSpFp7milX33EP0i +kge6HbZacYp9fVj8sUa5RWFXrB27SKX5SvtIGepqKev69RSYeHHKR+jT9YX2NuSK +9wONo4IBwjCCAb4wgfUGBSqFA2RwBIHrMIHoDDTQn9CQ0JrQnCDCq9Ca0YDQuNC/ +0YLQvtCf0YDQviBIU03CuyDQstC10YDRgdC40LggMi4wDEPQn9CQ0JogwqvQk9C+ +0LvQvtCy0L3QvtC5INGD0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC9 +0YLRgMK7DDXQl9Cw0LrQu9GO0YfQtdC90LjQtSDihJYgMTQ5LzMvMi8yLzIzINC+ +0YIgMDIuMDMuMjAxOAw00JfQsNC60LvRjtGH0LXQvdC40LUg4oSWIDE0OS83LzYv +MTA1INC+0YIgMjcuMDYuMjAxODA/BgUqhQNkbwQ2DDTQn9CQ0JrQnCDCq9Ca0YDQ +uNC/0YLQvtCf0YDQviBIU03CuyDQstC10YDRgdC40LggMi4wMEMGA1UdIAQ8MDow +CAYGKoUDZHEBMAgGBiqFA2RxAjAIBgYqhQNkcQMwCAYGKoUDZHEEMAgGBiqFA2Rx +BTAGBgRVHSAAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBTCVPG0a9RMt+BtNrQjkPH+wzybBjAKBggqhQMHAQEDAgNBAJr6/eI7rHL7 ++FsQnoH2i6DVxqalbIxLKj05edpZGPLLb6B2PTAMya7pSt9hb8QnFABgsR4IE5gT +4VVkDWbX/n4= +-----END CERTIFICATE----- diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/logos/saikyo-logo.svg b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/logos/saikyo-logo.svg new file mode 100644 index 0000000..a0cbab6 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/logos/saikyo-logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + S + diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/secure-boot/saikyo-mok.der b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/secure-boot/saikyo-mok.der new file mode 100644 index 0000000..adcd5a2 Binary files /dev/null and b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/secure-boot/saikyo-mok.der differ diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/skel/.config/saikyo-os/README b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/skel/.config/saikyo-os/README new file mode 100644 index 0000000..3cd06de --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/skel/.config/saikyo-os/README @@ -0,0 +1,2 @@ +This directory is a placeholder for default user settings (KDE/SDDM/etc). +Recommended approach: ship defaults via /etc/skel or desktop-specific mechanisms. diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/wallpapers/saikyo-default.svg b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/wallpapers/saikyo-default.svg new file mode 100644 index 0000000..488937f --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/saikyo-os/wallpapers/saikyo-default.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + S + + + + + + diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/sddm/themes/Saikyo/Main.qml b/saikyo-branding/debian/saikyo-branding/usr/share/sddm/themes/Saikyo/Main.qml new file mode 100644 index 0000000..8ef8e8c --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/sddm/themes/Saikyo/Main.qml @@ -0,0 +1,186 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.15 +import SddmComponents 2.0 + +Item { + id: root + width: 1920 + height: 1080 + + property string backgroundPath: config.String("Theme", "Background", "") + property string logoPath: config.String("Theme", "Logo", "") + + Image { + id: bg + anchors.fill: parent + source: root.backgroundPath + fillMode: Image.PreserveAspectCrop + smooth: true + cache: true + } + + Rectangle { + anchors.fill: parent + color: "#050a14" + opacity: 0.55 + } + + ColumnLayout { + id: panel + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + spacing: 18 + width: Math.min(parent.width * 0.42, 560) + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 180 + Layout.preferredWidth: 180 + + Image { + anchors.fill: parent + source: root.logoPath + fillMode: Image.PreserveAspectFit + smooth: true + cache: true + } + } + + Label { + Layout.alignment: Qt.AlignHCenter + text: "SAIKYO OS" + font.pixelSize: 38 + font.weight: Font.DemiBold + color: "#e8f1ff" + } + + ComboBox { + id: userBox + Layout.fillWidth: true + model: userModel + textRole: "name" + currentIndex: userModel.lastIndex + focus: true + background: Rectangle { + radius: 10 + color: "#0b1d44" + border.color: "#153a85" + border.width: 1 + } + contentItem: Text { + leftPadding: 14 + rightPadding: 14 + text: userBox.displayText + color: "#e8f1ff" + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + + TextField { + id: password + Layout.fillWidth: true + echoMode: TextInput.Password + placeholderText: "Пароль" + inputMethodHints: Qt.ImhNoPredictiveText + background: Rectangle { + radius: 10 + color: "#0b1d44" + border.color: "#153a85" + border.width: 1 + } + color: "#e8f1ff" + padding: 14 + onAccepted: loginButton.clicked() + } + + Button { + id: loginButton + Layout.fillWidth: true + text: "Войти" + enabled: password.text.length > 0 + background: Rectangle { + radius: 10 + color: loginButton.enabled ? "#1e55b8" : "#163b7a" + } + contentItem: Text { + text: loginButton.text + color: "#ffffff" + font.pixelSize: 18 + font.weight: Font.DemiBold + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + sddm.login(userModel.get(userBox.currentIndex).name, password.text, sessionModel.lastIndex) + } + } + + Label { + id: msg + Layout.fillWidth: true + text: (sddm.loginFailed ? "Неверный логин или пароль" : "") + color: "#ff8a8a" + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + + RowLayout { + Layout.fillWidth: true + spacing: 12 + + ComboBox { + id: sessionBox + Layout.fillWidth: true + model: sessionModel + textRole: "name" + currentIndex: sessionModel.lastIndex + background: Rectangle { + radius: 10 + color: "#0b1d44" + border.color: "#153a85" + border.width: 1 + } + contentItem: Text { + leftPadding: 14 + rightPadding: 14 + text: sessionBox.displayText + color: "#e8f1ff" + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + + Button { + text: "Выключить" + Layout.preferredWidth: 160 + background: Rectangle { + radius: 10 + color: "#0b1d44" + border.color: "#153a85" + border.width: 1 + } + contentItem: Text { + text: parent.text + color: "#e8f1ff" + font.pixelSize: 16 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: sddm.powerOff() + } + } + } + + Label { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + text: "SAIKYO" + color: "#b7d0ff" + opacity: 0.55 + font.pixelSize: 14 + letterSpacing: 4 + } +} diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/sddm/themes/Saikyo/theme.conf b/saikyo-branding/debian/saikyo-branding/usr/share/sddm/themes/Saikyo/theme.conf new file mode 100644 index 0000000..fd5c962 --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/sddm/themes/Saikyo/theme.conf @@ -0,0 +1,15 @@ +[General] +Type=sddm-theme +Name=SAIKYO +Description=SAIKYO OS login screen +Author=SAIKYO OS +Copyright=SAIKYO OS +License=proprietary +Version=1.0 +Website=https://saikyo-os.ru +MainScript=Main.qml +ConfigFile=theme.conf + +[Theme] +Background=/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_boot_background.png +Logo=/usr/share/ksplash/Themes/Saikyo/contents/splash/saikyo_logo_white.png diff --git a/saikyo-branding/debian/saikyo-branding/usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg b/saikyo-branding/debian/saikyo-branding/usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg new file mode 100644 index 0000000..488937f --- /dev/null +++ b/saikyo-branding/debian/saikyo-branding/usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + S + + + + + + diff --git a/saikyo-branding/skel/.config/saikyo-os/README b/saikyo-branding/skel/.config/saikyo-os/README new file mode 100644 index 0000000..3cd06de --- /dev/null +++ b/saikyo-branding/skel/.config/saikyo-os/README @@ -0,0 +1,2 @@ +This directory is a placeholder for default user settings (KDE/SDDM/etc). +Recommended approach: ship defaults via /etc/skel or desktop-specific mechanisms. diff --git a/saikyo-grub-theme/debian/.debhelper/generated/saikyo-grub-theme/dh_installchangelogs.dch.trimmed b/saikyo-grub-theme/debian/.debhelper/generated/saikyo-grub-theme/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..4035efb --- /dev/null +++ b/saikyo-grub-theme/debian/.debhelper/generated/saikyo-grub-theme/dh_installchangelogs.dch.trimmed @@ -0,0 +1,5 @@ +saikyo-grub-theme (1.0.1) stable; urgency=medium + + * Enforce gfxterm defaults and refresh grub.cfg on install/upgrade. + + -- SAIKYO OS Sun, 18 Jan 2026 19:55:00 +0000 diff --git a/saikyo-grub-theme/debian/.debhelper/generated/saikyo-grub-theme/installed-by-dh_install b/saikyo-grub-theme/debian/.debhelper/generated/saikyo-grub-theme/installed-by-dh_install new file mode 100644 index 0000000..604358c --- /dev/null +++ b/saikyo-grub-theme/debian/.debhelper/generated/saikyo-grub-theme/installed-by-dh_install @@ -0,0 +1,2 @@ +./theme/saikyo/theme.txt +./etc-default-grub.d/saikyo.cfg diff --git a/saikyo-grub-theme/debian/.debhelper/generated/saikyo-grub-theme/installed-by-dh_installdocs b/saikyo-grub-theme/debian/.debhelper/generated/saikyo-grub-theme/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-grub-theme/debian/changelog b/saikyo-grub-theme/debian/changelog new file mode 100644 index 0000000..4035efb --- /dev/null +++ b/saikyo-grub-theme/debian/changelog @@ -0,0 +1,5 @@ +saikyo-grub-theme (1.0.1) stable; urgency=medium + + * Enforce gfxterm defaults and refresh grub.cfg on install/upgrade. + + -- SAIKYO OS Sun, 18 Jan 2026 19:55:00 +0000 diff --git a/saikyo-grub-theme/debian/control b/saikyo-grub-theme/debian/control new file mode 100644 index 0000000..49a16b8 --- /dev/null +++ b/saikyo-grub-theme/debian/control @@ -0,0 +1,13 @@ +Source: saikyo-grub-theme +Section: misc +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-grub-theme +Architecture: all +Depends: ${misc:Depends}, grub-common +Description: SAIKYO OS GRUB theme + Installs Saikyo OS GRUB theme and GRUB defaults snippet. diff --git a/saikyo-grub-theme/debian/copyright b/saikyo-grub-theme/debian/copyright new file mode 100644 index 0000000..51e1141 --- /dev/null +++ b/saikyo-grub-theme/debian/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-grub-theme +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-grub-theme/debian/debhelper-build-stamp b/saikyo-grub-theme/debian/debhelper-build-stamp new file mode 100644 index 0000000..0723628 --- /dev/null +++ b/saikyo-grub-theme/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-grub-theme diff --git a/saikyo-grub-theme/debian/files b/saikyo-grub-theme/debian/files new file mode 100644 index 0000000..8de661c --- /dev/null +++ b/saikyo-grub-theme/debian/files @@ -0,0 +1,2 @@ +saikyo-grub-theme_1.0.1_all.deb misc optional +saikyo-grub-theme_1.0.1_amd64.buildinfo misc optional diff --git a/saikyo-grub-theme/debian/install b/saikyo-grub-theme/debian/install new file mode 100644 index 0000000..a73cd38 --- /dev/null +++ b/saikyo-grub-theme/debian/install @@ -0,0 +1,2 @@ +theme/saikyo/theme.txt /boot/grub/themes/saikyo/theme.txt +etc-default-grub.d/saikyo.cfg /etc/default/grub.d/saikyo.cfg diff --git a/saikyo-grub-theme/debian/postinst b/saikyo-grub-theme/debian/postinst new file mode 100755 index 0000000..987c3a4 --- /dev/null +++ b/saikyo-grub-theme/debian/postinst @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +case "$1" in + configure) + # Ensure GRUB snippet is a file under /etc/default/grub.d/*.cfg + if [ -f /etc/default/grub.d/saikyo.cfg/saikyo.cfg ]; then + mkdir -p /etc/default/grub.d || true + rm -f /etc/default/grub.d/saikyo.cfg || true + cat /etc/default/grub.d/saikyo.cfg/saikyo.cfg > /etc/default/grub.d/saikyo.cfg || true + fi + + if command -v update-grub >/dev/null 2>&1; then + update-grub >/dev/null 2>&1 || true + elif command -v update-grub2 >/dev/null 2>&1; then + update-grub2 >/dev/null 2>&1 || true + elif command -v grub-mkconfig >/dev/null 2>&1; then + grub-mkconfig -o /boot/grub/grub.cfg >/dev/null 2>&1 || true + grub-mkconfig -o /boot/grub2/grub.cfg >/dev/null 2>&1 || true + fi + ;; +esac + +exit 0 diff --git a/saikyo-grub-theme/debian/rules b/saikyo-grub-theme/debian/rules new file mode 100755 index 0000000..e1ad233 --- /dev/null +++ b/saikyo-grub-theme/debian/rules @@ -0,0 +1,12 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: + +override_dh_fixperms: + dh_fixperms + chmod 0755 debian/postinst || true diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme.debhelper.log b/saikyo-grub-theme/debian/saikyo-grub-theme.debhelper.log new file mode 100644 index 0000000..93c5512 --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme.debhelper.log @@ -0,0 +1 @@ +dh_fixperms diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme.substvars b/saikyo-grub-theme/debian/saikyo-grub-theme.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/conffiles b/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/conffiles new file mode 100644 index 0000000..1bc90c4 --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/conffiles @@ -0,0 +1 @@ +/etc/default/grub.d/saikyo.cfg/saikyo.cfg diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/control b/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/control new file mode 100644 index 0000000..0869e5f --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/control @@ -0,0 +1,10 @@ +Package: saikyo-grub-theme +Version: 1.0.1 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 22 +Depends: grub-common +Section: misc +Priority: optional +Description: SAIKYO OS GRUB theme + Installs Saikyo OS GRUB theme and GRUB defaults snippet. diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/md5sums b/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/md5sums new file mode 100644 index 0000000..17567cf --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/md5sums @@ -0,0 +1,3 @@ +b5fa6fe7a040660c95a931d110d8720d boot/grub/themes/saikyo/theme.txt/theme.txt +34991bd93cf5dfa5306add2ab5e3d348 usr/share/doc/saikyo-grub-theme/changelog.gz +5fe0f1ad633a133465500d3b83c95dd2 usr/share/doc/saikyo-grub-theme/copyright diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/postinst b/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/postinst new file mode 100755 index 0000000..987c3a4 --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme/DEBIAN/postinst @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +case "$1" in + configure) + # Ensure GRUB snippet is a file under /etc/default/grub.d/*.cfg + if [ -f /etc/default/grub.d/saikyo.cfg/saikyo.cfg ]; then + mkdir -p /etc/default/grub.d || true + rm -f /etc/default/grub.d/saikyo.cfg || true + cat /etc/default/grub.d/saikyo.cfg/saikyo.cfg > /etc/default/grub.d/saikyo.cfg || true + fi + + if command -v update-grub >/dev/null 2>&1; then + update-grub >/dev/null 2>&1 || true + elif command -v update-grub2 >/dev/null 2>&1; then + update-grub2 >/dev/null 2>&1 || true + elif command -v grub-mkconfig >/dev/null 2>&1; then + grub-mkconfig -o /boot/grub/grub.cfg >/dev/null 2>&1 || true + grub-mkconfig -o /boot/grub2/grub.cfg >/dev/null 2>&1 || true + fi + ;; +esac + +exit 0 diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme/boot/grub/themes/saikyo/theme.txt/theme.txt b/saikyo-grub-theme/debian/saikyo-grub-theme/boot/grub/themes/saikyo/theme.txt/theme.txt new file mode 100644 index 0000000..123fd6a --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme/boot/grub/themes/saikyo/theme.txt/theme.txt @@ -0,0 +1,38 @@ +# Minimal GRUB theme for Saikyo OS (dark blue) + +title-text: "SAIKYO OS" +title-color: "#e8f1ff" +title-font: "DejaVu Sans Bold 24" + +desktop-color: "#071a3a" + +message-font: "DejaVu Sans 14" +message-color: "#b7d0ff" + +terminal-font: "DejaVu Sans Mono 14" + ++ boot_menu { + left = 10% + top = 35% + width = 80% + height = 50% + item_font = "DejaVu Sans 16" + item_color = "#b7d0ff" + selected_item_color = "#e8f1ff" + selected_item_style = "menu_selected" + item_height = 28 + item_padding = 8 + item_spacing = 6 +} + ++ label { + id = "__help" + left = 10% + top = 88% + width = 80% + height = 5% + text = "SAIKYO: стрелки — выбор, Enter — загрузка" + font = "DejaVu Sans 12" + color = "#6ea0ff" + align = "center" +} diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme/etc/default/grub.d/saikyo.cfg/saikyo.cfg b/saikyo-grub-theme/debian/saikyo-grub-theme/etc/default/grub.d/saikyo.cfg/saikyo.cfg new file mode 100644 index 0000000..fa8c7d4 --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme/etc/default/grub.d/saikyo.cfg/saikyo.cfg @@ -0,0 +1,7 @@ +GRUB_DISTRIBUTOR="SAIKYO OS" +GRUB_TIMEOUT=5 +GRUB_TERMINAL_OUTPUT="gfxterm" +GRUB_GFXMODE="auto" +GRUB_GFXPAYLOAD_LINUX="keep" +GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=3 systemd.show_status=false rd.systemd.show_status=false vt.global_cursor_default=0" +GRUB_THEME="/boot/grub/themes/saikyo/theme.txt" diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme/usr/share/doc/saikyo-grub-theme/changelog.gz b/saikyo-grub-theme/debian/saikyo-grub-theme/usr/share/doc/saikyo-grub-theme/changelog.gz new file mode 100644 index 0000000..e65332f Binary files /dev/null and b/saikyo-grub-theme/debian/saikyo-grub-theme/usr/share/doc/saikyo-grub-theme/changelog.gz differ diff --git a/saikyo-grub-theme/debian/saikyo-grub-theme/usr/share/doc/saikyo-grub-theme/copyright b/saikyo-grub-theme/debian/saikyo-grub-theme/usr/share/doc/saikyo-grub-theme/copyright new file mode 100644 index 0000000..51e1141 --- /dev/null +++ b/saikyo-grub-theme/debian/saikyo-grub-theme/usr/share/doc/saikyo-grub-theme/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-grub-theme +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-grub-theme/debian/source/format b/saikyo-grub-theme/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/saikyo-grub-theme/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/saikyo-grub-theme/etc-default-grub.d/saikyo.cfg b/saikyo-grub-theme/etc-default-grub.d/saikyo.cfg new file mode 100644 index 0000000..fa8c7d4 --- /dev/null +++ b/saikyo-grub-theme/etc-default-grub.d/saikyo.cfg @@ -0,0 +1,7 @@ +GRUB_DISTRIBUTOR="SAIKYO OS" +GRUB_TIMEOUT=5 +GRUB_TERMINAL_OUTPUT="gfxterm" +GRUB_GFXMODE="auto" +GRUB_GFXPAYLOAD_LINUX="keep" +GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=3 systemd.show_status=false rd.systemd.show_status=false vt.global_cursor_default=0" +GRUB_THEME="/boot/grub/themes/saikyo/theme.txt" diff --git a/saikyo-grub-theme/theme/saikyo/theme.txt b/saikyo-grub-theme/theme/saikyo/theme.txt new file mode 100644 index 0000000..123fd6a --- /dev/null +++ b/saikyo-grub-theme/theme/saikyo/theme.txt @@ -0,0 +1,38 @@ +# Minimal GRUB theme for Saikyo OS (dark blue) + +title-text: "SAIKYO OS" +title-color: "#e8f1ff" +title-font: "DejaVu Sans Bold 24" + +desktop-color: "#071a3a" + +message-font: "DejaVu Sans 14" +message-color: "#b7d0ff" + +terminal-font: "DejaVu Sans Mono 14" + ++ boot_menu { + left = 10% + top = 35% + width = 80% + height = 50% + item_font = "DejaVu Sans 16" + item_color = "#b7d0ff" + selected_item_color = "#e8f1ff" + selected_item_style = "menu_selected" + item_height = 28 + item_padding = 8 + item_spacing = 6 +} + ++ label { + id = "__help" + left = 10% + top = 88% + width = 80% + height = 5% + text = "SAIKYO: стрелки — выбор, Enter — загрузка" + font = "DejaVu Sans 12" + color = "#6ea0ff" + align = "center" +} diff --git a/saikyo-kde-rebrand/bin/saikyo-kde-rebrand b/saikyo-kde-rebrand/bin/saikyo-kde-rebrand new file mode 100644 index 0000000..9b76fd4 --- /dev/null +++ b/saikyo-kde-rebrand/bin/saikyo-kde-rebrand @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +set -uo pipefail + +STATE_DIR="/var/lib/saikyo-kde-rebrand" +GENERATED_LIST="${STATE_DIR}/generated.list" +DIVERT_LIST="${STATE_DIR}/diverts.list" +TMP_DIR="${STATE_DIR}/tmp" + +apply_replacements_sed() { + sed \ + -e 's/\bKDE\b/SAIKYO KDE/g' \ + -e 's/\bPlasma\b/SAIKYO KDE/g' \ + -e 's/\bPLASMA\b/SAIKYO KDE/g' \ + -e 's/\bDiscover\b/SAIKYO Discover/g' \ + -e 's/\bDEBIAN\b/SAIKYO OS/g' \ + -e 's/\bDebian\b/SAIKYO OS/g' +} + +mkdir -p "${STATE_DIR}" "${TMP_DIR}" +chmod 0755 "${STATE_DIR}" "${TMP_DIR}" + +record_generated() { + local f="$1" + touch "${GENERATED_LIST}" 2>/dev/null || true + if ! grep -qxF "$f" "${GENERATED_LIST}" 2>/dev/null; then + echo "$f" >>"${GENERATED_LIST}" + fi +} + +record_divert() { + local f="$1" + touch "${DIVERT_LIST}" 2>/dev/null || true + if ! grep -qxF "$f" "${DIVERT_LIST}" 2>/dev/null; then + echo "$f" >>"${DIVERT_LIST}" + fi +} + +require_root() { + if [[ "$(id -u)" -ne 0 ]]; then + echo "ERROR: must be run as root" >&2 + exit 1 + fi +} + +is_diverted_by_us() { + local f="$1" + dpkg-divert --list "$f" 2>/dev/null | grep -q "package: saikyo-kde-rebrand" || return 1 +} + +divert_mo() { + local f="$1" + if is_diverted_by_us "$f"; then + record_divert "$f" + return 0 + fi + dpkg-divert --package saikyo-kde-rebrand --add --rename --divert "${f}.saikyo-orig" "$f" >/dev/null + record_divert "$f" +} + +overlay_path_for() { + local f="$1" + case "$f" in + /usr/share/applications/*) + printf '%s\n' "/usr/local/share/applications/${f#/usr/share/applications/}" + ;; + /usr/share/metainfo/*) + printf '%s\n' "/usr/local/share/metainfo/${f#/usr/share/metainfo/}" + ;; + /usr/share/locale/*) + printf '%s\n' "/usr/local/share/locale/${f#/usr/share/locale/}" + ;; + *) + return 1 + ;; + esac +} + +patch_text_to_overlay() { + local src="$1" + local dst + dst="$(overlay_path_for "$src")" || return 0 + mkdir -p "$(dirname "$dst")" 2>/dev/null || return 0 + if apply_replacements_sed <"$src" >"$dst" 2>/dev/null; then + chmod 0644 "$dst" 2>/dev/null || true + record_generated "$dst" + fi +} + +patch_mo_to_overlay() { + local src="$1" + local dst tmp_po tmp_mo + dst="$(overlay_path_for "$src")" || return 0 + mkdir -p "$(dirname "$dst")" + + tmp_po="${TMP_DIR}/$(basename "$src").$$.po" + tmp_mo="${TMP_DIR}/$(basename "$src").$$.mo" + + # Try to patch, but don't fail if msgunfmt/msgfmt fails on corrupted .mo + if msgunfmt "$src" 2>/dev/null | apply_replacements_sed >"$tmp_po" 2>/dev/null; then + if msgfmt -o "$tmp_mo" "$tmp_po" 2>/dev/null && [[ -f "$tmp_mo" ]]; then + install -m 0644 "$tmp_mo" "$dst" 2>/dev/null || true + record_generated "$dst" + fi + fi + rm -f "$tmp_po" "$tmp_mo" 2>/dev/null || true +} + +patch_mo_in_place_diverted() { + local f="$1" + local tmp_po tmp_mo + + tmp_po="${TMP_DIR}/$(basename "$f").$$.po" + tmp_mo="${TMP_DIR}/$(basename "$f").$$.mo" + + # Try to patch, but don't fail if msgunfmt/msgfmt fails on corrupted .mo + if msgunfmt "${f}.saikyo-orig" 2>/dev/null | apply_replacements_sed >"$tmp_po" 2>/dev/null; then + if msgfmt -o "$tmp_mo" "$tmp_po" 2>/dev/null && [[ -f "$tmp_mo" ]]; then + install -m 0644 "$tmp_mo" "$f" 2>/dev/null || true + fi + fi + rm -f "$tmp_po" "$tmp_mo" 2>/dev/null || true +} + +apply_rebrand() { + require_root + : >"${GENERATED_LIST}" + touch "${DIVERT_LIST}" + + local f + + while IFS= read -r -d '' f; do + if grep -Eqi '\b(KDE|Plasma|PLASMA|Discover|Debian|DEBIAN)\b' "$f" 2>/dev/null; then + patch_text_to_overlay "$f" || true + fi + done < <(find /usr/share/applications -type f -name '*.desktop' -print0 2>/dev/null) || true + + while IFS= read -r -d '' f; do + if grep -Eqi '\b(KDE|Plasma|PLASMA|Discover|Debian|DEBIAN)\b' "$f" 2>/dev/null; then + patch_text_to_overlay "$f" || true + fi + done < <(find /usr/share/metainfo -type f \( -name '*.metainfo.xml' -o -name '*.appdata.xml' \) -print0 2>/dev/null) || true + + while IFS= read -r -d '' f; do + if grep -a -Eq '\b(KDE|Plasma|PLASMA|Discover|Debian|DEBIAN)\b' "$f" 2>/dev/null; then + divert_mo "$f" || true + patch_mo_in_place_diverted "$f" || true + fi + done < <(find /usr/share/locale -type f -name '*.mo' -print0 2>/dev/null) || true +} + +revert_rebrand() { + require_root + + if [[ -f "${GENERATED_LIST}" ]]; then + local f + while IFS= read -r f; do + [[ -n "${f}" ]] || continue + rm -f "$f" 2>/dev/null || true + done <"${GENERATED_LIST}" + + rm -f "${GENERATED_LIST}" 2>/dev/null || true + fi + + if [[ -f "${DIVERT_LIST}" ]]; then + local f + while IFS= read -r f; do + [[ -n "${f}" ]] || continue + if is_diverted_by_us "$f"; then + rm -f "$f" 2>/dev/null || true + dpkg-divert --package saikyo-kde-rebrand --remove --rename --divert "${f}.saikyo-orig" "$f" >/dev/null || true + fi + done <"${DIVERT_LIST}" + + rm -f "${DIVERT_LIST}" 2>/dev/null || true + fi + + # Best-effort cleanup of empty directories + find /usr/local/share/applications /usr/local/share/metainfo -type d -empty -print0 2>/dev/null | xargs -0r rmdir 2>/dev/null || true +} + +main() { + local cmd="${1:-}" + case "$cmd" in + apply) apply_rebrand;; + revert) revert_rebrand;; + *) + echo "Usage: saikyo-kde-rebrand {apply|revert}" >&2 + exit 2 + ;; + esac +} + +main "$@" diff --git a/saikyo-kde-rebrand/debian/.debhelper/generated/saikyo-kde-rebrand/dh_installchangelogs.dch.trimmed b/saikyo-kde-rebrand/debian/.debhelper/generated/saikyo-kde-rebrand/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..a24f2ab --- /dev/null +++ b/saikyo-kde-rebrand/debian/.debhelper/generated/saikyo-kde-rebrand/dh_installchangelogs.dch.trimmed @@ -0,0 +1,13 @@ +saikyo-kde-rebrand (1.0.2) stable; urgency=medium + + * Fix error handling in rebrand script + * Remove set -e to prevent failures on corrupted .mo files + * Add graceful error handling for msgunfmt/msgfmt operations + + -- SAIKYO OS Tue, 21 Jan 2026 16:07:00 +0300 + +saikyo-kde-rebrand (1.0.1) stable; urgency=medium + + * Implement system-wide KDE -> SAIKYO KDE rebrand. + + -- SAIKYO OS Sun, 11 Jan 2026 07:00:00 +0000 diff --git a/saikyo-kde-rebrand/debian/.debhelper/generated/saikyo-kde-rebrand/installed-by-dh_install b/saikyo-kde-rebrand/debian/.debhelper/generated/saikyo-kde-rebrand/installed-by-dh_install new file mode 100644 index 0000000..40c3978 --- /dev/null +++ b/saikyo-kde-rebrand/debian/.debhelper/generated/saikyo-kde-rebrand/installed-by-dh_install @@ -0,0 +1 @@ +./bin/saikyo-kde-rebrand diff --git a/saikyo-kde-rebrand/debian/.debhelper/generated/saikyo-kde-rebrand/installed-by-dh_installdocs b/saikyo-kde-rebrand/debian/.debhelper/generated/saikyo-kde-rebrand/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-kde-rebrand/debian/changelog b/saikyo-kde-rebrand/debian/changelog new file mode 100644 index 0000000..a24f2ab --- /dev/null +++ b/saikyo-kde-rebrand/debian/changelog @@ -0,0 +1,13 @@ +saikyo-kde-rebrand (1.0.2) stable; urgency=medium + + * Fix error handling in rebrand script + * Remove set -e to prevent failures on corrupted .mo files + * Add graceful error handling for msgunfmt/msgfmt operations + + -- SAIKYO OS Tue, 21 Jan 2026 16:07:00 +0300 + +saikyo-kde-rebrand (1.0.1) stable; urgency=medium + + * Implement system-wide KDE -> SAIKYO KDE rebrand. + + -- SAIKYO OS Sun, 11 Jan 2026 07:00:00 +0000 diff --git a/saikyo-kde-rebrand/debian/control b/saikyo-kde-rebrand/debian/control new file mode 100644 index 0000000..066cc29 --- /dev/null +++ b/saikyo-kde-rebrand/debian/control @@ -0,0 +1,15 @@ +Source: saikyo-kde-rebrand +Section: misc +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-kde-rebrand +Architecture: all +Depends: ${misc:Depends}, bash, coreutils, findutils, grep, sed, dpkg, gettext +Description: Replace KDE branding strings with SAIKYO KDE (system-wide) + System-wide rebranding that replaces visible "KDE" strings in desktop entries, + AppStream metadata and translations with "SAIKYO KDE". + Uses /usr/local overlays for desktop/appstream and dpkg-divert for translations. diff --git a/saikyo-kde-rebrand/debian/debhelper-build-stamp b/saikyo-kde-rebrand/debian/debhelper-build-stamp new file mode 100644 index 0000000..a4eda32 --- /dev/null +++ b/saikyo-kde-rebrand/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-kde-rebrand diff --git a/saikyo-kde-rebrand/debian/files b/saikyo-kde-rebrand/debian/files new file mode 100644 index 0000000..e8cd5c7 --- /dev/null +++ b/saikyo-kde-rebrand/debian/files @@ -0,0 +1,2 @@ +saikyo-kde-rebrand_1.0.2_all.deb misc optional +saikyo-kde-rebrand_1.0.2_amd64.buildinfo misc optional diff --git a/saikyo-kde-rebrand/debian/install b/saikyo-kde-rebrand/debian/install new file mode 100644 index 0000000..fb1611b --- /dev/null +++ b/saikyo-kde-rebrand/debian/install @@ -0,0 +1 @@ +bin/saikyo-kde-rebrand usr/sbin/ diff --git a/saikyo-kde-rebrand/debian/rules b/saikyo-kde-rebrand/debian/rules new file mode 100755 index 0000000..fd85c76 --- /dev/null +++ b/saikyo-kde-rebrand/debian/rules @@ -0,0 +1,12 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: + +override_dh_fixperms: + dh_fixperms + chmod 0755 debian/saikyo-kde-rebrand/usr/sbin/saikyo-kde-rebrand diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.debhelper.log b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.debhelper.log new file mode 100644 index 0000000..93c5512 --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.debhelper.log @@ -0,0 +1 @@ +dh_fixperms diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.postinst b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.postinst new file mode 100644 index 0000000..4e3d095 --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.postinst @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +if [ "$1" = "configure" ]; then + if [ -x /usr/sbin/saikyo-kde-rebrand ]; then + /usr/sbin/saikyo-kde-rebrand apply || true + fi +fi + +exit 0 diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.postrm b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.postrm new file mode 100644 index 0000000..d91874c --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.postrm @@ -0,0 +1,12 @@ +#!/bin/sh +set -e + +case "$1" in + remove|purge) + if [ -x /usr/sbin/saikyo-kde-rebrand ]; then + /usr/sbin/saikyo-kde-rebrand revert || true + fi + ;; +esac + +exit 0 diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.substvars b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/control b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/control new file mode 100644 index 0000000..df17f3e --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/control @@ -0,0 +1,12 @@ +Package: saikyo-kde-rebrand +Version: 1.0.2 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 16 +Depends: bash, coreutils, findutils, grep, sed, dpkg, gettext +Section: misc +Priority: optional +Description: Replace KDE branding strings with SAIKYO KDE (system-wide) + System-wide rebranding that replaces visible "KDE" strings in desktop entries, + AppStream metadata and translations with "SAIKYO KDE". + Uses /usr/local overlays for desktop/appstream and dpkg-divert for translations. diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/md5sums b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/md5sums new file mode 100644 index 0000000..feaf76c --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/md5sums @@ -0,0 +1,2 @@ +48bb381cb3c8919e0f5c8750c0a47b5b usr/sbin/saikyo-kde-rebrand +7ef1c24f950bc128117d350baca3e44a usr/share/doc/saikyo-kde-rebrand/changelog.gz diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/postinst b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/postinst new file mode 100755 index 0000000..4e3d095 --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/postinst @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +if [ "$1" = "configure" ]; then + if [ -x /usr/sbin/saikyo-kde-rebrand ]; then + /usr/sbin/saikyo-kde-rebrand apply || true + fi +fi + +exit 0 diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/postrm b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/postrm new file mode 100755 index 0000000..d91874c --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/DEBIAN/postrm @@ -0,0 +1,12 @@ +#!/bin/sh +set -e + +case "$1" in + remove|purge) + if [ -x /usr/sbin/saikyo-kde-rebrand ]; then + /usr/sbin/saikyo-kde-rebrand revert || true + fi + ;; +esac + +exit 0 diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/usr/sbin/saikyo-kde-rebrand b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/usr/sbin/saikyo-kde-rebrand new file mode 100755 index 0000000..9b76fd4 --- /dev/null +++ b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/usr/sbin/saikyo-kde-rebrand @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +set -uo pipefail + +STATE_DIR="/var/lib/saikyo-kde-rebrand" +GENERATED_LIST="${STATE_DIR}/generated.list" +DIVERT_LIST="${STATE_DIR}/diverts.list" +TMP_DIR="${STATE_DIR}/tmp" + +apply_replacements_sed() { + sed \ + -e 's/\bKDE\b/SAIKYO KDE/g' \ + -e 's/\bPlasma\b/SAIKYO KDE/g' \ + -e 's/\bPLASMA\b/SAIKYO KDE/g' \ + -e 's/\bDiscover\b/SAIKYO Discover/g' \ + -e 's/\bDEBIAN\b/SAIKYO OS/g' \ + -e 's/\bDebian\b/SAIKYO OS/g' +} + +mkdir -p "${STATE_DIR}" "${TMP_DIR}" +chmod 0755 "${STATE_DIR}" "${TMP_DIR}" + +record_generated() { + local f="$1" + touch "${GENERATED_LIST}" 2>/dev/null || true + if ! grep -qxF "$f" "${GENERATED_LIST}" 2>/dev/null; then + echo "$f" >>"${GENERATED_LIST}" + fi +} + +record_divert() { + local f="$1" + touch "${DIVERT_LIST}" 2>/dev/null || true + if ! grep -qxF "$f" "${DIVERT_LIST}" 2>/dev/null; then + echo "$f" >>"${DIVERT_LIST}" + fi +} + +require_root() { + if [[ "$(id -u)" -ne 0 ]]; then + echo "ERROR: must be run as root" >&2 + exit 1 + fi +} + +is_diverted_by_us() { + local f="$1" + dpkg-divert --list "$f" 2>/dev/null | grep -q "package: saikyo-kde-rebrand" || return 1 +} + +divert_mo() { + local f="$1" + if is_diverted_by_us "$f"; then + record_divert "$f" + return 0 + fi + dpkg-divert --package saikyo-kde-rebrand --add --rename --divert "${f}.saikyo-orig" "$f" >/dev/null + record_divert "$f" +} + +overlay_path_for() { + local f="$1" + case "$f" in + /usr/share/applications/*) + printf '%s\n' "/usr/local/share/applications/${f#/usr/share/applications/}" + ;; + /usr/share/metainfo/*) + printf '%s\n' "/usr/local/share/metainfo/${f#/usr/share/metainfo/}" + ;; + /usr/share/locale/*) + printf '%s\n' "/usr/local/share/locale/${f#/usr/share/locale/}" + ;; + *) + return 1 + ;; + esac +} + +patch_text_to_overlay() { + local src="$1" + local dst + dst="$(overlay_path_for "$src")" || return 0 + mkdir -p "$(dirname "$dst")" 2>/dev/null || return 0 + if apply_replacements_sed <"$src" >"$dst" 2>/dev/null; then + chmod 0644 "$dst" 2>/dev/null || true + record_generated "$dst" + fi +} + +patch_mo_to_overlay() { + local src="$1" + local dst tmp_po tmp_mo + dst="$(overlay_path_for "$src")" || return 0 + mkdir -p "$(dirname "$dst")" + + tmp_po="${TMP_DIR}/$(basename "$src").$$.po" + tmp_mo="${TMP_DIR}/$(basename "$src").$$.mo" + + # Try to patch, but don't fail if msgunfmt/msgfmt fails on corrupted .mo + if msgunfmt "$src" 2>/dev/null | apply_replacements_sed >"$tmp_po" 2>/dev/null; then + if msgfmt -o "$tmp_mo" "$tmp_po" 2>/dev/null && [[ -f "$tmp_mo" ]]; then + install -m 0644 "$tmp_mo" "$dst" 2>/dev/null || true + record_generated "$dst" + fi + fi + rm -f "$tmp_po" "$tmp_mo" 2>/dev/null || true +} + +patch_mo_in_place_diverted() { + local f="$1" + local tmp_po tmp_mo + + tmp_po="${TMP_DIR}/$(basename "$f").$$.po" + tmp_mo="${TMP_DIR}/$(basename "$f").$$.mo" + + # Try to patch, but don't fail if msgunfmt/msgfmt fails on corrupted .mo + if msgunfmt "${f}.saikyo-orig" 2>/dev/null | apply_replacements_sed >"$tmp_po" 2>/dev/null; then + if msgfmt -o "$tmp_mo" "$tmp_po" 2>/dev/null && [[ -f "$tmp_mo" ]]; then + install -m 0644 "$tmp_mo" "$f" 2>/dev/null || true + fi + fi + rm -f "$tmp_po" "$tmp_mo" 2>/dev/null || true +} + +apply_rebrand() { + require_root + : >"${GENERATED_LIST}" + touch "${DIVERT_LIST}" + + local f + + while IFS= read -r -d '' f; do + if grep -Eqi '\b(KDE|Plasma|PLASMA|Discover|Debian|DEBIAN)\b' "$f" 2>/dev/null; then + patch_text_to_overlay "$f" || true + fi + done < <(find /usr/share/applications -type f -name '*.desktop' -print0 2>/dev/null) || true + + while IFS= read -r -d '' f; do + if grep -Eqi '\b(KDE|Plasma|PLASMA|Discover|Debian|DEBIAN)\b' "$f" 2>/dev/null; then + patch_text_to_overlay "$f" || true + fi + done < <(find /usr/share/metainfo -type f \( -name '*.metainfo.xml' -o -name '*.appdata.xml' \) -print0 2>/dev/null) || true + + while IFS= read -r -d '' f; do + if grep -a -Eq '\b(KDE|Plasma|PLASMA|Discover|Debian|DEBIAN)\b' "$f" 2>/dev/null; then + divert_mo "$f" || true + patch_mo_in_place_diverted "$f" || true + fi + done < <(find /usr/share/locale -type f -name '*.mo' -print0 2>/dev/null) || true +} + +revert_rebrand() { + require_root + + if [[ -f "${GENERATED_LIST}" ]]; then + local f + while IFS= read -r f; do + [[ -n "${f}" ]] || continue + rm -f "$f" 2>/dev/null || true + done <"${GENERATED_LIST}" + + rm -f "${GENERATED_LIST}" 2>/dev/null || true + fi + + if [[ -f "${DIVERT_LIST}" ]]; then + local f + while IFS= read -r f; do + [[ -n "${f}" ]] || continue + if is_diverted_by_us "$f"; then + rm -f "$f" 2>/dev/null || true + dpkg-divert --package saikyo-kde-rebrand --remove --rename --divert "${f}.saikyo-orig" "$f" >/dev/null || true + fi + done <"${DIVERT_LIST}" + + rm -f "${DIVERT_LIST}" 2>/dev/null || true + fi + + # Best-effort cleanup of empty directories + find /usr/local/share/applications /usr/local/share/metainfo -type d -empty -print0 2>/dev/null | xargs -0r rmdir 2>/dev/null || true +} + +main() { + local cmd="${1:-}" + case "$cmd" in + apply) apply_rebrand;; + revert) revert_rebrand;; + *) + echo "Usage: saikyo-kde-rebrand {apply|revert}" >&2 + exit 2 + ;; + esac +} + +main "$@" diff --git a/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/usr/share/doc/saikyo-kde-rebrand/changelog.gz b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/usr/share/doc/saikyo-kde-rebrand/changelog.gz new file mode 100644 index 0000000..8256459 Binary files /dev/null and b/saikyo-kde-rebrand/debian/saikyo-kde-rebrand/usr/share/doc/saikyo-kde-rebrand/changelog.gz differ diff --git a/saikyo-kde-rebrand/etc/environment.d/90-saikyo-kde-rebrand.conf b/saikyo-kde-rebrand/etc/environment.d/90-saikyo-kde-rebrand.conf new file mode 100644 index 0000000..b9ae837 --- /dev/null +++ b/saikyo-kde-rebrand/etc/environment.d/90-saikyo-kde-rebrand.conf @@ -0,0 +1 @@ +LOCPATH=/usr/local/share/locale:/usr/share/locale diff --git a/saikyo-kde-rebrand/etc/profile.d/saikyo-kde-rebrand.sh b/saikyo-kde-rebrand/etc/profile.d/saikyo-kde-rebrand.sh new file mode 100644 index 0000000..30b2f29 --- /dev/null +++ b/saikyo-kde-rebrand/etc/profile.d/saikyo-kde-rebrand.sh @@ -0,0 +1 @@ +export LOCPATH="/usr/local/share/locale:/usr/share/locale" diff --git a/saikyo-license/assets/icons/saikyo-activation-logo.png b/saikyo-license/assets/icons/saikyo-activation-logo.png new file mode 100644 index 0000000..88b2c88 Binary files /dev/null and b/saikyo-license/assets/icons/saikyo-activation-logo.png differ diff --git a/saikyo-license/assets/saikyo-license-ed25519-pub.pem b/saikyo-license/assets/saikyo-license-ed25519-pub.pem new file mode 100644 index 0000000..5685392 --- /dev/null +++ b/saikyo-license/assets/saikyo-license-ed25519-pub.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAi3wHAzikI3M9tTA8BBeossfIGc8ar8ehHQLQUd6KHI4= +-----END PUBLIC KEY----- diff --git a/saikyo-license/assets/saikyo-license-pub.pem b/saikyo-license/assets/saikyo-license-pub.pem new file mode 100644 index 0000000..c9d243c --- /dev/null +++ b/saikyo-license/assets/saikyo-license-pub.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAiGLdmslGJoZDnmEPX+7Y +R1ABbGHQabzg+srFrg1JcB6aSRbQeR/ClM5BPwmuxx0xGU3YveroBe1aUBqDRT+Z +lXZ9Dk+EQbND02YxFntChazI77CLWYC1ezKoNBSWq6abgnJDlabbkT4EUjT69VF4 +iyJ6OtW4nzRzBzUncnQyV4Q5Nz4mKNfUpzbm5dXQwXFubRSmjHndYHjHsutKrtGL +q/mGzTydbxZBc9JAZDAld2nu+lUwh0naLRxvZfOOAOG61oGFc3xHMiNbW7WdnQYz +0ft9PhdX1DYre+RQB5Ts+afjkIRpoBVVI1hnLGzsp3bKNKmFPCOqYP8yWMKeZ7j8 +GA4uLakPvoMVNYMA2Rh2dMrPsaQToVBmpL/x1z1Pj8ExfMMSLUQZYlmD1CW70pm3 +LSYex/vs+MImlPdBSbovEJA3EdeQmZsy2/jQS9IMbhEPNXuFcF/X/pOcAh/lDn3F +qSBX8tfPF/6wTtyVa3yDuyR1vRD++jNADJ7ZClQFU9OzUjBwFBOcp8WAZMTbI0NL +2PNwMqBVyX0p+dFnn8MyEdL7A4V/8SuVyzZ3xMBB4tPQCf2CZ/TjIIRsQY6qWKKe +TTIvosFxx8O7GgL5caT4ugZhA2LO+FCzB8V9EGlv+AtOODCMQg8WE85uVqJj/u0f +m5NqOQt8GL4V7DkI5WXMkdMCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/saikyo-license/assets/saikyo-license.desktop b/saikyo-license/assets/saikyo-license.desktop new file mode 100644 index 0000000..69bcab9 --- /dev/null +++ b/saikyo-license/assets/saikyo-license.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Type=Application +Name=SAIKYO OS Activation +Name[ru]=Активация SAIKYO OS +Comment=Activate and manage Saikyo OS license +Comment[ru]=Активация и управление лицензией SAIKYO OS +Exec=/usr/bin/saikyo-license-gui +Icon=saikyo-activation-logo +Terminal=false +Categories=Settings;System; +Keywords=license;activation;saikyo;лицензия;активация; diff --git a/saikyo-license/assets/saikyo-license.metainfo.xml b/saikyo-license/assets/saikyo-license.metainfo.xml new file mode 100644 index 0000000..cfdb227 --- /dev/null +++ b/saikyo-license/assets/saikyo-license.metainfo.xml @@ -0,0 +1,22 @@ + + + saikyo-license.desktop + Saikyo OS License + Лицензия Saikyo OS + Saikyo OS offline subscription license tool + Установка и просмотр подписки Saikyo OS + CC0-1.0 + Proprietary + + SAIKYO OS + + https://saikyo-os.ru + mailto:support@saikyo-os.ru + saikyo-license.desktop + + saikyo-license-kde + + +

Offline license installation and signature verification for Saikyo OS subscriptions.

+
+
diff --git a/saikyo-license/bin/saikyo-license b/saikyo-license/bin/saikyo-license new file mode 100644 index 0000000..bfa0df8 --- /dev/null +++ b/saikyo-license/bin/saikyo-license @@ -0,0 +1,445 @@ +#!/usr/bin/env bash +set -euo pipefail + +LICENSE_DIR="/etc/saikyo/license" +PUBKEY_FILE="/usr/share/saikyo-os/license/saikyo-license-pub.pem" +PUBKEY_ED25519_FILE="/usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem" +LICENSE_JSON="${LICENSE_DIR}/license.json" +LICENSE_SIG="${LICENSE_DIR}/license.sig" +TRIAL_START_FILE="${LICENSE_DIR}/trial_start" +TRIAL_DAYS=7 + +require_jq() { + if ! command -v jq >/dev/null 2>&1; then + echo "ERROR: jq is required" >&2 + exit 1 + fi +} + +usage() { + echo "Usage: saikyo-license [args]" >&2 + echo "Commands:" >&2 + echo " install " >&2 + echo " install-code " >&2 + echo " activate --token [--server ]" >&2 + echo " renew --token [--server ]" >&2 + echo " verify" >&2 + echo " status" >&2 + exit 2 +} + +require_root() { + if [[ "$(id -u)" -ne 0 ]]; then + echo "ERROR: must be run as root" >&2 + exit 1 + fi +} + +verify_pair() { + if [[ ! -f "${PUBKEY_FILE}" && ! -f "${PUBKEY_ED25519_FILE}" ]]; then + echo "ERROR: public keys not found in /usr/share/saikyo-os/license" >&2 + exit 1 + fi + if [[ ! -f "${LICENSE_JSON}" || ! -f "${LICENSE_SIG}" ]]; then + if [[ -f "${TRIAL_START_FILE}" ]]; then + local start now end + start=$(cat "${TRIAL_START_FILE}" 2>/dev/null | tr -d '\r\n' | head -c 32) + now=$(date -u +%s 2>/dev/null || echo "") + if [[ -n "${start}" && -n "${now}" && "${start}" =~ ^[0-9]+$ ]]; then + end=$(( start + TRIAL_DAYS*86400 )) + if [[ "${now}" -le "${end}" ]]; then + echo "VALID" + return 0 + fi + fi + fi + echo "INVALID" + return 1 + fi + + require_jq + + # Verify signature over raw JSON bytes. + # New licenses: Ed25519 (pkeyutl -verify -rawin). + # Legacy licenses: RSA (dgst -sha256 -verify). + if [[ -f "${PUBKEY_ED25519_FILE}" ]]; then + if openssl pkeyutl -verify -pubin -inkey "${PUBKEY_ED25519_FILE}" -rawin -in "${LICENSE_JSON}" -sigfile "${LICENSE_SIG}" >/dev/null 2>&1; then + : + else + if [[ -f "${PUBKEY_FILE}" ]]; then + if ! openssl dgst -sha256 -verify "${PUBKEY_FILE}" -signature "${LICENSE_SIG}" "${LICENSE_JSON}" >/dev/null 2>&1; then + echo "INVALID" + return 1 + fi + else + echo "INVALID" + return 1 + fi + fi + else + if ! openssl dgst -sha256 -verify "${PUBKEY_FILE}" -signature "${LICENSE_SIG}" "${LICENSE_JSON}" >/dev/null 2>&1; then + echo "INVALID" + return 1 + fi + fi + + local lic_machine_id lic_valid_until + lic_machine_id=$(jq -r '.machine_id // empty' "${LICENSE_JSON}" 2>/dev/null || true) + lic_valid_until=$(jq -r '.valid_until // empty' "${LICENSE_JSON}" 2>/dev/null || true) + + if [[ -z "${lic_machine_id}" || -z "${lic_valid_until}" ]]; then + echo "INVALID" + return 1 + fi + + local current_machine_id + current_machine_id="" + if [[ -f /etc/machine-id ]]; then + current_machine_id=$(cat /etc/machine-id 2>/dev/null || true) + fi + if [[ -z "${current_machine_id}" || "${lic_machine_id}" != "${current_machine_id}" ]]; then + echo "INVALID" + return 1 + fi + + local now exp + now=$(date -u +%s) + exp=$(date -u -d "${lic_valid_until}" +%s 2>/dev/null || echo "") + if [[ -z "${exp}" || "${now}" -gt "${exp}" ]]; then + echo "INVALID" + return 1 + fi + + echo "VALID" + return 0 +} + +cmd_install() { + require_root + local src_json="${1:-}" + local src_sig="${2:-}" + [[ -n "${src_json}" && -n "${src_sig}" ]] || usage + [[ -f "${src_json}" ]] || { echo "ERROR: file not found: ${src_json}" >&2; exit 1; } + [[ -f "${src_sig}" ]] || { echo "ERROR: file not found: ${src_sig}" >&2; exit 1; } + + require_jq + + local lic_machine_id + lic_machine_id=$(jq -r '.machine_id // empty' "${src_json}" 2>/dev/null || true) + if [[ -z "${lic_machine_id}" ]]; then + echo "ERROR: license.json missing required field: machine_id" >&2 + exit 1 + fi + + local current_machine_id + current_machine_id="" + if [[ -f /etc/machine-id ]]; then + current_machine_id=$(cat /etc/machine-id 2>/dev/null || true) + fi + if [[ -z "${current_machine_id}" ]]; then + echo "ERROR: cannot read /etc/machine-id" >&2 + exit 1 + fi + if [[ "${lic_machine_id}" != "${current_machine_id}" ]]; then + echo "ERROR: machine-id mismatch" >&2 + echo " expected=${lic_machine_id}" >&2 + echo " actual=${current_machine_id}" >&2 + exit 1 + fi + + mkdir -p "${LICENSE_DIR}" + chmod 0755 "${LICENSE_DIR}" + install -m 0644 "${src_json}" "${LICENSE_JSON}" + install -m 0644 "${src_sig}" "${LICENSE_SIG}" + + verify_pair >/dev/null + echo "OK" +} + +cmd_install_code() { + require_root + require_jq + + local rc="${1:-}" + [[ -n "${rc}" ]] || usage + rc=$(printf '%s' "$rc" | tr -d '\r\n\t ' | tr -d '-' | head -c 20000) + + local td lic_json lic_sig + td=$(mktemp -d) + lic_json="${td}/license.json" + lic_sig="${td}/license.sig" + + if printf '%s' "$rc" | grep -q '^SAI-RC:'; then + if ! command -v base32 >/dev/null 2>&1; then + echo "ERROR: base32 is required" >&2 + rm -rf "${td}" || true + exit 1 + fi + local body ppart spart ppad spad + body=${rc#SAI-RC:} + ppart=${body%%.*} + spart=${body#*.} + if [[ -z "${ppart}" || -z "${spart}" || "${ppart}" == "${body}" ]]; then + echo "ERROR: invalid SAI-RC format" >&2 + rm -rf "${td}" || true + exit 1 + fi + ppad=$(( (8 - (${#ppart} % 8)) % 8 )) + spad=$(( (8 - (${#spart} % 8)) % 8 )) + if [[ "$ppad" -gt 0 ]]; then ppart="${ppart}$(printf '%*s' "$ppad" '' | tr ' ' '=')"; fi + if [[ "$spad" -gt 0 ]]; then spart="${spart}$(printf '%*s' "$spad" '' | tr ' ' '=')"; fi + printf '%s' "$ppart" | base32 -d >"${lic_json}" 2>/dev/null || true + printf '%s' "$spart" | base32 -d >"${lic_sig}" 2>/dev/null || true + elif printf '%s' "$rc" | grep -q '^SAI-RESP2:'; then + local body jpart spart jb64 sb64 jpad spad + body=${rc#SAI-RESP2:} + jpart=${body%%.*} + spart=${body#*.} + if [[ -z "${jpart}" || -z "${spart}" || "${jpart}" == "${body}" ]]; then + echo "ERROR: invalid SAI-RESP2 format" >&2 + rm -rf "${td}" || true + exit 1 + fi + jb64=$(printf '%s' "$jpart" | tr '_-' '/+') + sb64=$(printf '%s' "$spart" | tr '_-' '/+') + jpad=$(( (4 - (${#jb64} % 4)) % 4 )) + spad=$(( (4 - (${#sb64} % 4)) % 4 )) + if [[ "$jpad" -eq 1 ]]; then jb64="${jb64}="; elif [[ "$jpad" -eq 2 ]]; then jb64="${jb64}=="; elif [[ "$jpad" -eq 3 ]]; then jb64="${jb64}==="; fi + if [[ "$spad" -eq 1 ]]; then sb64="${sb64}="; elif [[ "$spad" -eq 2 ]]; then sb64="${sb64}=="; elif [[ "$spad" -eq 3 ]]; then sb64="${sb64}==="; fi + printf '%s' "$jb64" | base64 -d >"${lic_json}" 2>/dev/null || true + printf '%s' "$sb64" | base64 -d >"${lic_sig}" 2>/dev/null || true + elif printf '%s' "$rc" | grep -q '^SAI-RESP:'; then + local b64u payload lic_json_b64 lic_sig_b64 pad + b64u=${rc#SAI-RESP:} + b64u=$(printf '%s' "$b64u" | tr '_-' '/+') + pad=$(( (4 - (${#b64u} % 4)) % 4 )) + if [[ "$pad" -eq 1 ]]; then b64u="${b64u}="; elif [[ "$pad" -eq 2 ]]; then b64u="${b64u}=="; elif [[ "$pad" -eq 3 ]]; then b64u="${b64u}==="; fi + payload=$(printf '%s' "$b64u" | base64 -d 2>/dev/null || true) + lic_json_b64=$(printf '%s' "$payload" | sed -n 's/.*"license_json_b64":"\([^"]*\)".*/\1/p' | head -c 20000) + lic_sig_b64=$(printf '%s' "$payload" | sed -n 's/.*"license_sig_b64":"\([^"]*\)".*/\1/p' | head -c 20000) + if [[ -z "${lic_json_b64}" || -z "${lic_sig_b64}" ]]; then + echo "ERROR: invalid SAI-RESP payload" >&2 + rm -rf "${td}" || true + exit 1 + fi + printf '%s' "$lic_json_b64" | base64 -d >"${lic_json}" 2>/dev/null || true + printf '%s' "$lic_sig_b64" | base64 -d >"${lic_sig}" 2>/dev/null || true + else + echo "ERROR: unsupported response-code format" >&2 + rm -rf "${td}" || true + exit 1 + fi + + if [[ ! -s "${lic_json}" || ! -s "${lic_sig}" ]]; then + echo "ERROR: failed to decode response-code" >&2 + rm -rf "${td}" || true + exit 1 + fi + + cmd_install "${lic_json}" "${lic_sig}" + rm -rf "${td}" || true +} + +cmd_verify() { + verify_pair +} + +cmd_activate() { + require_root + require_jq + + local server="https://saikyo-os.ru/license" + local token="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --server) + server="${2:-}"; shift 2 || true + ;; + --token) + token="${2:-}"; shift 2 || true + ;; + -h|--help) + usage + ;; + *) + echo "ERROR: unknown option: $1" >&2 + usage + ;; + esac + done + + if [[ -z "${token}" ]]; then + echo "ERROR: --token is required" >&2 + exit 1 + fi + if [[ -z "${server}" ]]; then + echo "ERROR: --server is empty" >&2 + exit 1 + fi + if ! command -v curl >/dev/null 2>&1; then + echo "ERROR: curl is required for online activation" >&2 + exit 1 + fi + if [[ ! -r /etc/ssl/certs/ca-certificates.crt ]]; then + echo "ERROR: CA bundle not found: /etc/ssl/certs/ca-certificates.crt" >&2 + exit 1 + fi + + local server_host + server_host=$(printf '%s' "${server}" | sed -n 's#^https\?://\([^/]*\).*#\1#p' | head -n 1) + if [[ -n "${server_host}" ]] && command -v getent >/dev/null 2>&1; then + if ! getent ahosts "${server_host}" >/dev/null 2>&1; then + echo "ERROR: cannot resolve server host: ${server_host}" >&2 + exit 1 + fi + fi + + local mid + mid=$(cat /etc/machine-id 2>/dev/null || true) + if [[ -z "${mid}" ]]; then + echo "ERROR: cannot read /etc/machine-id" >&2 + exit 1 + fi + + local req tmpdir resp_json lic_json_b64 lic_sig_b64 + req=$(jq -n --arg token "${token}" --arg machine_id "${mid}" --arg hostname "$(hostname 2>/dev/null || true)" '{token:$token, machine_id:$machine_id, hostname:$hostname}') + + tmpdir=$(mktemp -d) + trap 'rm -rf "${tmpdir}" 2>/dev/null || true' RETURN + + resp_json="${tmpdir}/response.json" + resp_err="${tmpdir}/curl.stderr" + req_file="${tmpdir}/request.json" + http_code="" + + printf '%s' "${req}" > "${req_file}" + + http_code=$(curl -sS \ + --connect-timeout 10 \ + --max-time 30 \ + --retry 3 \ + --retry-delay 1 \ + --retry-connrefused \ + --retry-all-errors \ + -H 'Content-Type: application/json' \ + -X POST \ + --data-binary "@${req_file}" \ + "${server%/}/v1/activate" \ + -o "${resp_json}" \ + -w '%{http_code}' \ + 2>"${resp_err}" || true) + + if [[ "${http_code}" != 2* ]]; then + echo "ERROR: activation request failed (http_code=${http_code:-unknown})" >&2 + if [[ -s "${resp_err}" ]]; then + sed -n '1,120p' "${resp_err}" >&2 || true + fi + if [[ -s "${resp_json}" ]]; then + sed -n '1,200p' "${resp_json}" >&2 || true + fi + exit 1 + fi + + lic_json_b64=$(jq -r '.license_json_b64 // empty' "${resp_json}" 2>/dev/null || true) + lic_sig_b64=$(jq -r '.license_sig_b64 // empty' "${resp_json}" 2>/dev/null || true) + + if [[ -z "${lic_json_b64}" || -z "${lic_sig_b64}" ]]; then + echo "ERROR: invalid response from license server" >&2 + cat "${resp_json}" >&2 || true + exit 1 + fi + + local lic_json_file lic_sig_file + lic_json_file="${tmpdir}/license.json" + lic_sig_file="${tmpdir}/license.sig" + + printf '%s' "${lic_json_b64}" | base64 -d > "${lic_json_file}" + printf '%s' "${lic_sig_b64}" | base64 -d > "${lic_sig_file}" + + cmd_install "${lic_json_file}" "${lic_sig_file}" +} + +cmd_renew() { + cmd_activate "$@" +} + +cmd_status() { + if [[ ! -f "${LICENSE_JSON}" ]]; then + if [[ -f "${TRIAL_START_FILE}" ]]; then + local start now end rem + start=$(cat "${TRIAL_START_FILE}" 2>/dev/null | tr -d '\r\n' | head -c 32) + now=$(date -u +%s 2>/dev/null || echo "") + if [[ -n "${start}" && -n "${now}" && "${start}" =~ ^[0-9]+$ ]]; then + end=$(( start + TRIAL_DAYS*86400 )) + if [[ "${now}" -le "${end}" ]]; then + rem=$(( end - now )) + echo "status=trial" + echo "trial_days=${TRIAL_DAYS}" + echo "trial_remaining_seconds=${rem}" + exit 0 + fi + fi + fi + echo "status=missing" + exit 0 + fi + + local sig_status + if sig_status=$(verify_pair 2>/dev/null); then + echo "signature=${sig_status,,}" + else + echo "signature=invalid" + fi + + require_jq + + local org_id org_name valid_until tier machine_id + org_id=$(jq -r '.org_id // empty' "${LICENSE_JSON}" 2>/dev/null || true) + org_name=$(jq -r '.org_name // empty' "${LICENSE_JSON}" 2>/dev/null || true) + valid_until=$(jq -r '.valid_until // empty' "${LICENSE_JSON}" 2>/dev/null || true) + tier=$(jq -r '.tier // empty' "${LICENSE_JSON}" 2>/dev/null || true) + machine_id=$(jq -r '.machine_id // empty' "${LICENSE_JSON}" 2>/dev/null || true) + + [[ -n "${org_id}" ]] && echo "org_id=${org_id}" + [[ -n "${org_name}" ]] && echo "org_name=${org_name}" + [[ -n "${tier}" ]] && echo "tier=${tier}" + [[ -n "${machine_id}" ]] && echo "machine_id=${machine_id}" + [[ -n "${valid_until}" ]] && echo "valid_until=${valid_until}" + + if [[ -n "${valid_until}" ]]; then + local now exp + now=$(date -u +%s) + exp=$(date -u -d "${valid_until}" +%s 2>/dev/null || echo "") + if [[ -n "${exp}" && "${now}" -gt "${exp}" ]]; then + echo "expired=yes" + else + echo "expired=no" + fi + fi +} + +main() { + local cmd="${1:-}" + shift || true + case "${cmd}" in + install) + cmd_install "$@" + ;; + install-code) + cmd_install_code "$@" + ;; + activate) + cmd_activate "$@" + ;; + renew) + cmd_renew "$@" + ;; + verify) cmd_verify;; + status) cmd_status;; + -h|--help|help|"") usage;; + *) usage;; + esac +} + +main "$@" diff --git a/saikyo-license/bin/saikyo-license-gui b/saikyo-license/bin/saikyo-license-gui new file mode 100644 index 0000000..a1fb87e --- /dev/null +++ b/saikyo-license/bin/saikyo-license-gui @@ -0,0 +1,573 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +SAIKYO OS License Activator - Modern GUI +Beautiful activation interface with dark theme +""" + +import sys +import os +import subprocess +import re +from datetime import datetime + +try: + from PyQt6.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QLabel, QPushButton, QLineEdit, QTextEdit, QFrame, QMessageBox, + QFileDialog, QStackedWidget, QGroupBox, QSpacerItem, QSizePolicy + ) + from PyQt6.QtCore import Qt, QTimer, QSize + from PyQt6.QtGui import QFont, QPixmap, QIcon, QPalette, QColor + PYQT_VERSION = 6 +except ImportError: + from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QLabel, QPushButton, QLineEdit, QTextEdit, QFrame, QMessageBox, + QFileDialog, QStackedWidget, QGroupBox, QSpacerItem, QSizePolicy + ) + from PyQt5.QtCore import Qt, QTimer, QSize + from PyQt5.QtGui import QFont, QPixmap, QIcon, QPalette, QColor + PYQT_VERSION = 5 + +# Saikyo OS color scheme +COLORS = { + 'bg_dark': '#0a0a0a', + 'bg_card': '#1a1a1a', + 'bg_input': '#252525', + 'accent': '#00ff66', + 'accent_hover': '#00cc52', + 'text': '#ffffff', + 'text_secondary': '#888888', + 'border': '#333333', + 'success': '#00ff66', + 'warning': '#ffaa00', + 'error': '#ff4444', +} + +STYLESHEET = f""" +QMainWindow, QWidget {{ + background-color: {COLORS['bg_dark']}; + color: {COLORS['text']}; + font-family: 'Segoe UI', 'Ubuntu', sans-serif; +}} + +QLabel {{ + color: {COLORS['text']}; + background: transparent; +}} + +QLabel#title {{ + font-size: 28px; + font-weight: bold; + color: {COLORS['accent']}; +}} + +QLabel#subtitle {{ + font-size: 14px; + color: {COLORS['text_secondary']}; +}} + +QLabel#status_label {{ + font-size: 16px; + font-weight: bold; + padding: 10px; + border-radius: 8px; +}} + +QLabel#status_active {{ + background-color: rgba(0, 255, 102, 0.15); + color: {COLORS['success']}; + border: 1px solid {COLORS['success']}; +}} + +QLabel#status_trial {{ + background-color: rgba(255, 170, 0, 0.15); + color: {COLORS['warning']}; + border: 1px solid {COLORS['warning']}; +}} + +QLabel#status_expired, QLabel#status_missing {{ + background-color: rgba(255, 68, 68, 0.15); + color: {COLORS['error']}; + border: 1px solid {COLORS['error']}; +}} + +QPushButton {{ + background-color: {COLORS['bg_card']}; + color: {COLORS['text']}; + border: 1px solid {COLORS['border']}; + border-radius: 8px; + padding: 12px 24px; + font-size: 14px; + font-weight: 500; + min-width: 120px; +}} + +QPushButton:hover {{ + background-color: {COLORS['bg_input']}; + border-color: {COLORS['accent']}; +}} + +QPushButton:pressed {{ + background-color: {COLORS['accent']}; + color: {COLORS['bg_dark']}; +}} + +QPushButton#primary {{ + background-color: {COLORS['accent']}; + color: {COLORS['bg_dark']}; + border: none; + font-weight: bold; +}} + +QPushButton#primary:hover {{ + background-color: {COLORS['accent_hover']}; +}} + +QLineEdit, QTextEdit {{ + background-color: {COLORS['bg_input']}; + color: {COLORS['text']}; + border: 1px solid {COLORS['border']}; + border-radius: 6px; + padding: 10px; + font-size: 14px; +}} + +QLineEdit:focus, QTextEdit:focus {{ + border-color: {COLORS['accent']}; +}} + +QGroupBox {{ + background-color: {COLORS['bg_card']}; + border: 1px solid {COLORS['border']}; + border-radius: 12px; + margin-top: 16px; + padding: 20px; + font-size: 14px; + font-weight: bold; +}} + +QGroupBox::title {{ + subcontrol-origin: margin; + left: 20px; + padding: 0 10px; + color: {COLORS['text_secondary']}; +}} + +QFrame#separator {{ + background-color: {COLORS['border']}; + max-height: 1px; +}} + +QFrame#card {{ + background-color: {COLORS['bg_card']}; + border: 1px solid {COLORS['border']}; + border-radius: 12px; + padding: 20px; +}} +""" + + +class LicenseManager: + """Interface to saikyo-license CLI""" + + LICENSE_CMD = '/usr/sbin/saikyo-license' + + @classmethod + def run_cmd(cls, *args): + try: + result = subprocess.run( + [cls.LICENSE_CMD] + list(args), + capture_output=True, text=True, timeout=30 + ) + return result.returncode, result.stdout, result.stderr + except Exception as e: + return -1, '', str(e) + + @classmethod + def run_privileged(cls, *args): + try: + result = subprocess.run( + ['pkexec', cls.LICENSE_CMD] + list(args), + capture_output=True, text=True, timeout=60 + ) + return result.returncode, result.stdout, result.stderr + except Exception as e: + return -1, '', str(e) + + @classmethod + def get_status(cls): + code, out, err = cls.run_cmd('status') + status = { + 'status': 'unknown', + 'trial_days': '', + 'trial_remaining_seconds': '0', + 'valid_until': '', + 'expired': 'no', + 'org_name': '', + 'tier': '', + 'machine_id': '', + } + for line in out.split('\n'): + if '=' in line: + k, v = line.split('=', 1) + k = k.strip().replace('-', '_') + status[k] = v.strip() + return status + + @classmethod + def verify(cls): + code, _, _ = cls.run_cmd('verify') + return code == 0 + + @classmethod + def install_code(cls, code): + return cls.run_privileged('install-code', code) + + @classmethod + def install_files(cls, json_path, sig_path): + return cls.run_privileged('install', json_path, sig_path) + + @classmethod + def activate_online(cls, token, server='https://saikyo-os.ru/license'): + return cls.run_privileged('activate', '--token', token, '--server', server) + + @classmethod + def get_machine_id(cls): + try: + with open('/etc/machine-id', 'r') as f: + return f.read().strip() + except: + return 'Не удалось прочитать' + + +def human_duration(seconds): + try: + s = int(seconds) + except: + return '—' + if s <= 0: + return '0м' + d = s // 86400 + h = (s % 86400) // 3600 + m = (s % 3600) // 60 + if d > 0: + return f'{d}д {h}ч {m}м' + elif h > 0: + return f'{h}ч {m}м' + else: + return f'{m}м' + + +class StatusCard(QFrame): + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName('card') + self.setup_ui() + + def setup_ui(self): + layout = QVBoxLayout(self) + layout.setSpacing(16) + + # Status badge + self.status_label = QLabel('Загрузка...') + self.status_label.setObjectName('status_label') + self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter if PYQT_VERSION == 6 else Qt.AlignCenter) + layout.addWidget(self.status_label) + + # Info grid + info_layout = QVBoxLayout() + info_layout.setSpacing(8) + + self.org_label = self._create_info_row('Организация:', '—') + self.tier_label = self._create_info_row('Тариф:', '—') + self.remaining_label = self._create_info_row('Осталось:', '—') + self.valid_until_label = self._create_info_row('Действует до:', '—') + self.machine_id_label = self._create_info_row('Machine ID:', '—') + + info_layout.addLayout(self.org_label[0]) + info_layout.addLayout(self.tier_label[0]) + info_layout.addLayout(self.remaining_label[0]) + info_layout.addLayout(self.valid_until_label[0]) + info_layout.addLayout(self.machine_id_label[0]) + + layout.addLayout(info_layout) + + def _create_info_row(self, label_text, value_text): + layout = QHBoxLayout() + label = QLabel(label_text) + label.setStyleSheet(f'color: {COLORS["text_secondary"]}; font-size: 13px;') + value = QLabel(value_text) + value.setStyleSheet('font-size: 13px; font-weight: 500;') + value.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse if PYQT_VERSION == 6 else Qt.TextSelectableByMouse) + layout.addWidget(label) + layout.addStretch() + layout.addWidget(value) + return (layout, value) + + def update_status(self, status_data): + status = status_data.get('status', 'unknown') + + # Update status badge + if status == 'trial': + self.status_label.setText('⏱ ПРОБНЫЙ ПЕРИОД') + self.status_label.setObjectName('status_trial') + elif status == 'missing': + self.status_label.setText('⚠ НЕ АКТИВИРОВАНА') + self.status_label.setObjectName('status_missing') + elif status_data.get('expired') == 'yes': + self.status_label.setText('✗ ИСТЕКЛА') + self.status_label.setObjectName('status_expired') + elif LicenseManager.verify(): + self.status_label.setText('✓ АКТИВНА') + self.status_label.setObjectName('status_active') + else: + self.status_label.setText('? НЕИЗВЕСТНО') + self.status_label.setObjectName('status_missing') + + # Force style refresh + self.status_label.setStyleSheet(self.status_label.styleSheet()) + + # Update info + self.org_label[1].setText(status_data.get('org_name') or '—') + self.tier_label[1].setText(status_data.get('tier') or '—') + + # Calculate remaining time + if status == 'trial': + remaining = human_duration(status_data.get('trial_remaining_seconds', 0)) + elif status_data.get('valid_until'): + try: + exp = datetime.fromisoformat(status_data['valid_until'].replace('Z', '+00:00')) + now = datetime.now(exp.tzinfo) if exp.tzinfo else datetime.now() + delta = (exp - now).total_seconds() + remaining = human_duration(delta) if delta > 0 else 'Истекла' + except: + remaining = '—' + else: + remaining = '—' + + self.remaining_label[1].setText(remaining) + self.valid_until_label[1].setText(status_data.get('valid_until') or '—') + self.machine_id_label[1].setText(LicenseManager.get_machine_id()[:16] + '...') + + +class ActivationWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setup_ui() + + def setup_ui(self): + layout = QVBoxLayout(self) + layout.setSpacing(20) + + # Online activation + online_group = QGroupBox('Онлайн активация') + online_layout = QVBoxLayout(online_group) + + self.token_input = QLineEdit() + self.token_input.setPlaceholderText('Введите токен активации...') + online_layout.addWidget(self.token_input) + + self.activate_btn = QPushButton('Активировать') + self.activate_btn.setObjectName('primary') + self.activate_btn.clicked.connect(self.activate_online) + online_layout.addWidget(self.activate_btn) + + layout.addWidget(online_group) + + # Offline activation + offline_group = QGroupBox('Оффлайн активация (response-код)') + offline_layout = QVBoxLayout(offline_group) + + self.code_input = QTextEdit() + self.code_input.setPlaceholderText('Вставьте response-код (SAI-RC:... или SAI-RESP2:...)') + self.code_input.setMaximumHeight(100) + offline_layout.addWidget(self.code_input) + + self.offline_btn = QPushButton('Активировать оффлайн') + self.offline_btn.clicked.connect(self.activate_offline) + offline_layout.addWidget(self.offline_btn) + + layout.addWidget(offline_group) + + # File installation + file_group = QGroupBox('Установка из файлов') + file_layout = QVBoxLayout(file_group) + + file_hint = QLabel('Выберите license.json и license.sig') + file_hint.setStyleSheet(f'color: {COLORS["text_secondary"]}; font-size: 12px;') + file_layout.addWidget(file_hint) + + self.file_btn = QPushButton('Выбрать файлы...') + self.file_btn.clicked.connect(self.install_from_files) + file_layout.addWidget(self.file_btn) + + layout.addWidget(file_group) + + layout.addStretch() + + def activate_online(self): + token = self.token_input.text().strip() + if not token: + QMessageBox.warning(self, 'Ошибка', 'Введите токен активации') + return + + code, out, err = LicenseManager.activate_online(token) + if code == 0: + QMessageBox.information(self, 'Успех', 'Лицензия успешно активирована!') + self.token_input.clear() + self.parent().parent().refresh_status() + else: + QMessageBox.critical(self, 'Ошибка', f'Ошибка активации:\n{err or out}') + + def activate_offline(self): + code = self.code_input.toPlainText().strip() + if not code: + QMessageBox.warning(self, 'Ошибка', 'Вставьте response-код') + return + + ret, out, err = LicenseManager.install_code(code) + if ret == 0: + QMessageBox.information(self, 'Успех', 'Лицензия успешно установлена!') + self.code_input.clear() + self.parent().parent().refresh_status() + else: + QMessageBox.critical(self, 'Ошибка', f'Ошибка установки:\n{err or out}') + + def install_from_files(self): + json_path, _ = QFileDialog.getOpenFileName( + self, 'Выберите license.json', os.path.expanduser('~'), + 'JSON files (*.json);;All files (*)' + ) + if not json_path: + return + + sig_path, _ = QFileDialog.getOpenFileName( + self, 'Выберите license.sig', os.path.dirname(json_path), + 'Signature files (*.sig);;All files (*)' + ) + if not sig_path: + return + + ret, out, err = LicenseManager.install_files(json_path, sig_path) + if ret == 0: + QMessageBox.information(self, 'Успех', 'Лицензия успешно установлена!') + self.parent().parent().refresh_status() + else: + QMessageBox.critical(self, 'Ошибка', f'Ошибка установки:\n{err or out}') + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle('SAIKYO OS — Активация лицензии') + self.setMinimumSize(500, 650) + self.setMaximumSize(600, 800) + + # Set window icon + icon_path = '/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png' + if os.path.exists(icon_path): + self.setWindowIcon(QIcon(icon_path)) + + self.setup_ui() + self.refresh_status() + + def setup_ui(self): + central = QWidget() + self.setCentralWidget(central) + + layout = QVBoxLayout(central) + layout.setContentsMargins(30, 30, 30, 30) + layout.setSpacing(20) + + # Header + header_layout = QVBoxLayout() + header_layout.setSpacing(8) + + # Logo + logo_path = '/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png' + if os.path.exists(logo_path): + logo_label = QLabel() + pixmap = QPixmap(logo_path).scaled( + 80, 80, + Qt.AspectRatioMode.KeepAspectRatio if PYQT_VERSION == 6 else Qt.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation if PYQT_VERSION == 6 else Qt.SmoothTransformation + ) + logo_label.setPixmap(pixmap) + logo_label.setAlignment(Qt.AlignmentFlag.AlignCenter if PYQT_VERSION == 6 else Qt.AlignCenter) + header_layout.addWidget(logo_label) + + title = QLabel('SAIKYO OS') + title.setObjectName('title') + title.setAlignment(Qt.AlignmentFlag.AlignCenter if PYQT_VERSION == 6 else Qt.AlignCenter) + header_layout.addWidget(title) + + subtitle = QLabel('Активация лицензии') + subtitle.setObjectName('subtitle') + subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter if PYQT_VERSION == 6 else Qt.AlignCenter) + header_layout.addWidget(subtitle) + + layout.addLayout(header_layout) + + # Status card + self.status_card = StatusCard() + layout.addWidget(self.status_card) + + # Activation widget + self.activation_widget = ActivationWidget(self) + layout.addWidget(self.activation_widget) + + # Footer buttons + footer_layout = QHBoxLayout() + + refresh_btn = QPushButton('Обновить') + refresh_btn.clicked.connect(self.refresh_status) + footer_layout.addWidget(refresh_btn) + + portal_btn = QPushButton('Открыть портал') + portal_btn.clicked.connect(self.open_portal) + footer_layout.addWidget(portal_btn) + + close_btn = QPushButton('Закрыть') + close_btn.clicked.connect(self.close) + footer_layout.addWidget(close_btn) + + layout.addLayout(footer_layout) + + def refresh_status(self): + status = LicenseManager.get_status() + self.status_card.update_status(status) + + def open_portal(self): + try: + subprocess.Popen(['xdg-open', 'https://saikyo-os.ru/license'], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except: + pass + + +def main(): + app = QApplication(sys.argv) + app.setStyle('Fusion') + app.setStyleSheet(STYLESHEET) + + # Dark palette + palette = QPalette() + palette.setColor(QPalette.ColorRole.Window if PYQT_VERSION == 6 else QPalette.Window, QColor(COLORS['bg_dark'])) + palette.setColor(QPalette.ColorRole.WindowText if PYQT_VERSION == 6 else QPalette.WindowText, QColor(COLORS['text'])) + palette.setColor(QPalette.ColorRole.Base if PYQT_VERSION == 6 else QPalette.Base, QColor(COLORS['bg_input'])) + palette.setColor(QPalette.ColorRole.Text if PYQT_VERSION == 6 else QPalette.Text, QColor(COLORS['text'])) + palette.setColor(QPalette.ColorRole.Button if PYQT_VERSION == 6 else QPalette.Button, QColor(COLORS['bg_card'])) + palette.setColor(QPalette.ColorRole.ButtonText if PYQT_VERSION == 6 else QPalette.ButtonText, QColor(COLORS['text'])) + palette.setColor(QPalette.ColorRole.Highlight if PYQT_VERSION == 6 else QPalette.Highlight, QColor(COLORS['accent'])) + app.setPalette(palette) + + window = MainWindow() + window.show() + + sys.exit(app.exec() if PYQT_VERSION == 6 else app.exec_()) + + +if __name__ == '__main__': + main() diff --git a/saikyo-license/bin/saikyo-license-kde b/saikyo-license/bin/saikyo-license-kde new file mode 100644 index 0000000..6e983c6 --- /dev/null +++ b/saikyo-license/bin/saikyo-license-kde @@ -0,0 +1,263 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Minimal KDE UI wrapper for Saikyo OS license install/status. +# Uses kdialog and pkexec. + +if ! command -v kdialog >/dev/null 2>&1; then + echo "ERROR: kdialog not found" >&2 + exit 1 +fi + +TITLE="SAIKYO OS — лицензия" +ICON="/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png" + +human_duration() { + local s="${1:-0}" + if [[ -z "${s}" || ! "${s}" =~ ^[0-9]+$ ]]; then + printf '%s' "—" + return 0 + fi + local d h m + d=$((s/86400)) + h=$(((s%86400)/3600)) + m=$(((s%3600)/60)) + if [[ "$d" -gt 0 ]]; then + printf '%sd %sh %sm' "$d" "$h" "$m" + elif [[ "$h" -gt 0 ]]; then + printf '%sh %sm' "$h" "$m" + else + printf '%sm' "$m" + fi +} + +parse_kv() { + # Reads key=value lines from stdin and exports them as shell vars. + local line k v + while IFS= read -r line; do + [[ -n "${line}" ]] || continue + case "${line}" in + *=*) + k=${line%%=*} + v=${line#*=} + k=${k//-/_} + case "${k}" in + status|trial_days|trial_remaining_seconds|valid_until|expired|org_id|org_name|tier|machine_id|signature) + printf -v "$k" '%s' "$v" + ;; + esac + ;; + esac + done +} + +get_status_fields() { + status=""; trial_days=""; trial_remaining_seconds=""; valid_until=""; expired=""; org_id=""; org_name=""; tier=""; machine_id=""; signature="" + local out + out=$(/usr/sbin/saikyo-license status 2>/dev/null || true) + parse_kv <<<"${out}" +} + +status_summary() { + get_status_fields + + local state="UNKNOWN" + local rem="—" + + if [[ "${status:-}" == "trial" ]]; then + state="TRIAL" + rem=$(human_duration "${trial_remaining_seconds:-0}") + elif [[ "${status:-}" == "missing" ]]; then + state="MISSING" + rem="—" + else + if [[ "${expired:-}" == "yes" ]]; then + state="EXPIRED" + else + # If we have a valid_until and not expired=yes, treat as active (even if signature says invalid we still show it). + if [[ -n "${valid_until:-}" ]]; then + state="ACTIVE" + else + state="UNKNOWN" + fi + fi + + if [[ -n "${valid_until:-}" ]]; then + local now exp_s + now=$(date -u +%s 2>/dev/null || echo "") + exp_s=$(date -u -d "${valid_until}" +%s 2>/dev/null || echo "") + if [[ -n "${now}" && -n "${exp_s}" && "${exp_s}" =~ ^[0-9]+$ && "${now}" =~ ^[0-9]+$ && "${exp_s}" -gt "${now}" ]]; then + rem=$(human_duration $((exp_s - now))) + else + rem="—" + fi + fi + fi + + local org="—" + [[ -n "${org_name:-}" ]] && org="${org_name}" + + local tier_txt="—" + [[ -n "${tier:-}" ]] && tier_txt="${tier}" + + local until_txt="—" + [[ -n "${valid_until:-}" ]] && until_txt="${valid_until}" + + local sig_txt="—" + [[ -n "${signature:-}" ]] && sig_txt="${signature}" + + printf '%s\n' \ + "Статус: ${state}" \ + "Осталось: ${rem}" \ + "Организация: ${org}" \ + "Тариф: ${tier_txt}" \ + "Действует до: ${until_txt}" \ + "Подпись: ${sig_txt}" +} + +has_valid_license() { + # saikyo-license verify returns 0 only for a valid, installed license + /usr/sbin/saikyo-license verify >/dev/null 2>&1 +} + +show_status() { + kdialog --title "$TITLE" --icon "$ICON" --msgbox "$(status_summary)" +} + +install_license() { + local json sig + + json=$(kdialog --title "$TITLE" --getopenfilename "$HOME" "license.json|license.json (*.json)" 2>/dev/null || true) + [[ -n "${json}" ]] || return 0 + + sig=$(kdialog --title "$TITLE" --getopenfilename "$HOME" "license.sig|license.sig (*.sig)" 2>/dev/null || true) + [[ -n "${sig}" ]] || return 0 + + # Run privileged install + local cmd + cmd=(pkexec /usr/sbin/saikyo-license install "$json" "$sig") + + local result + result=$(${cmd[@]} 2>&1) || { + kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка установки лицензии:\n\n${result}" + return 1 + } + + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия установлена.\n\n${result}" + show_status +} + +install_response_code() { + local code + code=$(kdialog --title "$TITLE" --icon "$ICON" --inputbox "SAIKYO OS оффлайн активация\n\nВставьте response-код (SAI-RC / SAI-RESP2 / SAI-RESP)" "" 2>/dev/null || true) + [[ -n "${code}" ]] || return 0 + + local result + result=$(pkexec /usr/sbin/saikyo-license install-code "${code}" 2>&1) || { + kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка оффлайн активации:\n\n${result}" + return 1 + } + + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия установлена.\n\n${result}" + show_status +} + +show_files_info() { + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Файлы лицензии:\n\n/etc/saikyo/license/license.json\n/etc/saikyo/license/license.sig\n/etc/saikyo/license/trial_start (если trial)\n\nКлючи проверки:\n/usr/share/saikyo-os/license/saikyo-license-pub.pem\n/usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem" 2>/dev/null || true +} + +show_diagnostics() { + local out + out=$( (/usr/sbin/saikyo-license status 2>&1 || true; echo; /usr/sbin/saikyo-license verify 2>&1 || true) | sed -n '1,200p' ) + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Диагностика (первые 200 строк):\n\n${out}" 2>/dev/null || true +} + +show_machine_id() { + local mid + mid=$(cat /etc/machine-id 2>/dev/null || true) + if [[ -z "${mid}" ]]; then + kdialog --title "$TITLE" --icon "$ICON" --error "Не удалось прочитать /etc/machine-id" + return 1 + fi + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Идентификатор машины (machine-id):\n\n${mid}" +} + +activate_online() { + local token + local server + server="https://saikyo-os.ru/license" + + token=$(kdialog --title "$TITLE" --icon "$ICON" --inputbox "SAIKYO OS активация\n\nВведите токен активации" "" 2>/dev/null || true) + [[ -n "${token}" ]] || return 0 + + local result + result=$(pkexec /usr/sbin/saikyo-license activate --token "${token}" --server "${server}" 2>&1) || { + kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка онлайн-активации:\n\n${result}" + return 1 + } + + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Онлайн-активация выполнена.\n\n${result}" + show_status +} + +open_portal() { + if command -v xdg-open >/dev/null 2>&1; then + xdg-open "https://saikyo-os.ru/license" >/dev/null 2>&1 || true + fi +} + +force_activation() { + # Non-closable UX (as much as possible): closing/canceling just re-opens. + # The only exit is a successful activation (valid license). + if has_valid_license; then + exit 0 + fi + + while true; do + if has_valid_license; then + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия активирована. Спасибо." + exit 0 + fi + + kdialog --title "$TITLE" --icon "$ICON" --msgbox "SAIKYO OS активация\n\nДля продолжения работы необходимо активировать лицензию.\n\nНажмите OK, чтобы ввести токен." 2>/dev/null || true + + if activate_online; then + : + else + # Show error handled inside activate_online; loop. + : + fi + done +} + +if [[ "${1:-}" == "--force-activate" ]]; then + shift || true + force_activation +fi + +while true; do + header=$(status_summary) + action=$(kdialog --title "$TITLE" --icon "$ICON" --menu "${header}\n\nДействия:" \ + status "Показать статус" \ + activate "Онлайн-активация/продление (токен)" \ + offline "Оффлайн активация (response-код)" \ + install "Установить лицензию (license.json + license.sig)" \ + portal "Открыть портал" \ + files "Где лежат файлы/ключи" \ + diag "Диагностика" \ + machine-id "Показать machine-id" \ + exit "Выход" \ + 2>/dev/null || true) + + case "$action" in + status) show_status;; + activate) activate_online;; + offline) install_response_code;; + install) install_license;; + portal) open_portal;; + files) show_files_info;; + diag) show_diagnostics;; + machine-id) show_machine_id;; + exit|"") exit 0;; + esac +done diff --git a/saikyo-license/debian/.debhelper/generated/saikyo-license/dh_installchangelogs.dch.trimmed b/saikyo-license/debian/.debhelper/generated/saikyo-license/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..b91afbd --- /dev/null +++ b/saikyo-license/debian/.debhelper/generated/saikyo-license/dh_installchangelogs.dch.trimmed @@ -0,0 +1,5 @@ +saikyo-license (1.0.2) stable; urgency=medium + + * Improve KDE license UI: status-first view with remaining time and diagnostics. + + -- SAIKYO OS Mon, 20 Jan 2026 12:30:00 +0000 diff --git a/saikyo-license/debian/.debhelper/generated/saikyo-license/installed-by-dh_install b/saikyo-license/debian/.debhelper/generated/saikyo-license/installed-by-dh_install new file mode 100644 index 0000000..93930aa --- /dev/null +++ b/saikyo-license/debian/.debhelper/generated/saikyo-license/installed-by-dh_install @@ -0,0 +1,8 @@ +./bin/saikyo-license +./bin/saikyo-license-kde +./bin/saikyo-license-gui +./assets/saikyo-license-pub.pem +./assets/saikyo-license-ed25519-pub.pem +./assets/saikyo-license.desktop +./assets/saikyo-license.metainfo.xml +./assets/icons/saikyo-activation-logo.png diff --git a/saikyo-license/debian/.debhelper/generated/saikyo-license/installed-by-dh_installdocs b/saikyo-license/debian/.debhelper/generated/saikyo-license/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-license/debian/changelog b/saikyo-license/debian/changelog new file mode 100644 index 0000000..b91afbd --- /dev/null +++ b/saikyo-license/debian/changelog @@ -0,0 +1,5 @@ +saikyo-license (1.0.2) stable; urgency=medium + + * Improve KDE license UI: status-first view with remaining time and diagnostics. + + -- SAIKYO OS Mon, 20 Jan 2026 12:30:00 +0000 diff --git a/saikyo-license/debian/control b/saikyo-license/debian/control new file mode 100644 index 0000000..b3ecbe2 --- /dev/null +++ b/saikyo-license/debian/control @@ -0,0 +1,14 @@ +Source: saikyo-license +Section: admin +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-license +Architecture: all +Depends: ${misc:Depends}, bash, coreutils, openssl, jq, curl, kdialog, polkitd, python3, python3-pyqt6 | python3-pyqt5 +Description: Saikyo OS offline subscription license tool + Offline license installation and signature verification for Saikyo OS subscriptions. + Includes modern GUI activation interface. diff --git a/saikyo-license/debian/copyright b/saikyo-license/debian/copyright new file mode 100644 index 0000000..6d0fe07 --- /dev/null +++ b/saikyo-license/debian/copyright @@ -0,0 +1,11 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-license +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 ООО «САЙКО» +License: Proprietary + This package provides Saikyo OS subscription license tooling. + +License: Proprietary + All rights reserved. diff --git a/saikyo-license/debian/debhelper-build-stamp b/saikyo-license/debian/debhelper-build-stamp new file mode 100644 index 0000000..7c3bf7d --- /dev/null +++ b/saikyo-license/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-license diff --git a/saikyo-license/debian/files b/saikyo-license/debian/files new file mode 100644 index 0000000..0340604 --- /dev/null +++ b/saikyo-license/debian/files @@ -0,0 +1,2 @@ +saikyo-license_1.0.2_all.deb admin optional +saikyo-license_1.0.2_amd64.buildinfo admin optional diff --git a/saikyo-license/debian/install b/saikyo-license/debian/install new file mode 100644 index 0000000..2c7f601 --- /dev/null +++ b/saikyo-license/debian/install @@ -0,0 +1,8 @@ +bin/saikyo-license usr/sbin/ +bin/saikyo-license-kde usr/bin/ +bin/saikyo-license-gui usr/bin/ +assets/saikyo-license-pub.pem usr/share/saikyo-os/license/ +assets/saikyo-license-ed25519-pub.pem usr/share/saikyo-os/license/ +assets/saikyo-license.desktop usr/share/applications/ +assets/saikyo-license.metainfo.xml usr/share/metainfo/ +assets/icons/saikyo-activation-logo.png usr/share/icons/hicolor/512x512/apps/ diff --git a/saikyo-license/debian/rules b/saikyo-license/debian/rules new file mode 100755 index 0000000..3cecec2 --- /dev/null +++ b/saikyo-license/debian/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: + +override_dh_fixperms: + dh_fixperms + chmod 0755 debian/saikyo-license/usr/sbin/saikyo-license + chmod 0755 debian/saikyo-license/usr/bin/saikyo-license-kde diff --git a/saikyo-license/debian/saikyo-license.debhelper.log b/saikyo-license/debian/saikyo-license.debhelper.log new file mode 100644 index 0000000..93c5512 --- /dev/null +++ b/saikyo-license/debian/saikyo-license.debhelper.log @@ -0,0 +1 @@ +dh_fixperms diff --git a/saikyo-license/debian/saikyo-license.substvars b/saikyo-license/debian/saikyo-license.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-license/debian/saikyo-license.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-license/debian/saikyo-license/DEBIAN/control b/saikyo-license/debian/saikyo-license/DEBIAN/control new file mode 100644 index 0000000..2c5959f --- /dev/null +++ b/saikyo-license/debian/saikyo-license/DEBIAN/control @@ -0,0 +1,11 @@ +Package: saikyo-license +Version: 1.0.2 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 184 +Depends: bash, coreutils, openssl, jq, curl, kdialog, polkitd, python3, python3-pyqt6 | python3-pyqt5 +Section: admin +Priority: optional +Description: Saikyo OS offline subscription license tool + Offline license installation and signature verification for Saikyo OS subscriptions. + Includes modern GUI activation interface. diff --git a/saikyo-license/debian/saikyo-license/DEBIAN/md5sums b/saikyo-license/debian/saikyo-license/DEBIAN/md5sums new file mode 100644 index 0000000..c8b3eb7 --- /dev/null +++ b/saikyo-license/debian/saikyo-license/DEBIAN/md5sums @@ -0,0 +1,10 @@ +484620cc45a0098677bfab81740ba4a4 usr/bin/saikyo-license-gui +49d7565a213eda8ddb1bbe625309ff7b usr/bin/saikyo-license-kde +5f4f834d1ac02db920539924070bcb17 usr/sbin/saikyo-license +c8e810240b4d47f8133505f117c30348 usr/share/applications/saikyo-license.desktop +5efc42b8d610614284dfe3c573d8e0a0 usr/share/doc/saikyo-license/changelog.gz +3836e4017c30fefa555dc1f04dd06c3c usr/share/doc/saikyo-license/copyright +8da376ea0ea42d60582834dccfe5175c usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png +59b69aae21db488bdee87e90077386e9 usr/share/metainfo/saikyo-license.metainfo.xml +44df3cb7eec70c18553e74d30bdbf580 usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem +051860606507eb83142eb56c4f71a828 usr/share/saikyo-os/license/saikyo-license-pub.pem diff --git a/saikyo-license/debian/saikyo-license/usr/bin/saikyo-license-gui b/saikyo-license/debian/saikyo-license/usr/bin/saikyo-license-gui new file mode 100755 index 0000000..a1fb87e --- /dev/null +++ b/saikyo-license/debian/saikyo-license/usr/bin/saikyo-license-gui @@ -0,0 +1,573 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +SAIKYO OS License Activator - Modern GUI +Beautiful activation interface with dark theme +""" + +import sys +import os +import subprocess +import re +from datetime import datetime + +try: + from PyQt6.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QLabel, QPushButton, QLineEdit, QTextEdit, QFrame, QMessageBox, + QFileDialog, QStackedWidget, QGroupBox, QSpacerItem, QSizePolicy + ) + from PyQt6.QtCore import Qt, QTimer, QSize + from PyQt6.QtGui import QFont, QPixmap, QIcon, QPalette, QColor + PYQT_VERSION = 6 +except ImportError: + from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QLabel, QPushButton, QLineEdit, QTextEdit, QFrame, QMessageBox, + QFileDialog, QStackedWidget, QGroupBox, QSpacerItem, QSizePolicy + ) + from PyQt5.QtCore import Qt, QTimer, QSize + from PyQt5.QtGui import QFont, QPixmap, QIcon, QPalette, QColor + PYQT_VERSION = 5 + +# Saikyo OS color scheme +COLORS = { + 'bg_dark': '#0a0a0a', + 'bg_card': '#1a1a1a', + 'bg_input': '#252525', + 'accent': '#00ff66', + 'accent_hover': '#00cc52', + 'text': '#ffffff', + 'text_secondary': '#888888', + 'border': '#333333', + 'success': '#00ff66', + 'warning': '#ffaa00', + 'error': '#ff4444', +} + +STYLESHEET = f""" +QMainWindow, QWidget {{ + background-color: {COLORS['bg_dark']}; + color: {COLORS['text']}; + font-family: 'Segoe UI', 'Ubuntu', sans-serif; +}} + +QLabel {{ + color: {COLORS['text']}; + background: transparent; +}} + +QLabel#title {{ + font-size: 28px; + font-weight: bold; + color: {COLORS['accent']}; +}} + +QLabel#subtitle {{ + font-size: 14px; + color: {COLORS['text_secondary']}; +}} + +QLabel#status_label {{ + font-size: 16px; + font-weight: bold; + padding: 10px; + border-radius: 8px; +}} + +QLabel#status_active {{ + background-color: rgba(0, 255, 102, 0.15); + color: {COLORS['success']}; + border: 1px solid {COLORS['success']}; +}} + +QLabel#status_trial {{ + background-color: rgba(255, 170, 0, 0.15); + color: {COLORS['warning']}; + border: 1px solid {COLORS['warning']}; +}} + +QLabel#status_expired, QLabel#status_missing {{ + background-color: rgba(255, 68, 68, 0.15); + color: {COLORS['error']}; + border: 1px solid {COLORS['error']}; +}} + +QPushButton {{ + background-color: {COLORS['bg_card']}; + color: {COLORS['text']}; + border: 1px solid {COLORS['border']}; + border-radius: 8px; + padding: 12px 24px; + font-size: 14px; + font-weight: 500; + min-width: 120px; +}} + +QPushButton:hover {{ + background-color: {COLORS['bg_input']}; + border-color: {COLORS['accent']}; +}} + +QPushButton:pressed {{ + background-color: {COLORS['accent']}; + color: {COLORS['bg_dark']}; +}} + +QPushButton#primary {{ + background-color: {COLORS['accent']}; + color: {COLORS['bg_dark']}; + border: none; + font-weight: bold; +}} + +QPushButton#primary:hover {{ + background-color: {COLORS['accent_hover']}; +}} + +QLineEdit, QTextEdit {{ + background-color: {COLORS['bg_input']}; + color: {COLORS['text']}; + border: 1px solid {COLORS['border']}; + border-radius: 6px; + padding: 10px; + font-size: 14px; +}} + +QLineEdit:focus, QTextEdit:focus {{ + border-color: {COLORS['accent']}; +}} + +QGroupBox {{ + background-color: {COLORS['bg_card']}; + border: 1px solid {COLORS['border']}; + border-radius: 12px; + margin-top: 16px; + padding: 20px; + font-size: 14px; + font-weight: bold; +}} + +QGroupBox::title {{ + subcontrol-origin: margin; + left: 20px; + padding: 0 10px; + color: {COLORS['text_secondary']}; +}} + +QFrame#separator {{ + background-color: {COLORS['border']}; + max-height: 1px; +}} + +QFrame#card {{ + background-color: {COLORS['bg_card']}; + border: 1px solid {COLORS['border']}; + border-radius: 12px; + padding: 20px; +}} +""" + + +class LicenseManager: + """Interface to saikyo-license CLI""" + + LICENSE_CMD = '/usr/sbin/saikyo-license' + + @classmethod + def run_cmd(cls, *args): + try: + result = subprocess.run( + [cls.LICENSE_CMD] + list(args), + capture_output=True, text=True, timeout=30 + ) + return result.returncode, result.stdout, result.stderr + except Exception as e: + return -1, '', str(e) + + @classmethod + def run_privileged(cls, *args): + try: + result = subprocess.run( + ['pkexec', cls.LICENSE_CMD] + list(args), + capture_output=True, text=True, timeout=60 + ) + return result.returncode, result.stdout, result.stderr + except Exception as e: + return -1, '', str(e) + + @classmethod + def get_status(cls): + code, out, err = cls.run_cmd('status') + status = { + 'status': 'unknown', + 'trial_days': '', + 'trial_remaining_seconds': '0', + 'valid_until': '', + 'expired': 'no', + 'org_name': '', + 'tier': '', + 'machine_id': '', + } + for line in out.split('\n'): + if '=' in line: + k, v = line.split('=', 1) + k = k.strip().replace('-', '_') + status[k] = v.strip() + return status + + @classmethod + def verify(cls): + code, _, _ = cls.run_cmd('verify') + return code == 0 + + @classmethod + def install_code(cls, code): + return cls.run_privileged('install-code', code) + + @classmethod + def install_files(cls, json_path, sig_path): + return cls.run_privileged('install', json_path, sig_path) + + @classmethod + def activate_online(cls, token, server='https://saikyo-os.ru/license'): + return cls.run_privileged('activate', '--token', token, '--server', server) + + @classmethod + def get_machine_id(cls): + try: + with open('/etc/machine-id', 'r') as f: + return f.read().strip() + except: + return 'Не удалось прочитать' + + +def human_duration(seconds): + try: + s = int(seconds) + except: + return '—' + if s <= 0: + return '0м' + d = s // 86400 + h = (s % 86400) // 3600 + m = (s % 3600) // 60 + if d > 0: + return f'{d}д {h}ч {m}м' + elif h > 0: + return f'{h}ч {m}м' + else: + return f'{m}м' + + +class StatusCard(QFrame): + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName('card') + self.setup_ui() + + def setup_ui(self): + layout = QVBoxLayout(self) + layout.setSpacing(16) + + # Status badge + self.status_label = QLabel('Загрузка...') + self.status_label.setObjectName('status_label') + self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter if PYQT_VERSION == 6 else Qt.AlignCenter) + layout.addWidget(self.status_label) + + # Info grid + info_layout = QVBoxLayout() + info_layout.setSpacing(8) + + self.org_label = self._create_info_row('Организация:', '—') + self.tier_label = self._create_info_row('Тариф:', '—') + self.remaining_label = self._create_info_row('Осталось:', '—') + self.valid_until_label = self._create_info_row('Действует до:', '—') + self.machine_id_label = self._create_info_row('Machine ID:', '—') + + info_layout.addLayout(self.org_label[0]) + info_layout.addLayout(self.tier_label[0]) + info_layout.addLayout(self.remaining_label[0]) + info_layout.addLayout(self.valid_until_label[0]) + info_layout.addLayout(self.machine_id_label[0]) + + layout.addLayout(info_layout) + + def _create_info_row(self, label_text, value_text): + layout = QHBoxLayout() + label = QLabel(label_text) + label.setStyleSheet(f'color: {COLORS["text_secondary"]}; font-size: 13px;') + value = QLabel(value_text) + value.setStyleSheet('font-size: 13px; font-weight: 500;') + value.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse if PYQT_VERSION == 6 else Qt.TextSelectableByMouse) + layout.addWidget(label) + layout.addStretch() + layout.addWidget(value) + return (layout, value) + + def update_status(self, status_data): + status = status_data.get('status', 'unknown') + + # Update status badge + if status == 'trial': + self.status_label.setText('⏱ ПРОБНЫЙ ПЕРИОД') + self.status_label.setObjectName('status_trial') + elif status == 'missing': + self.status_label.setText('⚠ НЕ АКТИВИРОВАНА') + self.status_label.setObjectName('status_missing') + elif status_data.get('expired') == 'yes': + self.status_label.setText('✗ ИСТЕКЛА') + self.status_label.setObjectName('status_expired') + elif LicenseManager.verify(): + self.status_label.setText('✓ АКТИВНА') + self.status_label.setObjectName('status_active') + else: + self.status_label.setText('? НЕИЗВЕСТНО') + self.status_label.setObjectName('status_missing') + + # Force style refresh + self.status_label.setStyleSheet(self.status_label.styleSheet()) + + # Update info + self.org_label[1].setText(status_data.get('org_name') or '—') + self.tier_label[1].setText(status_data.get('tier') or '—') + + # Calculate remaining time + if status == 'trial': + remaining = human_duration(status_data.get('trial_remaining_seconds', 0)) + elif status_data.get('valid_until'): + try: + exp = datetime.fromisoformat(status_data['valid_until'].replace('Z', '+00:00')) + now = datetime.now(exp.tzinfo) if exp.tzinfo else datetime.now() + delta = (exp - now).total_seconds() + remaining = human_duration(delta) if delta > 0 else 'Истекла' + except: + remaining = '—' + else: + remaining = '—' + + self.remaining_label[1].setText(remaining) + self.valid_until_label[1].setText(status_data.get('valid_until') or '—') + self.machine_id_label[1].setText(LicenseManager.get_machine_id()[:16] + '...') + + +class ActivationWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setup_ui() + + def setup_ui(self): + layout = QVBoxLayout(self) + layout.setSpacing(20) + + # Online activation + online_group = QGroupBox('Онлайн активация') + online_layout = QVBoxLayout(online_group) + + self.token_input = QLineEdit() + self.token_input.setPlaceholderText('Введите токен активации...') + online_layout.addWidget(self.token_input) + + self.activate_btn = QPushButton('Активировать') + self.activate_btn.setObjectName('primary') + self.activate_btn.clicked.connect(self.activate_online) + online_layout.addWidget(self.activate_btn) + + layout.addWidget(online_group) + + # Offline activation + offline_group = QGroupBox('Оффлайн активация (response-код)') + offline_layout = QVBoxLayout(offline_group) + + self.code_input = QTextEdit() + self.code_input.setPlaceholderText('Вставьте response-код (SAI-RC:... или SAI-RESP2:...)') + self.code_input.setMaximumHeight(100) + offline_layout.addWidget(self.code_input) + + self.offline_btn = QPushButton('Активировать оффлайн') + self.offline_btn.clicked.connect(self.activate_offline) + offline_layout.addWidget(self.offline_btn) + + layout.addWidget(offline_group) + + # File installation + file_group = QGroupBox('Установка из файлов') + file_layout = QVBoxLayout(file_group) + + file_hint = QLabel('Выберите license.json и license.sig') + file_hint.setStyleSheet(f'color: {COLORS["text_secondary"]}; font-size: 12px;') + file_layout.addWidget(file_hint) + + self.file_btn = QPushButton('Выбрать файлы...') + self.file_btn.clicked.connect(self.install_from_files) + file_layout.addWidget(self.file_btn) + + layout.addWidget(file_group) + + layout.addStretch() + + def activate_online(self): + token = self.token_input.text().strip() + if not token: + QMessageBox.warning(self, 'Ошибка', 'Введите токен активации') + return + + code, out, err = LicenseManager.activate_online(token) + if code == 0: + QMessageBox.information(self, 'Успех', 'Лицензия успешно активирована!') + self.token_input.clear() + self.parent().parent().refresh_status() + else: + QMessageBox.critical(self, 'Ошибка', f'Ошибка активации:\n{err or out}') + + def activate_offline(self): + code = self.code_input.toPlainText().strip() + if not code: + QMessageBox.warning(self, 'Ошибка', 'Вставьте response-код') + return + + ret, out, err = LicenseManager.install_code(code) + if ret == 0: + QMessageBox.information(self, 'Успех', 'Лицензия успешно установлена!') + self.code_input.clear() + self.parent().parent().refresh_status() + else: + QMessageBox.critical(self, 'Ошибка', f'Ошибка установки:\n{err or out}') + + def install_from_files(self): + json_path, _ = QFileDialog.getOpenFileName( + self, 'Выберите license.json', os.path.expanduser('~'), + 'JSON files (*.json);;All files (*)' + ) + if not json_path: + return + + sig_path, _ = QFileDialog.getOpenFileName( + self, 'Выберите license.sig', os.path.dirname(json_path), + 'Signature files (*.sig);;All files (*)' + ) + if not sig_path: + return + + ret, out, err = LicenseManager.install_files(json_path, sig_path) + if ret == 0: + QMessageBox.information(self, 'Успех', 'Лицензия успешно установлена!') + self.parent().parent().refresh_status() + else: + QMessageBox.critical(self, 'Ошибка', f'Ошибка установки:\n{err or out}') + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle('SAIKYO OS — Активация лицензии') + self.setMinimumSize(500, 650) + self.setMaximumSize(600, 800) + + # Set window icon + icon_path = '/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png' + if os.path.exists(icon_path): + self.setWindowIcon(QIcon(icon_path)) + + self.setup_ui() + self.refresh_status() + + def setup_ui(self): + central = QWidget() + self.setCentralWidget(central) + + layout = QVBoxLayout(central) + layout.setContentsMargins(30, 30, 30, 30) + layout.setSpacing(20) + + # Header + header_layout = QVBoxLayout() + header_layout.setSpacing(8) + + # Logo + logo_path = '/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png' + if os.path.exists(logo_path): + logo_label = QLabel() + pixmap = QPixmap(logo_path).scaled( + 80, 80, + Qt.AspectRatioMode.KeepAspectRatio if PYQT_VERSION == 6 else Qt.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation if PYQT_VERSION == 6 else Qt.SmoothTransformation + ) + logo_label.setPixmap(pixmap) + logo_label.setAlignment(Qt.AlignmentFlag.AlignCenter if PYQT_VERSION == 6 else Qt.AlignCenter) + header_layout.addWidget(logo_label) + + title = QLabel('SAIKYO OS') + title.setObjectName('title') + title.setAlignment(Qt.AlignmentFlag.AlignCenter if PYQT_VERSION == 6 else Qt.AlignCenter) + header_layout.addWidget(title) + + subtitle = QLabel('Активация лицензии') + subtitle.setObjectName('subtitle') + subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter if PYQT_VERSION == 6 else Qt.AlignCenter) + header_layout.addWidget(subtitle) + + layout.addLayout(header_layout) + + # Status card + self.status_card = StatusCard() + layout.addWidget(self.status_card) + + # Activation widget + self.activation_widget = ActivationWidget(self) + layout.addWidget(self.activation_widget) + + # Footer buttons + footer_layout = QHBoxLayout() + + refresh_btn = QPushButton('Обновить') + refresh_btn.clicked.connect(self.refresh_status) + footer_layout.addWidget(refresh_btn) + + portal_btn = QPushButton('Открыть портал') + portal_btn.clicked.connect(self.open_portal) + footer_layout.addWidget(portal_btn) + + close_btn = QPushButton('Закрыть') + close_btn.clicked.connect(self.close) + footer_layout.addWidget(close_btn) + + layout.addLayout(footer_layout) + + def refresh_status(self): + status = LicenseManager.get_status() + self.status_card.update_status(status) + + def open_portal(self): + try: + subprocess.Popen(['xdg-open', 'https://saikyo-os.ru/license'], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except: + pass + + +def main(): + app = QApplication(sys.argv) + app.setStyle('Fusion') + app.setStyleSheet(STYLESHEET) + + # Dark palette + palette = QPalette() + palette.setColor(QPalette.ColorRole.Window if PYQT_VERSION == 6 else QPalette.Window, QColor(COLORS['bg_dark'])) + palette.setColor(QPalette.ColorRole.WindowText if PYQT_VERSION == 6 else QPalette.WindowText, QColor(COLORS['text'])) + palette.setColor(QPalette.ColorRole.Base if PYQT_VERSION == 6 else QPalette.Base, QColor(COLORS['bg_input'])) + palette.setColor(QPalette.ColorRole.Text if PYQT_VERSION == 6 else QPalette.Text, QColor(COLORS['text'])) + palette.setColor(QPalette.ColorRole.Button if PYQT_VERSION == 6 else QPalette.Button, QColor(COLORS['bg_card'])) + palette.setColor(QPalette.ColorRole.ButtonText if PYQT_VERSION == 6 else QPalette.ButtonText, QColor(COLORS['text'])) + palette.setColor(QPalette.ColorRole.Highlight if PYQT_VERSION == 6 else QPalette.Highlight, QColor(COLORS['accent'])) + app.setPalette(palette) + + window = MainWindow() + window.show() + + sys.exit(app.exec() if PYQT_VERSION == 6 else app.exec_()) + + +if __name__ == '__main__': + main() diff --git a/saikyo-license/debian/saikyo-license/usr/bin/saikyo-license-kde b/saikyo-license/debian/saikyo-license/usr/bin/saikyo-license-kde new file mode 100755 index 0000000..6e983c6 --- /dev/null +++ b/saikyo-license/debian/saikyo-license/usr/bin/saikyo-license-kde @@ -0,0 +1,263 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Minimal KDE UI wrapper for Saikyo OS license install/status. +# Uses kdialog and pkexec. + +if ! command -v kdialog >/dev/null 2>&1; then + echo "ERROR: kdialog not found" >&2 + exit 1 +fi + +TITLE="SAIKYO OS — лицензия" +ICON="/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png" + +human_duration() { + local s="${1:-0}" + if [[ -z "${s}" || ! "${s}" =~ ^[0-9]+$ ]]; then + printf '%s' "—" + return 0 + fi + local d h m + d=$((s/86400)) + h=$(((s%86400)/3600)) + m=$(((s%3600)/60)) + if [[ "$d" -gt 0 ]]; then + printf '%sd %sh %sm' "$d" "$h" "$m" + elif [[ "$h" -gt 0 ]]; then + printf '%sh %sm' "$h" "$m" + else + printf '%sm' "$m" + fi +} + +parse_kv() { + # Reads key=value lines from stdin and exports them as shell vars. + local line k v + while IFS= read -r line; do + [[ -n "${line}" ]] || continue + case "${line}" in + *=*) + k=${line%%=*} + v=${line#*=} + k=${k//-/_} + case "${k}" in + status|trial_days|trial_remaining_seconds|valid_until|expired|org_id|org_name|tier|machine_id|signature) + printf -v "$k" '%s' "$v" + ;; + esac + ;; + esac + done +} + +get_status_fields() { + status=""; trial_days=""; trial_remaining_seconds=""; valid_until=""; expired=""; org_id=""; org_name=""; tier=""; machine_id=""; signature="" + local out + out=$(/usr/sbin/saikyo-license status 2>/dev/null || true) + parse_kv <<<"${out}" +} + +status_summary() { + get_status_fields + + local state="UNKNOWN" + local rem="—" + + if [[ "${status:-}" == "trial" ]]; then + state="TRIAL" + rem=$(human_duration "${trial_remaining_seconds:-0}") + elif [[ "${status:-}" == "missing" ]]; then + state="MISSING" + rem="—" + else + if [[ "${expired:-}" == "yes" ]]; then + state="EXPIRED" + else + # If we have a valid_until and not expired=yes, treat as active (even if signature says invalid we still show it). + if [[ -n "${valid_until:-}" ]]; then + state="ACTIVE" + else + state="UNKNOWN" + fi + fi + + if [[ -n "${valid_until:-}" ]]; then + local now exp_s + now=$(date -u +%s 2>/dev/null || echo "") + exp_s=$(date -u -d "${valid_until}" +%s 2>/dev/null || echo "") + if [[ -n "${now}" && -n "${exp_s}" && "${exp_s}" =~ ^[0-9]+$ && "${now}" =~ ^[0-9]+$ && "${exp_s}" -gt "${now}" ]]; then + rem=$(human_duration $((exp_s - now))) + else + rem="—" + fi + fi + fi + + local org="—" + [[ -n "${org_name:-}" ]] && org="${org_name}" + + local tier_txt="—" + [[ -n "${tier:-}" ]] && tier_txt="${tier}" + + local until_txt="—" + [[ -n "${valid_until:-}" ]] && until_txt="${valid_until}" + + local sig_txt="—" + [[ -n "${signature:-}" ]] && sig_txt="${signature}" + + printf '%s\n' \ + "Статус: ${state}" \ + "Осталось: ${rem}" \ + "Организация: ${org}" \ + "Тариф: ${tier_txt}" \ + "Действует до: ${until_txt}" \ + "Подпись: ${sig_txt}" +} + +has_valid_license() { + # saikyo-license verify returns 0 only for a valid, installed license + /usr/sbin/saikyo-license verify >/dev/null 2>&1 +} + +show_status() { + kdialog --title "$TITLE" --icon "$ICON" --msgbox "$(status_summary)" +} + +install_license() { + local json sig + + json=$(kdialog --title "$TITLE" --getopenfilename "$HOME" "license.json|license.json (*.json)" 2>/dev/null || true) + [[ -n "${json}" ]] || return 0 + + sig=$(kdialog --title "$TITLE" --getopenfilename "$HOME" "license.sig|license.sig (*.sig)" 2>/dev/null || true) + [[ -n "${sig}" ]] || return 0 + + # Run privileged install + local cmd + cmd=(pkexec /usr/sbin/saikyo-license install "$json" "$sig") + + local result + result=$(${cmd[@]} 2>&1) || { + kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка установки лицензии:\n\n${result}" + return 1 + } + + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия установлена.\n\n${result}" + show_status +} + +install_response_code() { + local code + code=$(kdialog --title "$TITLE" --icon "$ICON" --inputbox "SAIKYO OS оффлайн активация\n\nВставьте response-код (SAI-RC / SAI-RESP2 / SAI-RESP)" "" 2>/dev/null || true) + [[ -n "${code}" ]] || return 0 + + local result + result=$(pkexec /usr/sbin/saikyo-license install-code "${code}" 2>&1) || { + kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка оффлайн активации:\n\n${result}" + return 1 + } + + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия установлена.\n\n${result}" + show_status +} + +show_files_info() { + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Файлы лицензии:\n\n/etc/saikyo/license/license.json\n/etc/saikyo/license/license.sig\n/etc/saikyo/license/trial_start (если trial)\n\nКлючи проверки:\n/usr/share/saikyo-os/license/saikyo-license-pub.pem\n/usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem" 2>/dev/null || true +} + +show_diagnostics() { + local out + out=$( (/usr/sbin/saikyo-license status 2>&1 || true; echo; /usr/sbin/saikyo-license verify 2>&1 || true) | sed -n '1,200p' ) + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Диагностика (первые 200 строк):\n\n${out}" 2>/dev/null || true +} + +show_machine_id() { + local mid + mid=$(cat /etc/machine-id 2>/dev/null || true) + if [[ -z "${mid}" ]]; then + kdialog --title "$TITLE" --icon "$ICON" --error "Не удалось прочитать /etc/machine-id" + return 1 + fi + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Идентификатор машины (machine-id):\n\n${mid}" +} + +activate_online() { + local token + local server + server="https://saikyo-os.ru/license" + + token=$(kdialog --title "$TITLE" --icon "$ICON" --inputbox "SAIKYO OS активация\n\nВведите токен активации" "" 2>/dev/null || true) + [[ -n "${token}" ]] || return 0 + + local result + result=$(pkexec /usr/sbin/saikyo-license activate --token "${token}" --server "${server}" 2>&1) || { + kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка онлайн-активации:\n\n${result}" + return 1 + } + + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Онлайн-активация выполнена.\n\n${result}" + show_status +} + +open_portal() { + if command -v xdg-open >/dev/null 2>&1; then + xdg-open "https://saikyo-os.ru/license" >/dev/null 2>&1 || true + fi +} + +force_activation() { + # Non-closable UX (as much as possible): closing/canceling just re-opens. + # The only exit is a successful activation (valid license). + if has_valid_license; then + exit 0 + fi + + while true; do + if has_valid_license; then + kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия активирована. Спасибо." + exit 0 + fi + + kdialog --title "$TITLE" --icon "$ICON" --msgbox "SAIKYO OS активация\n\nДля продолжения работы необходимо активировать лицензию.\n\nНажмите OK, чтобы ввести токен." 2>/dev/null || true + + if activate_online; then + : + else + # Show error handled inside activate_online; loop. + : + fi + done +} + +if [[ "${1:-}" == "--force-activate" ]]; then + shift || true + force_activation +fi + +while true; do + header=$(status_summary) + action=$(kdialog --title "$TITLE" --icon "$ICON" --menu "${header}\n\nДействия:" \ + status "Показать статус" \ + activate "Онлайн-активация/продление (токен)" \ + offline "Оффлайн активация (response-код)" \ + install "Установить лицензию (license.json + license.sig)" \ + portal "Открыть портал" \ + files "Где лежат файлы/ключи" \ + diag "Диагностика" \ + machine-id "Показать machine-id" \ + exit "Выход" \ + 2>/dev/null || true) + + case "$action" in + status) show_status;; + activate) activate_online;; + offline) install_response_code;; + install) install_license;; + portal) open_portal;; + files) show_files_info;; + diag) show_diagnostics;; + machine-id) show_machine_id;; + exit|"") exit 0;; + esac +done diff --git a/saikyo-license/debian/saikyo-license/usr/sbin/saikyo-license b/saikyo-license/debian/saikyo-license/usr/sbin/saikyo-license new file mode 100755 index 0000000..bfa0df8 --- /dev/null +++ b/saikyo-license/debian/saikyo-license/usr/sbin/saikyo-license @@ -0,0 +1,445 @@ +#!/usr/bin/env bash +set -euo pipefail + +LICENSE_DIR="/etc/saikyo/license" +PUBKEY_FILE="/usr/share/saikyo-os/license/saikyo-license-pub.pem" +PUBKEY_ED25519_FILE="/usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem" +LICENSE_JSON="${LICENSE_DIR}/license.json" +LICENSE_SIG="${LICENSE_DIR}/license.sig" +TRIAL_START_FILE="${LICENSE_DIR}/trial_start" +TRIAL_DAYS=7 + +require_jq() { + if ! command -v jq >/dev/null 2>&1; then + echo "ERROR: jq is required" >&2 + exit 1 + fi +} + +usage() { + echo "Usage: saikyo-license [args]" >&2 + echo "Commands:" >&2 + echo " install " >&2 + echo " install-code " >&2 + echo " activate --token [--server ]" >&2 + echo " renew --token [--server ]" >&2 + echo " verify" >&2 + echo " status" >&2 + exit 2 +} + +require_root() { + if [[ "$(id -u)" -ne 0 ]]; then + echo "ERROR: must be run as root" >&2 + exit 1 + fi +} + +verify_pair() { + if [[ ! -f "${PUBKEY_FILE}" && ! -f "${PUBKEY_ED25519_FILE}" ]]; then + echo "ERROR: public keys not found in /usr/share/saikyo-os/license" >&2 + exit 1 + fi + if [[ ! -f "${LICENSE_JSON}" || ! -f "${LICENSE_SIG}" ]]; then + if [[ -f "${TRIAL_START_FILE}" ]]; then + local start now end + start=$(cat "${TRIAL_START_FILE}" 2>/dev/null | tr -d '\r\n' | head -c 32) + now=$(date -u +%s 2>/dev/null || echo "") + if [[ -n "${start}" && -n "${now}" && "${start}" =~ ^[0-9]+$ ]]; then + end=$(( start + TRIAL_DAYS*86400 )) + if [[ "${now}" -le "${end}" ]]; then + echo "VALID" + return 0 + fi + fi + fi + echo "INVALID" + return 1 + fi + + require_jq + + # Verify signature over raw JSON bytes. + # New licenses: Ed25519 (pkeyutl -verify -rawin). + # Legacy licenses: RSA (dgst -sha256 -verify). + if [[ -f "${PUBKEY_ED25519_FILE}" ]]; then + if openssl pkeyutl -verify -pubin -inkey "${PUBKEY_ED25519_FILE}" -rawin -in "${LICENSE_JSON}" -sigfile "${LICENSE_SIG}" >/dev/null 2>&1; then + : + else + if [[ -f "${PUBKEY_FILE}" ]]; then + if ! openssl dgst -sha256 -verify "${PUBKEY_FILE}" -signature "${LICENSE_SIG}" "${LICENSE_JSON}" >/dev/null 2>&1; then + echo "INVALID" + return 1 + fi + else + echo "INVALID" + return 1 + fi + fi + else + if ! openssl dgst -sha256 -verify "${PUBKEY_FILE}" -signature "${LICENSE_SIG}" "${LICENSE_JSON}" >/dev/null 2>&1; then + echo "INVALID" + return 1 + fi + fi + + local lic_machine_id lic_valid_until + lic_machine_id=$(jq -r '.machine_id // empty' "${LICENSE_JSON}" 2>/dev/null || true) + lic_valid_until=$(jq -r '.valid_until // empty' "${LICENSE_JSON}" 2>/dev/null || true) + + if [[ -z "${lic_machine_id}" || -z "${lic_valid_until}" ]]; then + echo "INVALID" + return 1 + fi + + local current_machine_id + current_machine_id="" + if [[ -f /etc/machine-id ]]; then + current_machine_id=$(cat /etc/machine-id 2>/dev/null || true) + fi + if [[ -z "${current_machine_id}" || "${lic_machine_id}" != "${current_machine_id}" ]]; then + echo "INVALID" + return 1 + fi + + local now exp + now=$(date -u +%s) + exp=$(date -u -d "${lic_valid_until}" +%s 2>/dev/null || echo "") + if [[ -z "${exp}" || "${now}" -gt "${exp}" ]]; then + echo "INVALID" + return 1 + fi + + echo "VALID" + return 0 +} + +cmd_install() { + require_root + local src_json="${1:-}" + local src_sig="${2:-}" + [[ -n "${src_json}" && -n "${src_sig}" ]] || usage + [[ -f "${src_json}" ]] || { echo "ERROR: file not found: ${src_json}" >&2; exit 1; } + [[ -f "${src_sig}" ]] || { echo "ERROR: file not found: ${src_sig}" >&2; exit 1; } + + require_jq + + local lic_machine_id + lic_machine_id=$(jq -r '.machine_id // empty' "${src_json}" 2>/dev/null || true) + if [[ -z "${lic_machine_id}" ]]; then + echo "ERROR: license.json missing required field: machine_id" >&2 + exit 1 + fi + + local current_machine_id + current_machine_id="" + if [[ -f /etc/machine-id ]]; then + current_machine_id=$(cat /etc/machine-id 2>/dev/null || true) + fi + if [[ -z "${current_machine_id}" ]]; then + echo "ERROR: cannot read /etc/machine-id" >&2 + exit 1 + fi + if [[ "${lic_machine_id}" != "${current_machine_id}" ]]; then + echo "ERROR: machine-id mismatch" >&2 + echo " expected=${lic_machine_id}" >&2 + echo " actual=${current_machine_id}" >&2 + exit 1 + fi + + mkdir -p "${LICENSE_DIR}" + chmod 0755 "${LICENSE_DIR}" + install -m 0644 "${src_json}" "${LICENSE_JSON}" + install -m 0644 "${src_sig}" "${LICENSE_SIG}" + + verify_pair >/dev/null + echo "OK" +} + +cmd_install_code() { + require_root + require_jq + + local rc="${1:-}" + [[ -n "${rc}" ]] || usage + rc=$(printf '%s' "$rc" | tr -d '\r\n\t ' | tr -d '-' | head -c 20000) + + local td lic_json lic_sig + td=$(mktemp -d) + lic_json="${td}/license.json" + lic_sig="${td}/license.sig" + + if printf '%s' "$rc" | grep -q '^SAI-RC:'; then + if ! command -v base32 >/dev/null 2>&1; then + echo "ERROR: base32 is required" >&2 + rm -rf "${td}" || true + exit 1 + fi + local body ppart spart ppad spad + body=${rc#SAI-RC:} + ppart=${body%%.*} + spart=${body#*.} + if [[ -z "${ppart}" || -z "${spart}" || "${ppart}" == "${body}" ]]; then + echo "ERROR: invalid SAI-RC format" >&2 + rm -rf "${td}" || true + exit 1 + fi + ppad=$(( (8 - (${#ppart} % 8)) % 8 )) + spad=$(( (8 - (${#spart} % 8)) % 8 )) + if [[ "$ppad" -gt 0 ]]; then ppart="${ppart}$(printf '%*s' "$ppad" '' | tr ' ' '=')"; fi + if [[ "$spad" -gt 0 ]]; then spart="${spart}$(printf '%*s' "$spad" '' | tr ' ' '=')"; fi + printf '%s' "$ppart" | base32 -d >"${lic_json}" 2>/dev/null || true + printf '%s' "$spart" | base32 -d >"${lic_sig}" 2>/dev/null || true + elif printf '%s' "$rc" | grep -q '^SAI-RESP2:'; then + local body jpart spart jb64 sb64 jpad spad + body=${rc#SAI-RESP2:} + jpart=${body%%.*} + spart=${body#*.} + if [[ -z "${jpart}" || -z "${spart}" || "${jpart}" == "${body}" ]]; then + echo "ERROR: invalid SAI-RESP2 format" >&2 + rm -rf "${td}" || true + exit 1 + fi + jb64=$(printf '%s' "$jpart" | tr '_-' '/+') + sb64=$(printf '%s' "$spart" | tr '_-' '/+') + jpad=$(( (4 - (${#jb64} % 4)) % 4 )) + spad=$(( (4 - (${#sb64} % 4)) % 4 )) + if [[ "$jpad" -eq 1 ]]; then jb64="${jb64}="; elif [[ "$jpad" -eq 2 ]]; then jb64="${jb64}=="; elif [[ "$jpad" -eq 3 ]]; then jb64="${jb64}==="; fi + if [[ "$spad" -eq 1 ]]; then sb64="${sb64}="; elif [[ "$spad" -eq 2 ]]; then sb64="${sb64}=="; elif [[ "$spad" -eq 3 ]]; then sb64="${sb64}==="; fi + printf '%s' "$jb64" | base64 -d >"${lic_json}" 2>/dev/null || true + printf '%s' "$sb64" | base64 -d >"${lic_sig}" 2>/dev/null || true + elif printf '%s' "$rc" | grep -q '^SAI-RESP:'; then + local b64u payload lic_json_b64 lic_sig_b64 pad + b64u=${rc#SAI-RESP:} + b64u=$(printf '%s' "$b64u" | tr '_-' '/+') + pad=$(( (4 - (${#b64u} % 4)) % 4 )) + if [[ "$pad" -eq 1 ]]; then b64u="${b64u}="; elif [[ "$pad" -eq 2 ]]; then b64u="${b64u}=="; elif [[ "$pad" -eq 3 ]]; then b64u="${b64u}==="; fi + payload=$(printf '%s' "$b64u" | base64 -d 2>/dev/null || true) + lic_json_b64=$(printf '%s' "$payload" | sed -n 's/.*"license_json_b64":"\([^"]*\)".*/\1/p' | head -c 20000) + lic_sig_b64=$(printf '%s' "$payload" | sed -n 's/.*"license_sig_b64":"\([^"]*\)".*/\1/p' | head -c 20000) + if [[ -z "${lic_json_b64}" || -z "${lic_sig_b64}" ]]; then + echo "ERROR: invalid SAI-RESP payload" >&2 + rm -rf "${td}" || true + exit 1 + fi + printf '%s' "$lic_json_b64" | base64 -d >"${lic_json}" 2>/dev/null || true + printf '%s' "$lic_sig_b64" | base64 -d >"${lic_sig}" 2>/dev/null || true + else + echo "ERROR: unsupported response-code format" >&2 + rm -rf "${td}" || true + exit 1 + fi + + if [[ ! -s "${lic_json}" || ! -s "${lic_sig}" ]]; then + echo "ERROR: failed to decode response-code" >&2 + rm -rf "${td}" || true + exit 1 + fi + + cmd_install "${lic_json}" "${lic_sig}" + rm -rf "${td}" || true +} + +cmd_verify() { + verify_pair +} + +cmd_activate() { + require_root + require_jq + + local server="https://saikyo-os.ru/license" + local token="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --server) + server="${2:-}"; shift 2 || true + ;; + --token) + token="${2:-}"; shift 2 || true + ;; + -h|--help) + usage + ;; + *) + echo "ERROR: unknown option: $1" >&2 + usage + ;; + esac + done + + if [[ -z "${token}" ]]; then + echo "ERROR: --token is required" >&2 + exit 1 + fi + if [[ -z "${server}" ]]; then + echo "ERROR: --server is empty" >&2 + exit 1 + fi + if ! command -v curl >/dev/null 2>&1; then + echo "ERROR: curl is required for online activation" >&2 + exit 1 + fi + if [[ ! -r /etc/ssl/certs/ca-certificates.crt ]]; then + echo "ERROR: CA bundle not found: /etc/ssl/certs/ca-certificates.crt" >&2 + exit 1 + fi + + local server_host + server_host=$(printf '%s' "${server}" | sed -n 's#^https\?://\([^/]*\).*#\1#p' | head -n 1) + if [[ -n "${server_host}" ]] && command -v getent >/dev/null 2>&1; then + if ! getent ahosts "${server_host}" >/dev/null 2>&1; then + echo "ERROR: cannot resolve server host: ${server_host}" >&2 + exit 1 + fi + fi + + local mid + mid=$(cat /etc/machine-id 2>/dev/null || true) + if [[ -z "${mid}" ]]; then + echo "ERROR: cannot read /etc/machine-id" >&2 + exit 1 + fi + + local req tmpdir resp_json lic_json_b64 lic_sig_b64 + req=$(jq -n --arg token "${token}" --arg machine_id "${mid}" --arg hostname "$(hostname 2>/dev/null || true)" '{token:$token, machine_id:$machine_id, hostname:$hostname}') + + tmpdir=$(mktemp -d) + trap 'rm -rf "${tmpdir}" 2>/dev/null || true' RETURN + + resp_json="${tmpdir}/response.json" + resp_err="${tmpdir}/curl.stderr" + req_file="${tmpdir}/request.json" + http_code="" + + printf '%s' "${req}" > "${req_file}" + + http_code=$(curl -sS \ + --connect-timeout 10 \ + --max-time 30 \ + --retry 3 \ + --retry-delay 1 \ + --retry-connrefused \ + --retry-all-errors \ + -H 'Content-Type: application/json' \ + -X POST \ + --data-binary "@${req_file}" \ + "${server%/}/v1/activate" \ + -o "${resp_json}" \ + -w '%{http_code}' \ + 2>"${resp_err}" || true) + + if [[ "${http_code}" != 2* ]]; then + echo "ERROR: activation request failed (http_code=${http_code:-unknown})" >&2 + if [[ -s "${resp_err}" ]]; then + sed -n '1,120p' "${resp_err}" >&2 || true + fi + if [[ -s "${resp_json}" ]]; then + sed -n '1,200p' "${resp_json}" >&2 || true + fi + exit 1 + fi + + lic_json_b64=$(jq -r '.license_json_b64 // empty' "${resp_json}" 2>/dev/null || true) + lic_sig_b64=$(jq -r '.license_sig_b64 // empty' "${resp_json}" 2>/dev/null || true) + + if [[ -z "${lic_json_b64}" || -z "${lic_sig_b64}" ]]; then + echo "ERROR: invalid response from license server" >&2 + cat "${resp_json}" >&2 || true + exit 1 + fi + + local lic_json_file lic_sig_file + lic_json_file="${tmpdir}/license.json" + lic_sig_file="${tmpdir}/license.sig" + + printf '%s' "${lic_json_b64}" | base64 -d > "${lic_json_file}" + printf '%s' "${lic_sig_b64}" | base64 -d > "${lic_sig_file}" + + cmd_install "${lic_json_file}" "${lic_sig_file}" +} + +cmd_renew() { + cmd_activate "$@" +} + +cmd_status() { + if [[ ! -f "${LICENSE_JSON}" ]]; then + if [[ -f "${TRIAL_START_FILE}" ]]; then + local start now end rem + start=$(cat "${TRIAL_START_FILE}" 2>/dev/null | tr -d '\r\n' | head -c 32) + now=$(date -u +%s 2>/dev/null || echo "") + if [[ -n "${start}" && -n "${now}" && "${start}" =~ ^[0-9]+$ ]]; then + end=$(( start + TRIAL_DAYS*86400 )) + if [[ "${now}" -le "${end}" ]]; then + rem=$(( end - now )) + echo "status=trial" + echo "trial_days=${TRIAL_DAYS}" + echo "trial_remaining_seconds=${rem}" + exit 0 + fi + fi + fi + echo "status=missing" + exit 0 + fi + + local sig_status + if sig_status=$(verify_pair 2>/dev/null); then + echo "signature=${sig_status,,}" + else + echo "signature=invalid" + fi + + require_jq + + local org_id org_name valid_until tier machine_id + org_id=$(jq -r '.org_id // empty' "${LICENSE_JSON}" 2>/dev/null || true) + org_name=$(jq -r '.org_name // empty' "${LICENSE_JSON}" 2>/dev/null || true) + valid_until=$(jq -r '.valid_until // empty' "${LICENSE_JSON}" 2>/dev/null || true) + tier=$(jq -r '.tier // empty' "${LICENSE_JSON}" 2>/dev/null || true) + machine_id=$(jq -r '.machine_id // empty' "${LICENSE_JSON}" 2>/dev/null || true) + + [[ -n "${org_id}" ]] && echo "org_id=${org_id}" + [[ -n "${org_name}" ]] && echo "org_name=${org_name}" + [[ -n "${tier}" ]] && echo "tier=${tier}" + [[ -n "${machine_id}" ]] && echo "machine_id=${machine_id}" + [[ -n "${valid_until}" ]] && echo "valid_until=${valid_until}" + + if [[ -n "${valid_until}" ]]; then + local now exp + now=$(date -u +%s) + exp=$(date -u -d "${valid_until}" +%s 2>/dev/null || echo "") + if [[ -n "${exp}" && "${now}" -gt "${exp}" ]]; then + echo "expired=yes" + else + echo "expired=no" + fi + fi +} + +main() { + local cmd="${1:-}" + shift || true + case "${cmd}" in + install) + cmd_install "$@" + ;; + install-code) + cmd_install_code "$@" + ;; + activate) + cmd_activate "$@" + ;; + renew) + cmd_renew "$@" + ;; + verify) cmd_verify;; + status) cmd_status;; + -h|--help|help|"") usage;; + *) usage;; + esac +} + +main "$@" diff --git a/saikyo-license/debian/saikyo-license/usr/share/applications/saikyo-license.desktop b/saikyo-license/debian/saikyo-license/usr/share/applications/saikyo-license.desktop new file mode 100644 index 0000000..69bcab9 --- /dev/null +++ b/saikyo-license/debian/saikyo-license/usr/share/applications/saikyo-license.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Type=Application +Name=SAIKYO OS Activation +Name[ru]=Активация SAIKYO OS +Comment=Activate and manage Saikyo OS license +Comment[ru]=Активация и управление лицензией SAIKYO OS +Exec=/usr/bin/saikyo-license-gui +Icon=saikyo-activation-logo +Terminal=false +Categories=Settings;System; +Keywords=license;activation;saikyo;лицензия;активация; diff --git a/saikyo-license/debian/saikyo-license/usr/share/doc/saikyo-license/changelog.gz b/saikyo-license/debian/saikyo-license/usr/share/doc/saikyo-license/changelog.gz new file mode 100644 index 0000000..4e7852b Binary files /dev/null and b/saikyo-license/debian/saikyo-license/usr/share/doc/saikyo-license/changelog.gz differ diff --git a/saikyo-license/debian/saikyo-license/usr/share/doc/saikyo-license/copyright b/saikyo-license/debian/saikyo-license/usr/share/doc/saikyo-license/copyright new file mode 100644 index 0000000..6d0fe07 --- /dev/null +++ b/saikyo-license/debian/saikyo-license/usr/share/doc/saikyo-license/copyright @@ -0,0 +1,11 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-license +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 ООО «САЙКО» +License: Proprietary + This package provides Saikyo OS subscription license tooling. + +License: Proprietary + All rights reserved. diff --git a/saikyo-license/debian/saikyo-license/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png b/saikyo-license/debian/saikyo-license/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png new file mode 100644 index 0000000..88b2c88 Binary files /dev/null and b/saikyo-license/debian/saikyo-license/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png differ diff --git a/saikyo-license/debian/saikyo-license/usr/share/metainfo/saikyo-license.metainfo.xml b/saikyo-license/debian/saikyo-license/usr/share/metainfo/saikyo-license.metainfo.xml new file mode 100644 index 0000000..cfdb227 --- /dev/null +++ b/saikyo-license/debian/saikyo-license/usr/share/metainfo/saikyo-license.metainfo.xml @@ -0,0 +1,22 @@ + + + saikyo-license.desktop + Saikyo OS License + Лицензия Saikyo OS + Saikyo OS offline subscription license tool + Установка и просмотр подписки Saikyo OS + CC0-1.0 + Proprietary + + SAIKYO OS + + https://saikyo-os.ru + mailto:support@saikyo-os.ru + saikyo-license.desktop + + saikyo-license-kde + + +

Offline license installation and signature verification for Saikyo OS subscriptions.

+
+
diff --git a/saikyo-license/debian/saikyo-license/usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem b/saikyo-license/debian/saikyo-license/usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem new file mode 100644 index 0000000..5685392 --- /dev/null +++ b/saikyo-license/debian/saikyo-license/usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAi3wHAzikI3M9tTA8BBeossfIGc8ar8ehHQLQUd6KHI4= +-----END PUBLIC KEY----- diff --git a/saikyo-license/debian/saikyo-license/usr/share/saikyo-os/license/saikyo-license-pub.pem b/saikyo-license/debian/saikyo-license/usr/share/saikyo-os/license/saikyo-license-pub.pem new file mode 100644 index 0000000..c9d243c --- /dev/null +++ b/saikyo-license/debian/saikyo-license/usr/share/saikyo-os/license/saikyo-license-pub.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAiGLdmslGJoZDnmEPX+7Y +R1ABbGHQabzg+srFrg1JcB6aSRbQeR/ClM5BPwmuxx0xGU3YveroBe1aUBqDRT+Z +lXZ9Dk+EQbND02YxFntChazI77CLWYC1ezKoNBSWq6abgnJDlabbkT4EUjT69VF4 +iyJ6OtW4nzRzBzUncnQyV4Q5Nz4mKNfUpzbm5dXQwXFubRSmjHndYHjHsutKrtGL +q/mGzTydbxZBc9JAZDAld2nu+lUwh0naLRxvZfOOAOG61oGFc3xHMiNbW7WdnQYz +0ft9PhdX1DYre+RQB5Ts+afjkIRpoBVVI1hnLGzsp3bKNKmFPCOqYP8yWMKeZ7j8 +GA4uLakPvoMVNYMA2Rh2dMrPsaQToVBmpL/x1z1Pj8ExfMMSLUQZYlmD1CW70pm3 +LSYex/vs+MImlPdBSbovEJA3EdeQmZsy2/jQS9IMbhEPNXuFcF/X/pOcAh/lDn3F +qSBX8tfPF/6wTtyVa3yDuyR1vRD++jNADJ7ZClQFU9OzUjBwFBOcp8WAZMTbI0NL +2PNwMqBVyX0p+dFnn8MyEdL7A4V/8SuVyzZ3xMBB4tPQCf2CZ/TjIIRsQY6qWKKe +TTIvosFxx8O7GgL5caT4ugZhA2LO+FCzB8V9EGlv+AtOODCMQg8WE85uVqJj/u0f +m5NqOQt8GL4V7DkI5WXMkdMCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/saikyo-os-keyring/debian/.debhelper/generated/saikyo-os-keyring/dh_installchangelogs.dch.trimmed b/saikyo-os-keyring/debian/.debhelper/generated/saikyo-os-keyring/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..1357485 --- /dev/null +++ b/saikyo-os-keyring/debian/.debhelper/generated/saikyo-os-keyring/dh_installchangelogs.dch.trimmed @@ -0,0 +1,19 @@ +saikyo-os-keyring (1.0.2) stable; urgency=medium + + * Rotate repository signing key to EED1AFEDACEFE0819959C6D7BBB8543DB07DA6AD. + * Ship key as ASCII and dearmor to /usr/share/keyrings/saikyo-archive-keyring.gpg. + + -- SAIKYO OS Sun, 11 Jan 2026 09:30:00 +0000 + +saikyo-os-keyring (1.0.1) stable; urgency=medium + + * Update repository keyring to include signing subkey 61AC179B793CEB2E25390B3E5592DB887549B695. + * Fix installation path so saikyo-os.list is installed as a file. + + -- SAIKYO OS Sun, 11 Jan 2026 04:30:00 +0000 + +saikyo-os-keyring (1.0.0) stable; urgency=medium + + * Initial release. + + -- SAIKYO OS Tue, 06 Jan 2026 00:00:00 +0000 diff --git a/saikyo-os-keyring/debian/.debhelper/generated/saikyo-os-keyring/installed-by-dh_install b/saikyo-os-keyring/debian/.debhelper/generated/saikyo-os-keyring/installed-by-dh_install new file mode 100644 index 0000000..40202bd --- /dev/null +++ b/saikyo-os-keyring/debian/.debhelper/generated/saikyo-os-keyring/installed-by-dh_install @@ -0,0 +1,2 @@ +./keyrings/saikyo-archive-keyring.asc +./sources/saikyo-os.list diff --git a/saikyo-os-keyring/debian/.debhelper/generated/saikyo-os-keyring/installed-by-dh_installdocs b/saikyo-os-keyring/debian/.debhelper/generated/saikyo-os-keyring/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-os-keyring/debian/changelog b/saikyo-os-keyring/debian/changelog new file mode 100644 index 0000000..1357485 --- /dev/null +++ b/saikyo-os-keyring/debian/changelog @@ -0,0 +1,19 @@ +saikyo-os-keyring (1.0.2) stable; urgency=medium + + * Rotate repository signing key to EED1AFEDACEFE0819959C6D7BBB8543DB07DA6AD. + * Ship key as ASCII and dearmor to /usr/share/keyrings/saikyo-archive-keyring.gpg. + + -- SAIKYO OS Sun, 11 Jan 2026 09:30:00 +0000 + +saikyo-os-keyring (1.0.1) stable; urgency=medium + + * Update repository keyring to include signing subkey 61AC179B793CEB2E25390B3E5592DB887549B695. + * Fix installation path so saikyo-os.list is installed as a file. + + -- SAIKYO OS Sun, 11 Jan 2026 04:30:00 +0000 + +saikyo-os-keyring (1.0.0) stable; urgency=medium + + * Initial release. + + -- SAIKYO OS Tue, 06 Jan 2026 00:00:00 +0000 diff --git a/saikyo-os-keyring/debian/control b/saikyo-os-keyring/debian/control new file mode 100644 index 0000000..eac3dca --- /dev/null +++ b/saikyo-os-keyring/debian/control @@ -0,0 +1,13 @@ +Source: saikyo-os-keyring +Section: misc +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-os-keyring +Architecture: all +Depends: ${misc:Depends}, gnupg +Description: SAIKYO OS APT repository keyring and sources + Installs the Saikyo OS APT keyring and apt sources list for repo.saikyo-os.ru. diff --git a/saikyo-os-keyring/debian/copyright b/saikyo-os-keyring/debian/copyright new file mode 100644 index 0000000..bdb62dc --- /dev/null +++ b/saikyo-os-keyring/debian/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-os-keyring +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-os-keyring/debian/debhelper-build-stamp b/saikyo-os-keyring/debian/debhelper-build-stamp new file mode 100644 index 0000000..b0ec03a --- /dev/null +++ b/saikyo-os-keyring/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-os-keyring diff --git a/saikyo-os-keyring/debian/files b/saikyo-os-keyring/debian/files new file mode 100644 index 0000000..ce29ee0 --- /dev/null +++ b/saikyo-os-keyring/debian/files @@ -0,0 +1,2 @@ +saikyo-os-keyring_1.0.2_all.deb misc optional +saikyo-os-keyring_1.0.2_amd64.buildinfo misc optional diff --git a/saikyo-os-keyring/debian/install b/saikyo-os-keyring/debian/install new file mode 100644 index 0000000..39c1842 --- /dev/null +++ b/saikyo-os-keyring/debian/install @@ -0,0 +1,2 @@ +keyrings/saikyo-archive-keyring.asc usr/share/keyrings/ +sources/saikyo-os.list etc/apt/sources.list.d/ diff --git a/saikyo-os-keyring/debian/rules b/saikyo-os-keyring/debian/rules new file mode 100755 index 0000000..5bd7184 --- /dev/null +++ b/saikyo-os-keyring/debian/rules @@ -0,0 +1,15 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: + +override_dh_install: + if [ ! -f keyrings/saikyo-archive-keyring.asc ]; then \ + echo "ERROR: Missing keyrings/saikyo-archive-keyring.asc" >&2; \ + exit 1; \ + fi + dh_install diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring.debhelper.log b/saikyo-os-keyring/debian/saikyo-os-keyring.debhelper.log new file mode 100644 index 0000000..b3d75ae --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring.debhelper.log @@ -0,0 +1 @@ +dh_install diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring.postinst b/saikyo-os-keyring/debian/saikyo-os-keyring.postinst new file mode 100755 index 0000000..72df717 --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring.postinst @@ -0,0 +1,22 @@ +#!/bin/sh +set -e + +case "$1" in + configure|abort-upgrade|abort-remove|abort-deconfigure) + # Backward compatibility: older broken packages installed the ASCII key as + # a file inside a directory (/usr/share/keyrings/saikyo-archive-keyring.asc/...). + if [ ! -f /usr/share/keyrings/saikyo-archive-keyring.asc ] && [ -f /usr/share/keyrings/saikyo-archive-keyring.asc/saikyo-archive-keyring.asc ]; then + mv -f /usr/share/keyrings/saikyo-archive-keyring.asc/saikyo-archive-keyring.asc /usr/share/keyrings/saikyo-archive-keyring.asc 2>/dev/null || true + rmdir /usr/share/keyrings/saikyo-archive-keyring.asc 2>/dev/null || true + chmod 0644 /usr/share/keyrings/saikyo-archive-keyring.asc 2>/dev/null || true + fi + + # Convert shipped ASCII key to binary keyring used by apt Signed-By. + if command -v gpg >/dev/null 2>&1 && [ -f /usr/share/keyrings/saikyo-archive-keyring.asc ]; then + gpg --dearmor --yes -o /usr/share/keyrings/saikyo-archive-keyring.gpg /usr/share/keyrings/saikyo-archive-keyring.asc || true + chmod 0644 /usr/share/keyrings/saikyo-archive-keyring.gpg || true + fi + ;; +esac + +exit 0 diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring.substvars b/saikyo-os-keyring/debian/saikyo-os-keyring.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/conffiles b/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/conffiles new file mode 100644 index 0000000..b74c13b --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/conffiles @@ -0,0 +1 @@ +/etc/apt/sources.list.d/saikyo-os.list diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/control b/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/control new file mode 100644 index 0000000..8325815 --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/control @@ -0,0 +1,10 @@ +Package: saikyo-os-keyring +Version: 1.0.2 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 18 +Depends: gnupg +Section: misc +Priority: optional +Description: SAIKYO OS APT repository keyring and sources + Installs the Saikyo OS APT keyring and apt sources list for repo.saikyo-os.ru. diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/md5sums b/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/md5sums new file mode 100644 index 0000000..eec8937 --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/md5sums @@ -0,0 +1,3 @@ +abe9c48798db2476c4163fd941ca9ae0 usr/share/doc/saikyo-os-keyring/changelog.gz +cb60223463e9f19cd3db5575067eb185 usr/share/doc/saikyo-os-keyring/copyright +44f9d8f4ab4e3e3c987dc4f6cd1da34f usr/share/keyrings/saikyo-archive-keyring.asc diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/postinst b/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/postinst new file mode 100755 index 0000000..72df717 --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring/DEBIAN/postinst @@ -0,0 +1,22 @@ +#!/bin/sh +set -e + +case "$1" in + configure|abort-upgrade|abort-remove|abort-deconfigure) + # Backward compatibility: older broken packages installed the ASCII key as + # a file inside a directory (/usr/share/keyrings/saikyo-archive-keyring.asc/...). + if [ ! -f /usr/share/keyrings/saikyo-archive-keyring.asc ] && [ -f /usr/share/keyrings/saikyo-archive-keyring.asc/saikyo-archive-keyring.asc ]; then + mv -f /usr/share/keyrings/saikyo-archive-keyring.asc/saikyo-archive-keyring.asc /usr/share/keyrings/saikyo-archive-keyring.asc 2>/dev/null || true + rmdir /usr/share/keyrings/saikyo-archive-keyring.asc 2>/dev/null || true + chmod 0644 /usr/share/keyrings/saikyo-archive-keyring.asc 2>/dev/null || true + fi + + # Convert shipped ASCII key to binary keyring used by apt Signed-By. + if command -v gpg >/dev/null 2>&1 && [ -f /usr/share/keyrings/saikyo-archive-keyring.asc ]; then + gpg --dearmor --yes -o /usr/share/keyrings/saikyo-archive-keyring.gpg /usr/share/keyrings/saikyo-archive-keyring.asc || true + chmod 0644 /usr/share/keyrings/saikyo-archive-keyring.gpg || true + fi + ;; +esac + +exit 0 diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring/etc/apt/sources.list.d/saikyo-os.list b/saikyo-os-keyring/debian/saikyo-os-keyring/etc/apt/sources.list.d/saikyo-os.list new file mode 100644 index 0000000..781474e --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring/etc/apt/sources.list.d/saikyo-os.list @@ -0,0 +1,2 @@ +deb [signed-by=/usr/share/keyrings/saikyo-archive-keyring.gpg] https://repo.saikyo-os.ru saikyo-1.0-trixie main +deb [signed-by=/usr/share/keyrings/saikyo-archive-keyring.gpg] https://repo.saikyo-os.ru saikyo-1.0-trixie-debian main diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/doc/saikyo-os-keyring/changelog.gz b/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/doc/saikyo-os-keyring/changelog.gz new file mode 100644 index 0000000..796c845 Binary files /dev/null and b/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/doc/saikyo-os-keyring/changelog.gz differ diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/doc/saikyo-os-keyring/copyright b/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/doc/saikyo-os-keyring/copyright new file mode 100644 index 0000000..bdb62dc --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/doc/saikyo-os-keyring/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-os-keyring +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/keyrings/saikyo-archive-keyring.asc b/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/keyrings/saikyo-archive-keyring.asc new file mode 100644 index 0000000..c132734 --- /dev/null +++ b/saikyo-os-keyring/debian/saikyo-os-keyring/usr/share/keyrings/saikyo-archive-keyring.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEaV8W/RYJKwYBBAHaRw8BAQdAcrbrhkn388hmbT8EIKs+kMLHY/VwVc1OtSEX +RH2QdkG0OVNhaWt5byBPUyBSZXBvIChzYWlreW8tMS4wLXRyaXhpZSkgPHN1cHBv +cnRAc2Fpa3lvLW9zLnJ1PoiWBBMWCgA+FiEEHbjudyB552BiDa+b5siIOcJOnVsF +AmlfFv0CGwEFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ5siIOcJO +nVt+7QEAv6jlsM0UaKODMctBFwPDQXPXKO29sWJwb1LhLzvrBG0BALkgYAU4oHVR +S3v4t8vKxhFLqxa2jCWwnExCATGJciABuDMEaV8W/RYJKwYBBAHaRw8BAQdA0uSi +1Q6qb8YBAsjqeQ5lU3/sKQBJXhr5bSR81WSOqoWI9QQYFgoAJhYhBB247ncgeedg +Yg2vm+bIiDnCTp1bBQJpXxb9AhsCBQkB4TOAAIEJEObIiDnCTp1bdiAEGRYKAB0W +IQRhrBebeTzrLiU5Cz5VktuIdUm2lQUCaV8W/QAKCRBVktuIdUm2lY5vAP9Sx8Q8 +iKvzFqQqdtMp1PVpMuhaauFzUYPsMR2ETDJK9wEAnqqCcwFXiInvYxbcxbISH5q+ +J/LXjMi9/SPh5GBIxwm0XwD+NKKhxyO15K7Dqej099hyDPAInCDlQaCHqyaj5nRb +qScA/jxU6MJnQeV3OtyC2G/Lt8ulncUIKWXobiAMcGnI4isE +=Xxex +-----END PGP PUBLIC KEY BLOCK----- diff --git a/saikyo-os-keyring/keyrings/README.txt b/saikyo-os-keyring/keyrings/README.txt new file mode 100644 index 0000000..1e3d35f --- /dev/null +++ b/saikyo-os-keyring/keyrings/README.txt @@ -0,0 +1,5 @@ +Place saikyo-archive-keyring.gpg here before building the package. +Expected path: + keyrings/saikyo-archive-keyring.gpg + +This file must contain the public keys used to sign repo.saikyo-os.ru (suites: saikyo, saikyo-debian). diff --git a/saikyo-os-keyring/keyrings/saikyo-archive-keyring.asc b/saikyo-os-keyring/keyrings/saikyo-archive-keyring.asc new file mode 100644 index 0000000..c132734 --- /dev/null +++ b/saikyo-os-keyring/keyrings/saikyo-archive-keyring.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEaV8W/RYJKwYBBAHaRw8BAQdAcrbrhkn388hmbT8EIKs+kMLHY/VwVc1OtSEX +RH2QdkG0OVNhaWt5byBPUyBSZXBvIChzYWlreW8tMS4wLXRyaXhpZSkgPHN1cHBv +cnRAc2Fpa3lvLW9zLnJ1PoiWBBMWCgA+FiEEHbjudyB552BiDa+b5siIOcJOnVsF +AmlfFv0CGwEFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ5siIOcJO +nVt+7QEAv6jlsM0UaKODMctBFwPDQXPXKO29sWJwb1LhLzvrBG0BALkgYAU4oHVR +S3v4t8vKxhFLqxa2jCWwnExCATGJciABuDMEaV8W/RYJKwYBBAHaRw8BAQdA0uSi +1Q6qb8YBAsjqeQ5lU3/sKQBJXhr5bSR81WSOqoWI9QQYFgoAJhYhBB247ncgeedg +Yg2vm+bIiDnCTp1bBQJpXxb9AhsCBQkB4TOAAIEJEObIiDnCTp1bdiAEGRYKAB0W +IQRhrBebeTzrLiU5Cz5VktuIdUm2lQUCaV8W/QAKCRBVktuIdUm2lY5vAP9Sx8Q8 +iKvzFqQqdtMp1PVpMuhaauFzUYPsMR2ETDJK9wEAnqqCcwFXiInvYxbcxbISH5q+ +J/LXjMi9/SPh5GBIxwm0XwD+NKKhxyO15K7Dqej099hyDPAInCDlQaCHqyaj5nRb +qScA/jxU6MJnQeV3OtyC2G/Lt8ulncUIKWXobiAMcGnI4isE +=Xxex +-----END PGP PUBLIC KEY BLOCK----- diff --git a/saikyo-os-keyring/sources/saikyo-os.list b/saikyo-os-keyring/sources/saikyo-os.list new file mode 100644 index 0000000..781474e --- /dev/null +++ b/saikyo-os-keyring/sources/saikyo-os.list @@ -0,0 +1,2 @@ +deb [signed-by=/usr/share/keyrings/saikyo-archive-keyring.gpg] https://repo.saikyo-os.ru saikyo-1.0-trixie main +deb [signed-by=/usr/share/keyrings/saikyo-archive-keyring.gpg] https://repo.saikyo-os.ru saikyo-1.0-trixie-debian main diff --git a/saikyo-os-meta/DEBIAN/postinst b/saikyo-os-meta/DEBIAN/postinst new file mode 100644 index 0000000..c39932a --- /dev/null +++ b/saikyo-os-meta/DEBIAN/postinst @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +# Saikyo OS meta package postinst: ensure audit-report, forensics collector, and rollback utilities are available + +# Ensure forensics collector is available system-wide +if [ -f /usr/share/saikyo-os/forensics/collect-artifacts.sh ]; then + ln -sf /usr/share/saikyo-os/forensics/collect-artifacts.sh /usr/local/sbin/saikyo-forensics + chmod +x /usr/local/sbin/saikyo-forensics +fi + +# Ensure audit-report is in PATH +if [ -f /usr/share/saikyo-os/bin/saikyo-audit-report ]; then + ln -sf /usr/share/saikyo-os/bin/saikyo-audit-report /usr/local/bin/saikyo-audit-report +fi + +# Ensure rollback utility is in PATH +if [ -f /usr/local/sbin/saikyo-rollback ]; then + chmod +x /usr/local/sbin/saikyo-rollback +fi + +# Enable systemd services for log forwarding (if configured) +if [ -f /etc/systemd/system/saikyo-log-forward.service ]; then + systemctl daemon-reload + systemctl enable saikyo-log-forward.service || true +fi + +echo "Saikyo OS meta package postinst completed." diff --git a/saikyo-os-meta/debian/.debhelper/generated/saikyo-os-meta/dh_installchangelogs.dch.trimmed b/saikyo-os-meta/debian/.debhelper/generated/saikyo-os-meta/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..6c40811 --- /dev/null +++ b/saikyo-os-meta/debian/.debhelper/generated/saikyo-os-meta/dh_installchangelogs.dch.trimmed @@ -0,0 +1,5 @@ +saikyo-os-meta (1.0.0) stable; urgency=medium + + * Initial release. + + -- SAIKYO OS Tue, 06 Jan 2026 00:00:00 +0000 diff --git a/saikyo-os-meta/debian/.debhelper/generated/saikyo-os-meta/installed-by-dh_installdocs b/saikyo-os-meta/debian/.debhelper/generated/saikyo-os-meta/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-os-meta/debian/changelog b/saikyo-os-meta/debian/changelog new file mode 100644 index 0000000..6c40811 --- /dev/null +++ b/saikyo-os-meta/debian/changelog @@ -0,0 +1,5 @@ +saikyo-os-meta (1.0.0) stable; urgency=medium + + * Initial release. + + -- SAIKYO OS Tue, 06 Jan 2026 00:00:00 +0000 diff --git a/saikyo-os-meta/debian/control b/saikyo-os-meta/debian/control new file mode 100644 index 0000000..60d08b7 --- /dev/null +++ b/saikyo-os-meta/debian/control @@ -0,0 +1,14 @@ +Source: saikyo-os-meta +Section: metapackages +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-os-meta +Architecture: all +Depends: ${misc:Depends}, saikyo-os-keyring, saikyo-os-release, saikyo-branding, saikyo-grub-theme, saikyo-license, saikyo-audit-report +Recommends: grub-customizer, bauh, saikyo-plymouth-installer +Description: SAIKYO OS meta package + Convenience meta-package for base SAIKYO OS integration. diff --git a/saikyo-os-meta/debian/copyright b/saikyo-os-meta/debian/copyright new file mode 100644 index 0000000..bd81fa2 --- /dev/null +++ b/saikyo-os-meta/debian/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-os-meta +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-os-meta/debian/debhelper-build-stamp b/saikyo-os-meta/debian/debhelper-build-stamp new file mode 100644 index 0000000..623c108 --- /dev/null +++ b/saikyo-os-meta/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-os-meta diff --git a/saikyo-os-meta/debian/files b/saikyo-os-meta/debian/files new file mode 100644 index 0000000..d758f94 --- /dev/null +++ b/saikyo-os-meta/debian/files @@ -0,0 +1,2 @@ +saikyo-os-meta_1.0.0_all.deb metapackages optional +saikyo-os-meta_1.0.0_amd64.buildinfo metapackages optional diff --git a/saikyo-os-meta/debian/rules b/saikyo-os-meta/debian/rules new file mode 100755 index 0000000..0fc3651 --- /dev/null +++ b/saikyo-os-meta/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: diff --git a/saikyo-os-meta/debian/saikyo-os-meta.substvars b/saikyo-os-meta/debian/saikyo-os-meta.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-os-meta/debian/saikyo-os-meta.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-os-meta/debian/saikyo-os-meta/DEBIAN/control b/saikyo-os-meta/debian/saikyo-os-meta/DEBIAN/control new file mode 100644 index 0000000..e2fd38a --- /dev/null +++ b/saikyo-os-meta/debian/saikyo-os-meta/DEBIAN/control @@ -0,0 +1,11 @@ +Package: saikyo-os-meta +Version: 1.0.0 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 9 +Depends: saikyo-os-keyring, saikyo-os-release, saikyo-branding, saikyo-grub-theme, saikyo-license, saikyo-audit-report +Recommends: grub-customizer, bauh, saikyo-plymouth-installer +Section: metapackages +Priority: optional +Description: SAIKYO OS meta package + Convenience meta-package for base SAIKYO OS integration. diff --git a/saikyo-os-meta/debian/saikyo-os-meta/DEBIAN/md5sums b/saikyo-os-meta/debian/saikyo-os-meta/DEBIAN/md5sums new file mode 100644 index 0000000..5d29d65 --- /dev/null +++ b/saikyo-os-meta/debian/saikyo-os-meta/DEBIAN/md5sums @@ -0,0 +1,2 @@ +fc33ca0962a238910345b8c50185ef37 usr/share/doc/saikyo-os-meta/changelog.gz +5c215ac8c11b1efe29d28e52cbad6d88 usr/share/doc/saikyo-os-meta/copyright diff --git a/saikyo-os-meta/debian/saikyo-os-meta/usr/share/doc/saikyo-os-meta/changelog.gz b/saikyo-os-meta/debian/saikyo-os-meta/usr/share/doc/saikyo-os-meta/changelog.gz new file mode 100644 index 0000000..747c11b Binary files /dev/null and b/saikyo-os-meta/debian/saikyo-os-meta/usr/share/doc/saikyo-os-meta/changelog.gz differ diff --git a/saikyo-os-meta/debian/saikyo-os-meta/usr/share/doc/saikyo-os-meta/copyright b/saikyo-os-meta/debian/saikyo-os-meta/usr/share/doc/saikyo-os-meta/copyright new file mode 100644 index 0000000..bd81fa2 --- /dev/null +++ b/saikyo-os-meta/debian/saikyo-os-meta/usr/share/doc/saikyo-os-meta/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-os-meta +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-os-meta/debian/source/format b/saikyo-os-meta/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/saikyo-os-meta/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/saikyo-os-release/debian/.debhelper/generated/saikyo-os-release/dh_installchangelogs.dch.trimmed b/saikyo-os-release/debian/.debhelper/generated/saikyo-os-release/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..cf47636 --- /dev/null +++ b/saikyo-os-release/debian/.debhelper/generated/saikyo-os-release/dh_installchangelogs.dch.trimmed @@ -0,0 +1,5 @@ +saikyo-os-release (1.0.1) stable; urgency=medium + + * Add LOGO field for desktop "About System". + + -- SAIKYO OS Sun, 11 Jan 2026 07:45:00 +0000 diff --git a/saikyo-os-release/debian/.debhelper/generated/saikyo-os-release/installed-by-dh_install b/saikyo-os-release/debian/.debhelper/generated/saikyo-os-release/installed-by-dh_install new file mode 100644 index 0000000..15402ec --- /dev/null +++ b/saikyo-os-release/debian/.debhelper/generated/saikyo-os-release/installed-by-dh_install @@ -0,0 +1,3 @@ +./files/os-release +./files/lsb-release +./files/issue diff --git a/saikyo-os-release/debian/.debhelper/generated/saikyo-os-release/installed-by-dh_installdocs b/saikyo-os-release/debian/.debhelper/generated/saikyo-os-release/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-os-release/debian/changelog b/saikyo-os-release/debian/changelog new file mode 100644 index 0000000..cf47636 --- /dev/null +++ b/saikyo-os-release/debian/changelog @@ -0,0 +1,5 @@ +saikyo-os-release (1.0.1) stable; urgency=medium + + * Add LOGO field for desktop "About System". + + -- SAIKYO OS Sun, 11 Jan 2026 07:45:00 +0000 diff --git a/saikyo-os-release/debian/control b/saikyo-os-release/debian/control new file mode 100644 index 0000000..1e24e26 --- /dev/null +++ b/saikyo-os-release/debian/control @@ -0,0 +1,13 @@ +Source: saikyo-os-release +Section: misc +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-os-release +Architecture: all +Depends: ${misc:Depends} +Description: SAIKYO OS release files + Installs /etc/os-release, /usr/lib/os-release, /etc/lsb-release. diff --git a/saikyo-os-release/debian/copyright b/saikyo-os-release/debian/copyright new file mode 100644 index 0000000..33fd0a8 --- /dev/null +++ b/saikyo-os-release/debian/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-os-release +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-os-release/debian/debhelper-build-stamp b/saikyo-os-release/debian/debhelper-build-stamp new file mode 100644 index 0000000..5f13180 --- /dev/null +++ b/saikyo-os-release/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-os-release diff --git a/saikyo-os-release/debian/files b/saikyo-os-release/debian/files new file mode 100644 index 0000000..ebf1ec6 --- /dev/null +++ b/saikyo-os-release/debian/files @@ -0,0 +1,2 @@ +saikyo-os-release_1.0.1_all.deb misc optional +saikyo-os-release_1.0.1_amd64.buildinfo misc optional diff --git a/saikyo-os-release/debian/install b/saikyo-os-release/debian/install new file mode 100644 index 0000000..becbcb8 --- /dev/null +++ b/saikyo-os-release/debian/install @@ -0,0 +1,3 @@ +files/os-release usr/share/saikyo-os-release/os-release +files/lsb-release usr/share/saikyo-os-release/lsb-release +files/issue usr/share/saikyo-os-release/issue diff --git a/saikyo-os-release/debian/rules b/saikyo-os-release/debian/rules new file mode 100755 index 0000000..0fc3651 --- /dev/null +++ b/saikyo-os-release/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: diff --git a/saikyo-os-release/debian/saikyo-os-release.postinst b/saikyo-os-release/debian/saikyo-os-release.postinst new file mode 100644 index 0000000..5274b65 --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release.postinst @@ -0,0 +1,27 @@ +#!/usr/bin/env sh +set -e + +case "$1" in + configure|abort-upgrade|abort-remove|abort-deconfigure) + tmpl_dir="/usr/share/saikyo-os-release" + + install_one() { + src="$1" + dst="$2" + if [ -r "$src" ]; then + if [ -e "$dst" ] && ! cmp -s "$src" "$dst"; then + cp -f "$dst" "${dst}.saikyo-bak" 2>/dev/null || true + fi + mkdir -p "$(dirname "$dst")" 2>/dev/null || true + install -m 0644 "$src" "$dst" + fi + } + + install_one "${tmpl_dir}/os-release/os-release" "/etc/os-release" + install_one "${tmpl_dir}/os-release/os-release" "/usr/lib/os-release" + install_one "${tmpl_dir}/lsb-release/lsb-release" "/etc/lsb-release" + install_one "${tmpl_dir}/issue/issue" "/etc/issue" + ;; +esac + +exit 0 diff --git a/saikyo-os-release/debian/saikyo-os-release.substvars b/saikyo-os-release/debian/saikyo-os-release.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-os-release/debian/saikyo-os-release/DEBIAN/control b/saikyo-os-release/debian/saikyo-os-release/DEBIAN/control new file mode 100644 index 0000000..e1363da --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release/DEBIAN/control @@ -0,0 +1,9 @@ +Package: saikyo-os-release +Version: 1.0.1 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 17 +Section: misc +Priority: optional +Description: SAIKYO OS release files + Installs /etc/os-release, /usr/lib/os-release, /etc/lsb-release. diff --git a/saikyo-os-release/debian/saikyo-os-release/DEBIAN/md5sums b/saikyo-os-release/debian/saikyo-os-release/DEBIAN/md5sums new file mode 100644 index 0000000..019813d --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release/DEBIAN/md5sums @@ -0,0 +1,5 @@ +ffe33a1d18626d1d0cb8530ad4e51628 usr/share/doc/saikyo-os-release/changelog.gz +82273c27d2534188cb5e9b17d7d2810a usr/share/doc/saikyo-os-release/copyright +b82865016c4fe1925513a3254648df6b usr/share/saikyo-os-release/issue/issue +804140a1328e0ca2bff07ca1df33eaf1 usr/share/saikyo-os-release/lsb-release/lsb-release +8fe465c0cf34500115dae0f1eac24731 usr/share/saikyo-os-release/os-release/os-release diff --git a/saikyo-os-release/debian/saikyo-os-release/DEBIAN/postinst b/saikyo-os-release/debian/saikyo-os-release/DEBIAN/postinst new file mode 100755 index 0000000..5274b65 --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release/DEBIAN/postinst @@ -0,0 +1,27 @@ +#!/usr/bin/env sh +set -e + +case "$1" in + configure|abort-upgrade|abort-remove|abort-deconfigure) + tmpl_dir="/usr/share/saikyo-os-release" + + install_one() { + src="$1" + dst="$2" + if [ -r "$src" ]; then + if [ -e "$dst" ] && ! cmp -s "$src" "$dst"; then + cp -f "$dst" "${dst}.saikyo-bak" 2>/dev/null || true + fi + mkdir -p "$(dirname "$dst")" 2>/dev/null || true + install -m 0644 "$src" "$dst" + fi + } + + install_one "${tmpl_dir}/os-release/os-release" "/etc/os-release" + install_one "${tmpl_dir}/os-release/os-release" "/usr/lib/os-release" + install_one "${tmpl_dir}/lsb-release/lsb-release" "/etc/lsb-release" + install_one "${tmpl_dir}/issue/issue" "/etc/issue" + ;; +esac + +exit 0 diff --git a/saikyo-os-release/debian/saikyo-os-release/usr/share/doc/saikyo-os-release/changelog.gz b/saikyo-os-release/debian/saikyo-os-release/usr/share/doc/saikyo-os-release/changelog.gz new file mode 100644 index 0000000..520f974 Binary files /dev/null and b/saikyo-os-release/debian/saikyo-os-release/usr/share/doc/saikyo-os-release/changelog.gz differ diff --git a/saikyo-os-release/debian/saikyo-os-release/usr/share/doc/saikyo-os-release/copyright b/saikyo-os-release/debian/saikyo-os-release/usr/share/doc/saikyo-os-release/copyright new file mode 100644 index 0000000..33fd0a8 --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release/usr/share/doc/saikyo-os-release/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-os-release +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/issue/issue b/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/issue/issue new file mode 100644 index 0000000..fd2a956 --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/issue/issue @@ -0,0 +1 @@ +SAIKYO OS 1.0 LTS \n \l diff --git a/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/lsb-release/lsb-release b/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/lsb-release/lsb-release new file mode 100644 index 0000000..b178890 --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/lsb-release/lsb-release @@ -0,0 +1,4 @@ +DISTRIB_ID=SAIKYO +DISTRIB_RELEASE=1.0 +DISTRIB_CODENAME=saikyo +DISTRIB_DESCRIPTION="SAIKYO OS 1.0 LTS" diff --git a/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/os-release/os-release b/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/os-release/os-release new file mode 100644 index 0000000..1007ff2 --- /dev/null +++ b/saikyo-os-release/debian/saikyo-os-release/usr/share/saikyo-os-release/os-release/os-release @@ -0,0 +1,9 @@ +PRETTY_NAME="SAIKYO OS 1.0 LTS" +NAME="SAIKYO OS" +VERSION_ID="1.0" +VERSION="1.0 LTS" +ID=saikyo +HOME_URL="https://saikyo-os.ru" +SUPPORT_URL="https://saikyo-os.ru/support" +BUG_REPORT_URL="https://saikyo-os.ru/bugs" +LOGO=distributor-logo diff --git a/saikyo-os-release/files/issue b/saikyo-os-release/files/issue new file mode 100644 index 0000000..fd2a956 --- /dev/null +++ b/saikyo-os-release/files/issue @@ -0,0 +1 @@ +SAIKYO OS 1.0 LTS \n \l diff --git a/saikyo-os-release/files/lsb-release b/saikyo-os-release/files/lsb-release new file mode 100644 index 0000000..b178890 --- /dev/null +++ b/saikyo-os-release/files/lsb-release @@ -0,0 +1,4 @@ +DISTRIB_ID=SAIKYO +DISTRIB_RELEASE=1.0 +DISTRIB_CODENAME=saikyo +DISTRIB_DESCRIPTION="SAIKYO OS 1.0 LTS" diff --git a/saikyo-os-release/files/os-release b/saikyo-os-release/files/os-release new file mode 100644 index 0000000..1007ff2 --- /dev/null +++ b/saikyo-os-release/files/os-release @@ -0,0 +1,9 @@ +PRETTY_NAME="SAIKYO OS 1.0 LTS" +NAME="SAIKYO OS" +VERSION_ID="1.0" +VERSION="1.0 LTS" +ID=saikyo +HOME_URL="https://saikyo-os.ru" +SUPPORT_URL="https://saikyo-os.ru/support" +BUG_REPORT_URL="https://saikyo-os.ru/bugs" +LOGO=distributor-logo diff --git a/saikyo-plymouth-installer/debian/.debhelper/generated/saikyo-plymouth-installer/dh_installchangelogs.dch.trimmed b/saikyo-plymouth-installer/debian/.debhelper/generated/saikyo-plymouth-installer/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..d13c9e7 --- /dev/null +++ b/saikyo-plymouth-installer/debian/.debhelper/generated/saikyo-plymouth-installer/dh_installchangelogs.dch.trimmed @@ -0,0 +1,6 @@ +saikyo-plymouth-installer (1.0.0-1saikyo1) trixie; urgency=medium + + * Initial release. + * Dark theme with Saikyo OS branding for installer. + + -- Saikyo OS Team Wed, 08 Jan 2026 15:55:00 +0000 diff --git a/saikyo-plymouth-installer/debian/.debhelper/generated/saikyo-plymouth-installer/installed-by-dh_installdocs b/saikyo-plymouth-installer/debian/.debhelper/generated/saikyo-plymouth-installer/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-plymouth-installer/debian/changelog b/saikyo-plymouth-installer/debian/changelog new file mode 100644 index 0000000..d13c9e7 --- /dev/null +++ b/saikyo-plymouth-installer/debian/changelog @@ -0,0 +1,6 @@ +saikyo-plymouth-installer (1.0.0-1saikyo1) trixie; urgency=medium + + * Initial release. + * Dark theme with Saikyo OS branding for installer. + + -- Saikyo OS Team Wed, 08 Jan 2026 15:55:00 +0000 diff --git a/saikyo-plymouth-installer/debian/control b/saikyo-plymouth-installer/debian/control new file mode 100644 index 0000000..aaaf037 --- /dev/null +++ b/saikyo-plymouth-installer/debian/control @@ -0,0 +1,13 @@ +Source: saikyo-plymouth-installer +Section: misc +Priority: optional +Maintainer: Saikyo OS Team +Build-Depends: debhelper-compat (= 13), plymouth +Standards-Version: 4.6.0 + +Package: saikyo-plymouth-installer +Architecture: all +Depends: ${misc:Depends}, plymouth, initramfs-tools +Description: Saikyo OS installer Plymouth theme + Dark Plymouth theme with Saikyo OS branding for the installer. + Displays "SAIKYO OS installer" text with a progress bar. diff --git a/saikyo-plymouth-installer/debian/debhelper-build-stamp b/saikyo-plymouth-installer/debian/debhelper-build-stamp new file mode 100644 index 0000000..cb3025f --- /dev/null +++ b/saikyo-plymouth-installer/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-plymouth-installer diff --git a/saikyo-plymouth-installer/debian/files b/saikyo-plymouth-installer/debian/files new file mode 100644 index 0000000..a0a79d4 --- /dev/null +++ b/saikyo-plymouth-installer/debian/files @@ -0,0 +1,2 @@ +saikyo-plymouth-installer_1.0.0-1saikyo1_all.deb misc optional +saikyo-plymouth-installer_1.0.0-1saikyo1_amd64.buildinfo misc optional diff --git a/saikyo-plymouth-installer/debian/postinst b/saikyo-plymouth-installer/debian/postinst new file mode 100644 index 0000000..b714060 --- /dev/null +++ b/saikyo-plymouth-installer/debian/postinst @@ -0,0 +1,17 @@ +#!/bin/sh +set -e + +case "$1" in + configure) + if command -v plymouth-set-default-theme >/dev/null 2>&1; then + # Set saikyo-boot as default theme for system boot + plymouth-set-default-theme -R saikyo-boot >/dev/null 2>&1 || true + fi + # Update initramfs to include plymouth theme + if command -v update-initramfs >/dev/null 2>&1; then + update-initramfs -u >/dev/null 2>&1 || true + fi + ;; +esac + +exit 0 diff --git a/saikyo-plymouth-installer/debian/rules b/saikyo-plymouth-installer/debian/rules new file mode 100755 index 0000000..2d33f6a --- /dev/null +++ b/saikyo-plymouth-installer/debian/rules @@ -0,0 +1,4 @@ +#!/usr/bin/make -f + +%: + dh $@ diff --git a/saikyo-plymouth-installer/debian/saikyo-plymouth-installer.substvars b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/control b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/control new file mode 100644 index 0000000..6d9c3c3 --- /dev/null +++ b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/control @@ -0,0 +1,11 @@ +Package: saikyo-plymouth-installer +Version: 1.0.0-1saikyo1 +Architecture: all +Maintainer: Saikyo OS Team +Installed-Size: 8 +Depends: plymouth, initramfs-tools +Section: misc +Priority: optional +Description: Saikyo OS installer Plymouth theme + Dark Plymouth theme with Saikyo OS branding for the installer. + Displays "SAIKYO OS installer" text with a progress bar. diff --git a/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/md5sums b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/md5sums new file mode 100644 index 0000000..c27e6ee --- /dev/null +++ b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/md5sums @@ -0,0 +1 @@ +6f9624beda3edb4430eeffa4facc450c usr/share/doc/saikyo-plymouth-installer/changelog.Debian.gz diff --git a/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/postinst b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/postinst new file mode 100755 index 0000000..b714060 --- /dev/null +++ b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/DEBIAN/postinst @@ -0,0 +1,17 @@ +#!/bin/sh +set -e + +case "$1" in + configure) + if command -v plymouth-set-default-theme >/dev/null 2>&1; then + # Set saikyo-boot as default theme for system boot + plymouth-set-default-theme -R saikyo-boot >/dev/null 2>&1 || true + fi + # Update initramfs to include plymouth theme + if command -v update-initramfs >/dev/null 2>&1; then + update-initramfs -u >/dev/null 2>&1 || true + fi + ;; +esac + +exit 0 diff --git a/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/usr/share/doc/saikyo-plymouth-installer/changelog.Debian.gz b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/usr/share/doc/saikyo-plymouth-installer/changelog.Debian.gz new file mode 100644 index 0000000..348898f Binary files /dev/null and b/saikyo-plymouth-installer/debian/saikyo-plymouth-installer/usr/share/doc/saikyo-plymouth-installer/changelog.Debian.gz differ diff --git a/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-boot/saikyo-boot.plymouth b/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-boot/saikyo-boot.plymouth new file mode 100644 index 0000000..c8102cf --- /dev/null +++ b/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-boot/saikyo-boot.plymouth @@ -0,0 +1,8 @@ +[Plymouth Theme] +Name=SAIKYO OS +Description=SAIKYO OS boot splash with animated progress bar +ModuleName=script + +[script] +ImageDir=/usr/share/plymouth/themes/saikyo-boot +ScriptFile=/usr/share/plymouth/themes/saikyo-boot/saikyo-boot.script diff --git a/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-boot/saikyo-boot.script b/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-boot/saikyo-boot.script new file mode 100644 index 0000000..61161db --- /dev/null +++ b/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-boot/saikyo-boot.script @@ -0,0 +1,125 @@ +# SAIKYO OS Boot Plymouth Theme +# Simple and reliable boot splash + +# Set dark background +Window.SetBackgroundTopColor(0.02, 0.02, 0.02); +Window.SetBackgroundBottomColor(0.02, 0.02, 0.02); + +# Screen dimensions +screen_width = Window.GetWidth(); +screen_height = Window.GetHeight(); +center_x = screen_width / 2; +center_y = screen_height / 2; + +# Create logo text "SAIKYO OS" in green +logo_image = Image.Text("SAIKYO OS", 0.0, 1.0, 0.4); +logo_sprite = Sprite(logo_image); +logo_sprite.SetX(center_x - logo_image.GetWidth() / 2); +logo_sprite.SetY(center_y - 80); + +# Create subtitle +subtitle_image = Image.Text("Загрузка системы...", 1.0, 1.0, 1.0); +subtitle_sprite = Sprite(subtitle_image); +subtitle_sprite.SetX(center_x - subtitle_image.GetWidth() / 2); +subtitle_sprite.SetY(center_y - 20); + +# Progress bar dimensions +bar_width = 400; +bar_height = 8; +bar_x = center_x - bar_width / 2; +bar_y = center_y + 60; + +# Create progress bar background (dark gray) +bar_bg_image = Image(bar_width, bar_height); +for (x = 0; x < bar_width; x++) +{ + for (y = 0; y < bar_height; y++) + { + bar_bg_image.SetPixel(x, y, 0.15, 0.15, 0.15, 1.0); + } +} +bar_bg_sprite = Sprite(bar_bg_image); +bar_bg_sprite.SetX(bar_x); +bar_bg_sprite.SetY(bar_y); + +# Create progress bar fill (green) +bar_fill_sprite = Sprite(); +bar_fill_sprite.SetX(bar_x); +bar_fill_sprite.SetY(bar_y); + +# Version text +version_image = Image.Text("v1.0", 0.5, 0.5, 0.5); +version_sprite = Sprite(version_image); +version_sprite.SetX(center_x - version_image.GetWidth() / 2); +version_sprite.SetY(screen_height - 50); + +# Progress value +progress_val = 0; + +# Update progress bar +fun update_progress(p) +{ + global.progress_val = p; + if (p < 0) p = 0; + if (p > 1) p = 1; + + fill_w = Math.Int(bar_width * p); + if (fill_w < 1) fill_w = 1; + + fill_image = Image(fill_w, bar_height); + for (x = 0; x < fill_w; x++) + { + for (y = 0; y < bar_height; y++) + { + fill_image.SetPixel(x, y, 0.0, 1.0, 0.4, 1.0); + } + } + bar_fill_sprite.SetImage(fill_image); +} + +# Callbacks +fun refresh_callback() +{ + update_progress(global.progress_val); +} + +fun boot_progress_callback(time, progress) +{ + global.progress_val = progress; + update_progress(progress); +} + +fun display_normal_callback() +{ +} + +fun display_password_callback(prompt, bullets) +{ + pass_image = Image.Text(prompt, 1.0, 1.0, 1.0); + pass_sprite = Sprite(pass_image); + pass_sprite.SetX(center_x - pass_image.GetWidth() / 2); + pass_sprite.SetY(center_y + 120); +} + +fun display_message_callback(text) +{ + msg_image = Image.Text(text, 0.8, 0.8, 0.8); + msg_sprite = Sprite(msg_image); + msg_sprite.SetX(center_x - msg_image.GetWidth() / 2); + msg_sprite.SetY(center_y + 150); +} + +fun quit_callback() +{ +} + +# Register callbacks +Plymouth.SetRefreshFunction(refresh_callback); +Plymouth.SetBootProgressFunction(boot_progress_callback); +Plymouth.SetDisplayNormalFunction(display_normal_callback); +Plymouth.SetDisplayPasswordFunction(display_password_callback); +Plymouth.SetMessageFunction(display_message_callback); +Plymouth.SetQuitFunction(quit_callback); + +# Initial draw +update_progress(0); diff --git a/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-installer/saikyo-installer.plymouth b/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-installer/saikyo-installer.plymouth new file mode 100644 index 0000000..b4e9d34 --- /dev/null +++ b/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-installer/saikyo-installer.plymouth @@ -0,0 +1,14 @@ +[PlymouthTheme] +Name=SAIKYO OS Installer +Description=SAIKYO OS branding (installer) +ModuleName=script + +[script] +ImageDir=/usr/share/plymouth/themes/saikyo-installer +ScriptFile=saikyo-installer.script + +[ubuntu-logo] +Font=Cantarell 12 +TextColor=#ffffff +BackgroundColor=#1a1a1a +Image=/usr/share/plymouth/themes/saikyo-installer/saikyo-logo.png diff --git a/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-installer/saikyo-installer.script b/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-installer/saikyo-installer.script new file mode 100644 index 0000000..61e98ca --- /dev/null +++ b/saikyo-plymouth-installer/usr/share/plymouth/themes/saikyo-installer/saikyo-installer.script @@ -0,0 +1,78 @@ +# Saikyo OS Installer Plymouth Script + +# Global variables +global.progress_bar.sprite = 0; +global.progress_bar.bar = 0; +global.progress_bar.width = 0; +global.progress_bar.height = 0; +global.progress_bar.x = 0; +global.progress_bar.y = 0; + +# Initialize +fun init () { + # Set background + background.color = [0.105, 0.105, 0.105, 1.0]; # Dark #1a1a1a + + # Set up progress bar + global.progress_bar.width = 400; + global.progress_bar.height = 6; + global.progress_bar.x = Window.GetWidth() / 2 - global.progress_bar.width / 2; + global.progress_bar.y = Window.GetHeight() * 0.8; + + # Create progress bar parts + global.progress_bar.sprite = Sprite(); + global.progress_bar.sprite.SetPosition(global.progress_bar.x, global.progress_bar.y, 0); + + global.progress_bar.bar = Sprite(); + global.progress_bar.bar.SetPosition(global.progress_bar.x, global.progress_bar.y, 0); +} + +# Display message +fun display_message_callback (text) { + message = Text(text, "Cantarell 12"); + message.color = [1.0, 1.0, 1.0, 1.0]; # White + message.SetPosition(Window.GetWidth() / 2 - message.GetWidth() / 2, + Window.GetHeight() * 0.7, 1); +} + +# Update progress +fun progress_callback (progress) { + progress_width = global.progress_bar.width * progress; + + # Draw progress bar background + global.progress_bar.sprite.SetImage(global.progress_bar.width, global.progress_bar.height); + global.progress_bar.sprite.SetPosition(global.progress_bar.x, global.progress_bar.y, 0); + + # Draw progress bar fill + global.progress_bar.bar.SetImage(progress_width, global.progress_bar.height); + global.progress_bar.bar.SetPosition(global.progress_bar.x, global.progress_bar.y, 1); +} + +# Main loop +fun refresh () { + # Draw "SAIKYO OS" text + title = Text("SAIKYO OS", "Cantarell 24"); + title.color = [1.0, 1.0, 1.0, 1.0]; # White + title.SetPosition(Window.GetWidth() / 2 - title.GetWidth() / 2, + Window.GetHeight() * 0.3, 2); + + # Draw progress bar + if (global.progress_bar.sprite) { + global.progress_bar.sprite.SetOpacity(1.0); + } + + if (global.progress_bar.bar) { + global.progress_bar.bar.SetOpacity(1.0); + } +} + +# Cleanup +fun quit () { + # Clean up sprites + if (global.progress_bar.sprite) { + global.progress_bar.sprite = NULL; + } + if (global.progress_bar.bar) { + global.progress_bar.bar = NULL; + } +} diff --git a/saikyo-security-profile/bin/saikyo-security-profile b/saikyo-security-profile/bin/saikyo-security-profile new file mode 100644 index 0000000..87be114 --- /dev/null +++ b/saikyo-security-profile/bin/saikyo-security-profile @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import subprocess +import sys +from pathlib import Path + + +def _run(cmd, check=True): + return subprocess.run(cmd, check=check) + + +def _write_file(path: Path, content: str, mode: int = 0o644): + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_suffix(path.suffix + ".tmp") + tmp.write_text(content, encoding="utf-8") + os.chmod(tmp, mode) + tmp.replace(path) + + +def _load_profile(profile_name: str) -> dict: + base = Path("/usr/share/saikyo-security-profile/profiles") + p = base / f"{profile_name}.json" + if not p.exists(): + raise FileNotFoundError(f"profile not found: {p}") + return json.loads(p.read_text(encoding="utf-8")) + + +def apply_profile(profile_name: str): + prof = _load_profile(profile_name) + + sysctl_lines = prof.get("sysctl", {}) + sysctl_text = "".join([f"{k} = {v}\n" for k, v in sysctl_lines.items()]) + _write_file(Path("/etc/sysctl.d/99-saikyo-security-profile.conf"), sysctl_text) + _run(["/sbin/sysctl", "--system"], check=False) + + enable_services = prof.get("enable_services", []) + for svc in enable_services: + _run(["/bin/systemctl", "enable", "--now", svc], check=False) + + ufw = prof.get("ufw", {}) + if ufw.get("enable", False): + _run(["/usr/sbin/ufw", "--force", "reset"], check=False) + default_in = ufw.get("default_in", "deny") + default_out = ufw.get("default_out", "allow") + _run(["/usr/sbin/ufw", "default", default_in, "incoming"], check=False) + _run(["/usr/sbin/ufw", "default", default_out, "outgoing"], check=False) + for rule in ufw.get("allow", []): + _run(["/usr/sbin/ufw", "allow"] + rule.split(), check=False) + _run(["/usr/sbin/ufw", "--force", "enable"], check=False) + + +def export_profile(profile_name: str, out_path: str): + prof = _load_profile(profile_name) + out = Path(out_path) + out.write_text(json.dumps(prof, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def import_profile(profile_name: str, in_path: str): + base = Path("/usr/share/saikyo-security-profile/profiles") + dst = base / f"{profile_name}.json" + src = Path(in_path) + data = json.loads(src.read_text(encoding="utf-8")) + dst.parent.mkdir(parents=True, exist_ok=True) + dst.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def main(argv): + p = argparse.ArgumentParser(prog="saikyo-security-profile") + sub = p.add_subparsers(dest="cmd", required=True) + + ap = sub.add_parser("apply") + ap.add_argument("profile", choices=["standard", "secure"]) + + ep = sub.add_parser("export") + ep.add_argument("profile", choices=["standard", "secure"]) + ep.add_argument("path") + + ip = sub.add_parser("import") + ip.add_argument("profile", choices=["standard", "secure"]) + ip.add_argument("path") + + args = p.parse_args(argv) + + if args.cmd == "apply": + apply_profile(args.profile) + return 0 + if args.cmd == "export": + export_profile(args.profile, args.path) + return 0 + if args.cmd == "import": + import_profile(args.profile, args.path) + return 0 + + return 2 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/saikyo-security-profile/bin/saikyo-security-profile-gui b/saikyo-security-profile/bin/saikyo-security-profile-gui new file mode 100644 index 0000000..f5510b5 --- /dev/null +++ b/saikyo-security-profile/bin/saikyo-security-profile-gui @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +import json +import os +import subprocess +import sys +from pathlib import Path + +from PyQt5 import QtCore, QtWidgets + + +APP_TITLE = "SAIKYO OS — Security Profile Manager" + + +def _run(cmd, check=False): + return subprocess.run(cmd, check=check) + + +def _pkexec(cmd): + # pkexec will prompt for admin auth via polkit + return _run(["/usr/bin/pkexec"] + cmd, check=False) + + +def _profiles_dir() -> Path: + return Path("/usr/share/saikyo-security-profile/profiles") + + +def _read_profile(profile: str) -> dict: + p = _profiles_dir() / f"{profile}.json" + return json.loads(p.read_text(encoding="utf-8")) + + +class MainWindow(QtWidgets.QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle(APP_TITLE) + self.setMinimumSize(720, 480) + + root = QtWidgets.QWidget(self) + self.setCentralWidget(root) + + self.profile_combo = QtWidgets.QComboBox() + self.profile_combo.addItems(["standard", "secure"]) + + self.apply_btn = QtWidgets.QPushButton("Применить") + self.export_btn = QtWidgets.QPushButton("Экспорт...") + self.import_btn = QtWidgets.QPushButton("Импорт...") + self.reload_btn = QtWidgets.QPushButton("Обновить") + + self.preview = QtWidgets.QPlainTextEdit() + self.preview.setReadOnly(True) + self.preview.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) + + top = QtWidgets.QHBoxLayout() + top.addWidget(QtWidgets.QLabel("Профиль:")) + top.addWidget(self.profile_combo, 1) + top.addWidget(self.apply_btn) + top.addWidget(self.export_btn) + top.addWidget(self.import_btn) + top.addWidget(self.reload_btn) + + layout = QtWidgets.QVBoxLayout(root) + layout.addLayout(top) + layout.addWidget(self.preview, 1) + + self.apply_btn.clicked.connect(self.on_apply) + self.export_btn.clicked.connect(self.on_export) + self.import_btn.clicked.connect(self.on_import) + self.reload_btn.clicked.connect(self.refresh_preview) + self.profile_combo.currentTextChanged.connect(lambda _: self.refresh_preview()) + + self.refresh_preview() + + def current_profile(self) -> str: + return self.profile_combo.currentText().strip() + + def refresh_preview(self): + prof_name = self.current_profile() + try: + data = _read_profile(prof_name) + self.preview.setPlainText(json.dumps(data, indent=2, sort_keys=True) + "\n") + except Exception as e: + self.preview.setPlainText(f"Ошибка чтения профиля '{prof_name}': {e}\n") + + def _msg(self, title: str, text: str, icon=QtWidgets.QMessageBox.Information): + m = QtWidgets.QMessageBox(self) + m.setIcon(icon) + m.setWindowTitle(title) + m.setText(text) + m.exec_() + + def on_apply(self): + prof = self.current_profile() + rc = _pkexec(["/usr/sbin/saikyo-security-profile", "apply", prof]).returncode + if rc == 0: + self._msg("Готово", f"Профиль '{prof}' применён.") + else: + self._msg("Ошибка", f"Не удалось применить профиль '{prof}'. Код: {rc}", QtWidgets.QMessageBox.Critical) + + def on_export(self): + prof = self.current_profile() + path, _ = QtWidgets.QFileDialog.getSaveFileName( + self, + "Экспорт профиля", + str(Path.home() / f"{prof}.json"), + "JSON (*.json);;All files (*)", + ) + if not path: + return + + try: + # Export does not require root + rc = _run(["/usr/sbin/saikyo-security-profile", "export", prof, path], check=False).returncode + if rc == 0: + self._msg("Готово", f"Профиль '{prof}' экспортирован в:\n{path}") + else: + self._msg("Ошибка", f"Не удалось экспортировать профиль '{prof}'. Код: {rc}", QtWidgets.QMessageBox.Critical) + except Exception as e: + self._msg("Ошибка", str(e), QtWidgets.QMessageBox.Critical) + + def on_import(self): + prof = self.current_profile() + path, _ = QtWidgets.QFileDialog.getOpenFileName( + self, + "Импорт профиля", + str(Path.home()), + "JSON (*.json);;All files (*)", + ) + if not path: + return + + # Validate JSON before asking for auth + try: + json.loads(Path(path).read_text(encoding="utf-8")) + except Exception as e: + self._msg("Ошибка", f"Некорректный JSON: {e}", QtWidgets.QMessageBox.Critical) + return + + rc = _pkexec(["/usr/sbin/saikyo-security-profile", "import", prof, path]).returncode + if rc == 0: + self._msg("Готово", f"Профиль '{prof}' импортирован из:\n{path}") + self.refresh_preview() + else: + self._msg("Ошибка", f"Не удалось импортировать профиль '{prof}'. Код: {rc}", QtWidgets.QMessageBox.Critical) + + +def main(argv): + if not Path("/usr/sbin/saikyo-security-profile").exists(): + print("ERROR: /usr/sbin/saikyo-security-profile not found", file=sys.stderr) + return 2 + + if not Path("/usr/bin/pkexec").exists(): + print("ERROR: pkexec not found (install policykit-1)", file=sys.stderr) + return 2 + + app = QtWidgets.QApplication(list(argv)) + app.setApplicationName("saikyo-security-profile-gui") + w = MainWindow() + w.show() + return app.exec_() + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv)) diff --git a/saikyo-security-profile/debian/.debhelper/generated/saikyo-security-profile/dh_installchangelogs.dch.trimmed b/saikyo-security-profile/debian/.debhelper/generated/saikyo-security-profile/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..4158015 --- /dev/null +++ b/saikyo-security-profile/debian/.debhelper/generated/saikyo-security-profile/dh_installchangelogs.dch.trimmed @@ -0,0 +1,5 @@ +saikyo-security-profile (1.0.1) stable; urgency=medium + + * Add Security Profile Manager GUI (apply/import/export). + + -- SAIKYO OS Sun, 18 Jan 2026 20:30:00 +0000 diff --git a/saikyo-security-profile/debian/.debhelper/generated/saikyo-security-profile/installed-by-dh_install b/saikyo-security-profile/debian/.debhelper/generated/saikyo-security-profile/installed-by-dh_install new file mode 100644 index 0000000..ed197fd --- /dev/null +++ b/saikyo-security-profile/debian/.debhelper/generated/saikyo-security-profile/installed-by-dh_install @@ -0,0 +1,4 @@ +./bin/saikyo-security-profile +./bin/saikyo-security-profile-gui +./profiles/ +./debian/saikyo-security-profile.desktop diff --git a/saikyo-security-profile/debian/.debhelper/generated/saikyo-security-profile/installed-by-dh_installdocs b/saikyo-security-profile/debian/.debhelper/generated/saikyo-security-profile/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/saikyo-security-profile/debian/changelog b/saikyo-security-profile/debian/changelog new file mode 100644 index 0000000..4158015 --- /dev/null +++ b/saikyo-security-profile/debian/changelog @@ -0,0 +1,5 @@ +saikyo-security-profile (1.0.1) stable; urgency=medium + + * Add Security Profile Manager GUI (apply/import/export). + + -- SAIKYO OS Sun, 18 Jan 2026 20:30:00 +0000 diff --git a/saikyo-security-profile/debian/control b/saikyo-security-profile/debian/control new file mode 100644 index 0000000..1ed8f37 --- /dev/null +++ b/saikyo-security-profile/debian/control @@ -0,0 +1,14 @@ +Source: saikyo-security-profile +Section: admin +Priority: optional +Maintainer: SAIKYO OS +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.2 +Rules-Requires-Root: no + +Package: saikyo-security-profile +Architecture: all +Depends: ${misc:Depends}, bash, coreutils, python3, python3-pyqt5, polkitd, systemd, procps, ufw, auditd, apparmor +Description: Saikyo OS security profile manager (CLI) + Applies a security profile (standard/secure) by configuring baseline settings + and enabling required security services. diff --git a/saikyo-security-profile/debian/copyright b/saikyo-security-profile/debian/copyright new file mode 100644 index 0000000..50c07c4 --- /dev/null +++ b/saikyo-security-profile/debian/copyright @@ -0,0 +1,26 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-security-profile +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-security-profile/debian/debhelper-build-stamp b/saikyo-security-profile/debian/debhelper-build-stamp new file mode 100644 index 0000000..84a6126 --- /dev/null +++ b/saikyo-security-profile/debian/debhelper-build-stamp @@ -0,0 +1 @@ +saikyo-security-profile diff --git a/saikyo-security-profile/debian/files b/saikyo-security-profile/debian/files new file mode 100644 index 0000000..8d6fb41 --- /dev/null +++ b/saikyo-security-profile/debian/files @@ -0,0 +1,2 @@ +saikyo-security-profile_1.0.1_all.deb admin optional +saikyo-security-profile_1.0.1_amd64.buildinfo admin optional diff --git a/saikyo-security-profile/debian/install b/saikyo-security-profile/debian/install new file mode 100644 index 0000000..58817a3 --- /dev/null +++ b/saikyo-security-profile/debian/install @@ -0,0 +1,4 @@ +bin/saikyo-security-profile usr/sbin/saikyo-security-profile +bin/saikyo-security-profile-gui usr/bin/saikyo-security-profile-gui +profiles/ usr/share/saikyo-security-profile/profiles/ +debian/saikyo-security-profile.desktop usr/share/applications/saikyo-security-profile.desktop diff --git a/saikyo-security-profile/debian/rules b/saikyo-security-profile/debian/rules new file mode 100755 index 0000000..2316691 --- /dev/null +++ b/saikyo-security-profile/debian/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + +override_dh_auto_test: + +override_dh_fixperms: + dh_fixperms + chmod 0755 debian/saikyo-security-profile/usr/sbin/saikyo-security-profile || true + chmod 0755 debian/saikyo-security-profile/usr/bin/saikyo-security-profile-gui || true diff --git a/saikyo-security-profile/debian/saikyo-security-profile.debhelper.log b/saikyo-security-profile/debian/saikyo-security-profile.debhelper.log new file mode 100644 index 0000000..93c5512 --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile.debhelper.log @@ -0,0 +1 @@ +dh_fixperms diff --git a/saikyo-security-profile/debian/saikyo-security-profile.desktop b/saikyo-security-profile/debian/saikyo-security-profile.desktop new file mode 100644 index 0000000..5e9db6a --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Security Profile Manager +Name[ru]=Менеджер профилей безопасности +Exec=/usr/bin/saikyo-security-profile-gui +Terminal=false +Categories=Settings;Security; diff --git a/saikyo-security-profile/debian/saikyo-security-profile.substvars b/saikyo-security-profile/debian/saikyo-security-profile.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/saikyo-security-profile/debian/saikyo-security-profile/DEBIAN/control b/saikyo-security-profile/debian/saikyo-security-profile/DEBIAN/control new file mode 100644 index 0000000..ac8eaf5 --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile/DEBIAN/control @@ -0,0 +1,11 @@ +Package: saikyo-security-profile +Version: 1.0.1 +Architecture: all +Maintainer: SAIKYO OS +Installed-Size: 31 +Depends: bash, coreutils, python3, python3-pyqt5, polkitd, systemd, procps, ufw, auditd, apparmor +Section: admin +Priority: optional +Description: Saikyo OS security profile manager (CLI) + Applies a security profile (standard/secure) by configuring baseline settings + and enabling required security services. diff --git a/saikyo-security-profile/debian/saikyo-security-profile/DEBIAN/md5sums b/saikyo-security-profile/debian/saikyo-security-profile/DEBIAN/md5sums new file mode 100644 index 0000000..f9e4b82 --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile/DEBIAN/md5sums @@ -0,0 +1,7 @@ +b1d0d3b73945348d2cf555472dbe7016 usr/bin/saikyo-security-profile-gui/saikyo-security-profile-gui +1db25ffdd765d32c4d0949bf38e30431 usr/sbin/saikyo-security-profile/saikyo-security-profile +57cef1ddf68fd0706451c0fc644cb8ce usr/share/applications/saikyo-security-profile.desktop/saikyo-security-profile.desktop +797af95e229f1756ba914a145e66a770 usr/share/doc/saikyo-security-profile/changelog.gz +ee0eed21e243496fc3682267e85d6c54 usr/share/doc/saikyo-security-profile/copyright +f88a09da73b9d8875e32a8b55d2b874c usr/share/saikyo-security-profile/profiles/profiles/secure.json +67ab84f2ba3e4bc0b510e76e3e45e864 usr/share/saikyo-security-profile/profiles/profiles/standard.json diff --git a/saikyo-security-profile/debian/saikyo-security-profile/usr/bin/saikyo-security-profile-gui/saikyo-security-profile-gui b/saikyo-security-profile/debian/saikyo-security-profile/usr/bin/saikyo-security-profile-gui/saikyo-security-profile-gui new file mode 100755 index 0000000..f5510b5 --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile/usr/bin/saikyo-security-profile-gui/saikyo-security-profile-gui @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +import json +import os +import subprocess +import sys +from pathlib import Path + +from PyQt5 import QtCore, QtWidgets + + +APP_TITLE = "SAIKYO OS — Security Profile Manager" + + +def _run(cmd, check=False): + return subprocess.run(cmd, check=check) + + +def _pkexec(cmd): + # pkexec will prompt for admin auth via polkit + return _run(["/usr/bin/pkexec"] + cmd, check=False) + + +def _profiles_dir() -> Path: + return Path("/usr/share/saikyo-security-profile/profiles") + + +def _read_profile(profile: str) -> dict: + p = _profiles_dir() / f"{profile}.json" + return json.loads(p.read_text(encoding="utf-8")) + + +class MainWindow(QtWidgets.QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle(APP_TITLE) + self.setMinimumSize(720, 480) + + root = QtWidgets.QWidget(self) + self.setCentralWidget(root) + + self.profile_combo = QtWidgets.QComboBox() + self.profile_combo.addItems(["standard", "secure"]) + + self.apply_btn = QtWidgets.QPushButton("Применить") + self.export_btn = QtWidgets.QPushButton("Экспорт...") + self.import_btn = QtWidgets.QPushButton("Импорт...") + self.reload_btn = QtWidgets.QPushButton("Обновить") + + self.preview = QtWidgets.QPlainTextEdit() + self.preview.setReadOnly(True) + self.preview.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) + + top = QtWidgets.QHBoxLayout() + top.addWidget(QtWidgets.QLabel("Профиль:")) + top.addWidget(self.profile_combo, 1) + top.addWidget(self.apply_btn) + top.addWidget(self.export_btn) + top.addWidget(self.import_btn) + top.addWidget(self.reload_btn) + + layout = QtWidgets.QVBoxLayout(root) + layout.addLayout(top) + layout.addWidget(self.preview, 1) + + self.apply_btn.clicked.connect(self.on_apply) + self.export_btn.clicked.connect(self.on_export) + self.import_btn.clicked.connect(self.on_import) + self.reload_btn.clicked.connect(self.refresh_preview) + self.profile_combo.currentTextChanged.connect(lambda _: self.refresh_preview()) + + self.refresh_preview() + + def current_profile(self) -> str: + return self.profile_combo.currentText().strip() + + def refresh_preview(self): + prof_name = self.current_profile() + try: + data = _read_profile(prof_name) + self.preview.setPlainText(json.dumps(data, indent=2, sort_keys=True) + "\n") + except Exception as e: + self.preview.setPlainText(f"Ошибка чтения профиля '{prof_name}': {e}\n") + + def _msg(self, title: str, text: str, icon=QtWidgets.QMessageBox.Information): + m = QtWidgets.QMessageBox(self) + m.setIcon(icon) + m.setWindowTitle(title) + m.setText(text) + m.exec_() + + def on_apply(self): + prof = self.current_profile() + rc = _pkexec(["/usr/sbin/saikyo-security-profile", "apply", prof]).returncode + if rc == 0: + self._msg("Готово", f"Профиль '{prof}' применён.") + else: + self._msg("Ошибка", f"Не удалось применить профиль '{prof}'. Код: {rc}", QtWidgets.QMessageBox.Critical) + + def on_export(self): + prof = self.current_profile() + path, _ = QtWidgets.QFileDialog.getSaveFileName( + self, + "Экспорт профиля", + str(Path.home() / f"{prof}.json"), + "JSON (*.json);;All files (*)", + ) + if not path: + return + + try: + # Export does not require root + rc = _run(["/usr/sbin/saikyo-security-profile", "export", prof, path], check=False).returncode + if rc == 0: + self._msg("Готово", f"Профиль '{prof}' экспортирован в:\n{path}") + else: + self._msg("Ошибка", f"Не удалось экспортировать профиль '{prof}'. Код: {rc}", QtWidgets.QMessageBox.Critical) + except Exception as e: + self._msg("Ошибка", str(e), QtWidgets.QMessageBox.Critical) + + def on_import(self): + prof = self.current_profile() + path, _ = QtWidgets.QFileDialog.getOpenFileName( + self, + "Импорт профиля", + str(Path.home()), + "JSON (*.json);;All files (*)", + ) + if not path: + return + + # Validate JSON before asking for auth + try: + json.loads(Path(path).read_text(encoding="utf-8")) + except Exception as e: + self._msg("Ошибка", f"Некорректный JSON: {e}", QtWidgets.QMessageBox.Critical) + return + + rc = _pkexec(["/usr/sbin/saikyo-security-profile", "import", prof, path]).returncode + if rc == 0: + self._msg("Готово", f"Профиль '{prof}' импортирован из:\n{path}") + self.refresh_preview() + else: + self._msg("Ошибка", f"Не удалось импортировать профиль '{prof}'. Код: {rc}", QtWidgets.QMessageBox.Critical) + + +def main(argv): + if not Path("/usr/sbin/saikyo-security-profile").exists(): + print("ERROR: /usr/sbin/saikyo-security-profile not found", file=sys.stderr) + return 2 + + if not Path("/usr/bin/pkexec").exists(): + print("ERROR: pkexec not found (install policykit-1)", file=sys.stderr) + return 2 + + app = QtWidgets.QApplication(list(argv)) + app.setApplicationName("saikyo-security-profile-gui") + w = MainWindow() + w.show() + return app.exec_() + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv)) diff --git a/saikyo-security-profile/debian/saikyo-security-profile/usr/sbin/saikyo-security-profile/saikyo-security-profile b/saikyo-security-profile/debian/saikyo-security-profile/usr/sbin/saikyo-security-profile/saikyo-security-profile new file mode 100755 index 0000000..87be114 --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile/usr/sbin/saikyo-security-profile/saikyo-security-profile @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import subprocess +import sys +from pathlib import Path + + +def _run(cmd, check=True): + return subprocess.run(cmd, check=check) + + +def _write_file(path: Path, content: str, mode: int = 0o644): + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_suffix(path.suffix + ".tmp") + tmp.write_text(content, encoding="utf-8") + os.chmod(tmp, mode) + tmp.replace(path) + + +def _load_profile(profile_name: str) -> dict: + base = Path("/usr/share/saikyo-security-profile/profiles") + p = base / f"{profile_name}.json" + if not p.exists(): + raise FileNotFoundError(f"profile not found: {p}") + return json.loads(p.read_text(encoding="utf-8")) + + +def apply_profile(profile_name: str): + prof = _load_profile(profile_name) + + sysctl_lines = prof.get("sysctl", {}) + sysctl_text = "".join([f"{k} = {v}\n" for k, v in sysctl_lines.items()]) + _write_file(Path("/etc/sysctl.d/99-saikyo-security-profile.conf"), sysctl_text) + _run(["/sbin/sysctl", "--system"], check=False) + + enable_services = prof.get("enable_services", []) + for svc in enable_services: + _run(["/bin/systemctl", "enable", "--now", svc], check=False) + + ufw = prof.get("ufw", {}) + if ufw.get("enable", False): + _run(["/usr/sbin/ufw", "--force", "reset"], check=False) + default_in = ufw.get("default_in", "deny") + default_out = ufw.get("default_out", "allow") + _run(["/usr/sbin/ufw", "default", default_in, "incoming"], check=False) + _run(["/usr/sbin/ufw", "default", default_out, "outgoing"], check=False) + for rule in ufw.get("allow", []): + _run(["/usr/sbin/ufw", "allow"] + rule.split(), check=False) + _run(["/usr/sbin/ufw", "--force", "enable"], check=False) + + +def export_profile(profile_name: str, out_path: str): + prof = _load_profile(profile_name) + out = Path(out_path) + out.write_text(json.dumps(prof, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def import_profile(profile_name: str, in_path: str): + base = Path("/usr/share/saikyo-security-profile/profiles") + dst = base / f"{profile_name}.json" + src = Path(in_path) + data = json.loads(src.read_text(encoding="utf-8")) + dst.parent.mkdir(parents=True, exist_ok=True) + dst.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def main(argv): + p = argparse.ArgumentParser(prog="saikyo-security-profile") + sub = p.add_subparsers(dest="cmd", required=True) + + ap = sub.add_parser("apply") + ap.add_argument("profile", choices=["standard", "secure"]) + + ep = sub.add_parser("export") + ep.add_argument("profile", choices=["standard", "secure"]) + ep.add_argument("path") + + ip = sub.add_parser("import") + ip.add_argument("profile", choices=["standard", "secure"]) + ip.add_argument("path") + + args = p.parse_args(argv) + + if args.cmd == "apply": + apply_profile(args.profile) + return 0 + if args.cmd == "export": + export_profile(args.profile, args.path) + return 0 + if args.cmd == "import": + import_profile(args.profile, args.path) + return 0 + + return 2 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/saikyo-security-profile/debian/saikyo-security-profile/usr/share/applications/saikyo-security-profile.desktop/saikyo-security-profile.desktop b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/applications/saikyo-security-profile.desktop/saikyo-security-profile.desktop new file mode 100644 index 0000000..5e9db6a --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/applications/saikyo-security-profile.desktop/saikyo-security-profile.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Security Profile Manager +Name[ru]=Менеджер профилей безопасности +Exec=/usr/bin/saikyo-security-profile-gui +Terminal=false +Categories=Settings;Security; diff --git a/saikyo-security-profile/debian/saikyo-security-profile/usr/share/doc/saikyo-security-profile/changelog.gz b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/doc/saikyo-security-profile/changelog.gz new file mode 100644 index 0000000..cb03114 Binary files /dev/null and b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/doc/saikyo-security-profile/changelog.gz differ diff --git a/saikyo-security-profile/debian/saikyo-security-profile/usr/share/doc/saikyo-security-profile/copyright b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/doc/saikyo-security-profile/copyright new file mode 100644 index 0000000..50c07c4 --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/doc/saikyo-security-profile/copyright @@ -0,0 +1,26 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: saikyo-security-profile +Source: https://saikyo-os.ru + +Files: * +Copyright: 2026 SAIKYO OS +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/saikyo-security-profile/debian/saikyo-security-profile/usr/share/saikyo-security-profile/profiles/profiles/secure.json b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/saikyo-security-profile/profiles/profiles/secure.json new file mode 100644 index 0000000..a9fd88a --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/saikyo-security-profile/profiles/profiles/secure.json @@ -0,0 +1,26 @@ +{ + "enable_services": [ + "apparmor.service", + "auditd.service" + ], + "sysctl": { + "fs.protected_hardlinks": "1", + "fs.protected_symlinks": "1", + "kernel.dmesg_restrict": "1", + "kernel.kptr_restrict": "2", + "kernel.unprivileged_bpf_disabled": "1", + "kernel.yama.ptrace_scope": "1", + "net.ipv4.conf.all.accept_redirects": "0", + "net.ipv4.conf.all.rp_filter": "1", + "net.ipv4.conf.default.accept_redirects": "0", + "net.ipv4.conf.default.rp_filter": "1" + }, + "ufw": { + "allow": [ + "OpenSSH" + ], + "default_in": "deny", + "default_out": "allow", + "enable": true + } +} diff --git a/saikyo-security-profile/debian/saikyo-security-profile/usr/share/saikyo-security-profile/profiles/profiles/standard.json b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/saikyo-security-profile/profiles/profiles/standard.json new file mode 100644 index 0000000..a521cee --- /dev/null +++ b/saikyo-security-profile/debian/saikyo-security-profile/usr/share/saikyo-security-profile/profiles/profiles/standard.json @@ -0,0 +1,21 @@ +{ + "enable_services": [ + "apparmor.service", + "auditd.service" + ], + "sysctl": { + "kernel.dmesg_restrict": "1", + "kernel.kptr_restrict": "2", + "kernel.yama.ptrace_scope": "1", + "net.ipv4.conf.all.rp_filter": "1", + "net.ipv4.conf.default.rp_filter": "1" + }, + "ufw": { + "allow": [ + "OpenSSH" + ], + "default_in": "deny", + "default_out": "allow", + "enable": true + } +} diff --git a/saikyo-security-profile/profiles/secure.json b/saikyo-security-profile/profiles/secure.json new file mode 100644 index 0000000..a9fd88a --- /dev/null +++ b/saikyo-security-profile/profiles/secure.json @@ -0,0 +1,26 @@ +{ + "enable_services": [ + "apparmor.service", + "auditd.service" + ], + "sysctl": { + "fs.protected_hardlinks": "1", + "fs.protected_symlinks": "1", + "kernel.dmesg_restrict": "1", + "kernel.kptr_restrict": "2", + "kernel.unprivileged_bpf_disabled": "1", + "kernel.yama.ptrace_scope": "1", + "net.ipv4.conf.all.accept_redirects": "0", + "net.ipv4.conf.all.rp_filter": "1", + "net.ipv4.conf.default.accept_redirects": "0", + "net.ipv4.conf.default.rp_filter": "1" + }, + "ufw": { + "allow": [ + "OpenSSH" + ], + "default_in": "deny", + "default_out": "allow", + "enable": true + } +} diff --git a/saikyo-security-profile/profiles/standard.json b/saikyo-security-profile/profiles/standard.json new file mode 100644 index 0000000..a521cee --- /dev/null +++ b/saikyo-security-profile/profiles/standard.json @@ -0,0 +1,21 @@ +{ + "enable_services": [ + "apparmor.service", + "auditd.service" + ], + "sysctl": { + "kernel.dmesg_restrict": "1", + "kernel.kptr_restrict": "2", + "kernel.yama.ptrace_scope": "1", + "net.ipv4.conf.all.rp_filter": "1", + "net.ipv4.conf.default.rp_filter": "1" + }, + "ufw": { + "allow": [ + "OpenSSH" + ], + "default_in": "deny", + "default_out": "allow", + "enable": true + } +}