Skip to content

v0.1.23-alpha

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 05 Oct 16:24
· 114 commits to main since this release

İmzalı sürüm (ed25519). manifest.json ve manifest.json.sig release'e dahildir; açık anahtar depoda keys/pops_release_ed25519.pub.pem. Doğrulama için bkz. keys/README.md. Ajan için POps-Agent-*-win-x64.msi önerilir (sessiz kurulum ve özellikleri: Installer/README.md); zip eski elle kurulum yoludur. Ajan paketleri Windows'ta SmartScreen uyarısı verebilir (Authenticode imzası henüz yok). MSI kendi .NET 10 çalışma zamanını getirir; uç noktada önceden bir şey kurmak gerekmez. Pardus/Debian için pops-agent_*_all.deb (Debian 12 / Pardus 23 ve sonrası; kurulum: Agent-Linux/README.md).

For operators, in short:

  1. New in the panel:
    • Exam mode per lab: the network is limited to an allow list, with optional app blocking. It lasts at most 8 hours and ends on time even offline.
    • File transfer: send a file to PCs, or fetch one from a PC with a reason.
    • Power actions with a countdown and a note, and messages to the user with an optional read receipt.
    • winget packages in Dağıtım, with a 69-app catalogue.
    • Vision v2: several screens, adaptive quality, and clipboard in a session the user accepted.
    • Every page in English.
  2. For organisations:
    • Sign-in with Active Directory/LDAP or OpenID Connect.
    • Organisational units (district → school): admins and API tokens see only their own schools.
    • REST API v1 with tokens, GLPI export and Sistem → Modüller.
    • Optionally, several backend workers with Redis.
  3. Linux agent, first version (Pardus 23+, Debian 12, Ubuntu 24.04; one .deb):
    • It reports inventory and installed packages, runs commands, and updates itself from signed releases with rollback.
    • It has no screen view, quarantine or tray yet.
  4. Update your agents (Sistem → Güncellemeler). The new Windows agent is needed for exam mode, file transfer, winget, power actions and messages, and Vision v2. Older agents are recognised: they are skipped, or get the old shutdown command. It also:
    • shows each update stage in the panel and resumes downloads (BITS);
    • deletes logs after 30 days;
    • lets you move the log and data folders.
  5. Before updating a native server:
    • Reinstall /usr/local/sbin/pops-deploy-backend from this release first. Migration 0031 rewrites large tables, and the new script takes a database dump before it.
    • Migrations 0024–0032 run by themselves; expect a longer first start.
    • If you edited Dashboard/assets/pops_config.js, rename its global to POPS_API.
    • The lab peer cache for agent updates is off by default.

Try it without installing: https://demo.pashacore.com.tr (demo / demo, read-only). Known limitations are in the README.

Added

  • Agent: lab-local peer cache for updates. The agent announces peer_cache in X-Agent-Features and follows the server's staged rollout. It downloads an update from the peers in update_agent first, then from the server, and checks every source against the signed size and SHA-256. Only when update_agent carries "peer_cache": true (the server's "Sınıf içinde eşten dağıt" setting, off by default) does it keep the verified package in C:\POpsData\cache for 2 hours (or until the next update). It then serves it read-only to PCs in its own subnet on TCP 8817: GET/HEAD /pops-cache/<sha256> only, at most 4 transfers, firewall rule group "POps Peer Cache" (LocalSubnet, POpsAgent.exe). Nothing is served or fetched from peers during quarantine or exam mode. The downloaded stage names the source (peer <hw_id>, server, cache). capabilities reports peer_cache_enabled; PEER_CACHE_ENABLED=0 on the MSI switches it off, and the feature is then not announced.
  • Several backend workers (optional, D-26). With REDIS_URL set, the backend can run as several uvicorn workers or on several servers behind a load balancer, without sticky sessions. Off by default: a single-process install behaves as before. See docs/ha.md.
    • Shared through Redis: commands, panel broadcasts, screen frames (only to session holders), Vision messages, the device list, session grants, the online-agent registry, notification limits and login rate limits.
    • A dead worker's agents are marked Offline, and one leader runs the periodic jobs. If Redis is down, each worker keeps its own agents and panels running.
  • tools/agent_simulator.py --url A,B spreads agents over several backends, and --report-every prints the split.
  • Agent: power actions and messages to the user. The agent announces power and message in X-Agent-Features and follows the server's power and user_message schemas. power shuts the PC down, restarts it, signs the user out or locks it, after a countdown of up to 10 minutes that the tray shows on top with the administrator's note ("Bilgisayar 60 sn içinde yeniden başlatılacak"); the service keeps the time, so it also acts without the tray, and the result is sent just before acting. Lock goes through the tray (LockWorkStation) and falls back to disconnecting the console session; sign-out uses WTSLogoffSession; shutdown and restart use shutdown.exe /f. cancel_task stops a countdown. user_message shows a window with a title, a text of up to 1000 characters and an info or warning icon; with requires_ack the single result waits for the user's Tamam ([TAMAM] okundu) for up to 30 minutes. Sign-out, lock and messages need a signed-in user (otherwise exit code -6, "[REDDEDİLDİ] oturum açık kullanıcı yok"); a message while a user is signed in but the tray is not running fails with -3. Every field is checked again on the agent and the text is cleaned as on the server (control, text-direction and zero-width characters removed; title and note on one line). Both can be switched off on the PC with the new MSI properties POWER_ENABLED=0 / MESSAGE_ENABLED=0 (on by default) or by the server with set_capabilities; switching on again needs the MSI. New event log entries 1130 (power action), 1140 and 1141 (message shown and its outcome) hold only metadata, never the message text.
  • Agent: the capabilities message reports exam_enabled, power_enabled and message_enabled (optional fields in the schema), next to terminal_enabled, vision_enabled, files_enabled and server_ca.
  • Agent: log and data folders from appsettings.json. LogDirectory and DataDirectory move C:\POpsLogs and C:\POpsData, for example to a second disk; the defaults are unchanged.
    • Full local paths only: relative, network (UNC) and device paths, wildcards, alternate data streams, a drive root, the Windows, profiles and install folders, Program Files / ProgramData themselves, and folders whose parent users could replace are refused. A refused value falls back to the default with an error in the log and event 1090; the agent still starts.
    • The chosen folders get the same permissions as the defaults (no inheritance). The updater and the watchdog get the folders from the service; the MSI uses them on an upgrade or repair. Nothing is moved when the setting changes; see docs/configuration.md.
  • Organisational units (district → school). A superadmin builds a tree of units on Ayarlar → Birimler, puts labs into units, and gives users and API tokens a scope.
    • A scoped account sees and acts only on its units' devices, sub-units included.
    • The server enforces the scope on every endpoint (Backend/pops/tenancy.py), so it also holds for API tokens. Out-of-scope ids answer 404, and organisation-wide settings answer 403.
    • Covered: devices, labs, tasks, deployment and winget, file transfer, exam mode, power actions and messages, Vision, logs, inventory, reports and CSV, licences, tickets, scheduled tasks, notifications, update progress and the panel WebSocket.
    • New /api/org-units; org_scope on users and tokens; the unit name shows in the sidebar. Nothing changes until units exist. D-25, migration 0032.
  • Scopes for directory and OIDC accounts. Group mappings can carry a scope (unit ids or "all"). While units exist, a new directory or OIDC account whose mappings set no scope gets an empty scope, so it is never unscoped by accident.
  • Panel sign-in with Active Directory / LDAP and OpenID Connect (Microsoft Entra ID, Google, Keycloak …). A superadmin sets it up under Ayarlar → Güvenlik → Kimlik sağlayıcıları.
    • Directory users sign in with the usual form; with OIDC the sign-in page shows " ile giriş yap".
    • Directory groups (or OIDC group claims) map to İzleyici, Yönetici or Süper admin and to pages, and the mapping is rewritten at every sign-in. Bağlantıyı sına checks the connection and a user's resulting role before saving.
    • Users can be created as, or switched to, directory or OIDC accounts. Local accounts keep working when the directory is down.
    • Turkish how-to: docs/tr/active-directory-ile-giris.md. Migration 0030; new dependency ldap3 (pure Python). Decision D-24.
  • Power actions and messages to the user as their own agent messages (power, user_message), for agents that announce X-Agent-Features: power,message.
    • POST /api/devices/power: shut down, restart, sign out or lock, with a delay of 0–600 s and an optional note of up to 200 characters.
    • POST /api/devices/message: title, text, Bilgi/Uyarı and an optional read receipt.
    • Both are also under /api/v1. Migration 0028.
  • Panel: power and message actions.
    • New Oturumu kapat and Kilitle actions.
    • Kapat and Yeniden başlat get a Gecikme choice (Hemen / 1 dk / 5 dk / 10 dk) and a "Kullanıcıya not" field.
    • New Mesaj gönder dialog with a character counter, Bilgi/Uyarı and "Okundu onayı iste".
    • The task drawer explains -6 (nobody signed in) and -8 (the agent does not support it).
  • Lab-local peer cache for agent updates, server side. Agents that announce peer_cache (X-Agent-Features) are updated one PC per lab first: the seed, an online PC with a known LAN address, wired and most recently seen first.
    • When the seed reports a successful update on its new version, the rest of the lab gets update_agent with peers: 1–3 PCs of the lab that hold the package, at http://<LAN IP>:<port>/pops-cache/<sha256>. The port is 8817, or the one announced in X-Agent-Peer-Cache.
    • A seed that does not reach verified or a result within 10 minutes, or fails, is replaced by the next one; with no seed left, the lab is updated without peers.
    • Agents without the feature, PCs without a lab, labs without a candidate and Linux agents are updated as before. Agents check the signed size and SHA-256 whatever the source.
  • Sistem → Ajanlar: peer cache switch and progress. "Sınıf içinde eşten dağıt" (off by default: while on, a PC holding a package opens a port to its local subnet; POST /api/system/update-peer-cache) and per-lab lines in the update progress ("tohum: PC-12, doğrulandı; 38 bilgisayara eşten dağıtılıyor"). PCs waiting for their lab's seed show "Tohum bekleniyor".
  • API and protocol for the peer cache.
    • deploy-update returns seeds, waiting_for_seed and with_peers, and update-progress returns each device's peer role and a per-lab summary. New setting PEER_CACHE_SEED_TIMEOUT_SECONDS (600).
    • Optional peers in update_agent, an X-Agent-Peer-Cache header and peer_cache in server_info.features. The agent's peer cache contract is in docs/agent.md.
  • Panel: Sistem → Modüller (superadmin).
    • Each module shows its organisation state, its dependencies and the labs that differ, with a switch for the organisation setting. A module whose dependency is off shows as off, with the reason.
    • Each module has a drawer with the organisation setting, what stops while it is off, and Kurum ayarı / Açık / Kapalı for every lab.
    • Before anything turns off, the panel shows what will happen and asks to confirm: dependent modules that turn off too, remote-screen sessions that close, and pending tasks that are denied.
    • Kurulum profili shows the current profile and applies Okul laboratuvarı or Kurum from a preview, optionally deleting lab-specific settings.
    • API: new GET /api/modules/{id}/preview. GET /api/system/install-profile/{name} also returns vision_sessions_closed and tasks_denied and takes reset_labs.
  • GLPI export (Sistem → Entegrasyonlar, superadmin; docs/integrations/glpi.md).
    • Sends computers (name, serial number, UUID, mapped location), installed software, helpdesk tickets and their public replies to GLPI's REST API. It runs on a schedule (daily, every 12 or 6 hours) or with Şimdi eşitle.
    • Off by default. The app and user tokens are stored encrypted and never returned.
    • Records are matched through a link table, then by BIOS serial number and SMBIOS UUID. POps never guesses between several matches, sends only changed records, and never deletes GLPI items except the software links it created.
    • New .env settings GLPI_ALLOW_PRIVATE and GLPI_CA_FILE; migration 0029 (glpi_links); endpoints under /api/system/glpi.
  • Agent: exam mode.
    • exam_mode limits the PC's network to the POps server, DNS/DHCP and an allow list of host names, addresses and CIDR ranges (at most 50). It uses its own firewall rule group, independent of quarantine.
    • The tray shows a banner with the message and the end time. Up to 50 listed apps are closed. The exam ends at until even without the server.
    • The agent answers every exam_mode with exam_state (enabled, since, until) and reports it once per connection to servers that list exam_mode.
    • New capability exam_enabled (MSI EXAM_ENABLED, on by default; the server can only switch it off). Switching it off ends a running exam, and while it is off exam_mode is refused with capability_denied.
    • The agent announces exam in X-Agent-Features. Events 1110–1112.
  • Agent: file transfer.
    • file_push downloads a file only from the server's one-time /api/files/<id>/download?t=… URL. It checks the size and SHA-256 and writes the file only to the public desktop or C:\POpsData\inbox\<date>, under a cleaned name; .lnk/.url/.scr only when allowed.
    • file_pull uploads a file the admin asked for, with a reason. Never from C:\POpsData\secure, from other users' profiles only when explicitly allowed, never over the size limit; symbolic links are resolved first.
    • The result is reported as file_result with outcome done, rejected or failed. The PC user is told in the tray; events 1120 and 1121.
    • New capability files_enabled (MSI FILES_ENABLED, on by default; reported in capabilities). While it is off, a request is refused with capability_denied carrying the transfer_id. The agent announces files in X-Agent-Features.
  • Agent: winget packages. The agent implements winget_install, announced with the new X-Agent-Features: winget header.
    • It installs a winget package as LocalSystem without a shell, only when the local terminal capability and the lab's deploy module are on.
    • It validates the package id and version itself, answers -7 when winget is missing, and reports winget's own exit code.
  • Agent tests read the shared protocol vectors in docs/protocol. Every server-to-agent example is handled, agent messages validate against the JSON Schemas with only documented fields, the signed update vector verifies with the test key only, and the agent-to-server examples match what the agent sends.
  • Docs: Vision on the secure desktop (docs/vision.md).
    • It covers what happens during UAC prompts and on the lock and sign-in screens, and why they are not captured today.
    • It sets out the design a safe implementation would need (a SYSTEM helper in the user's session, a SYSTEM-only pipe, consent and audit, a threat model, a lab test plan) and the decisions left for the owner.
  • Fuzzing with Atheris. Four targets in fuzz/: the agent WebSocket handler, the request models, the signed release manifest and the notification settings. A fuzz CI job runs each for 60 s; see docs/fuzzing.md.
  • File transfer (server and panel). An admin can send a file to PCs and fetch a file from a PC. In the device panel, Diğer has Dosya gönder (pick or drop a file up to 200 MB, destination Ortak masaüstü or POps gelen kutusu, a reason, and "çalıştırılabilir dosyaya izin ver" for .lnk/.url/.scr; with upload progress) and Dosya al (full path, largest size 10/50/200 MB, a reason; a superadmin can also allow other users' profiles). Dosya gönder is also in the action bar menu of Cihazlar and Sınıflar for the selected PCs; PCs that are off or have no file transfer are skipped and listed. The panel's new Dosya aktarımları list shows direction, name, size, status, who, when and why, with İndir for a fetched file. Server: POST /api/files/push, POST /api/files/pull, GET /api/files, GET /api/files/{id}/content, and for agents GET /api/files/{id}/download and POST /api/files/{id}/upload; agent messages file_push, file_pull and file_result, capability files_enabled in capabilities, feature file_transfer in server_info (schemas in docs/protocol). Each PC gets its own transfer and a one-time token (1 hour, stored as a hash) bound to that PC; the agent endpoints always need the device secret, and a wrong, used or expired token or another PC's secret gets 404. Sending, fetching and downloading need an admin panel session (API tokens are refused); every send, request, received file, download and agent result is in the hash-chained audit log (metadata only, never the content), and the tray's activity history shows that a file was sent or requested. Fetched files are kept 7 days, sent files until they are downloaded; files live in Backend/transfers (POPS_FILES_DIR, Docker volume transfers), are not served and are not backed up. New module Dosya aktarımı (files, on by default). Migration 0025_file_transfers (table file_transfers, clients.cap_files_enabled). The reverse proxy templates allow 200 MB bodies on the two upload paths. The Windows agent side comes with the agent team's release; until an agent reports files_enabled, the server sends it nothing.
  • Exam mode (sınav modu) per lab. In Sınıflar → Sınıf işlemleri → Sınav modu…, an admin sets:
    • the allowed addresses (domain, IP or CIDR, at most 50);
    • a duration (40/80/120 min or an end time, at most 8 hours);
    • a tray message, programs to block and a reason.
    • The lab's agents then block all traffic except the POps server, DNS, DHCP and the allow list, and they leave exam mode at the end time even when offline. While an exam runs, the lab shows the time left and every PC's state: in exam, left, unreachable, not supported (old agent), waiting or refused. The exam is re-sent on reconnect and to PCs moved into the lab. It ends when an admin ends it, at the end time, when the lab is deleted or when the exam module is switched off. A PC that leaves early raises an audit entry and a notification. API: POST/DELETE/GET /api/v1/labs/{lab}/exam and GET /api/v1/exams. Agent protocol: exam_mode and exam_state. Migration 0024.
  • Linux agent for Pardus and Debian (first version). The pops-agent_<version>_all.deb package runs on Debian 12, Pardus 23+ and Ubuntu 24.04. It uses the distribution's python3-websockets and python3-cryptography, with no venv or pip. The agent:
    • enrolls with the usual token and keeps a root-only device secret;
    • talks TLS only, optionally pinned to the school CA;
    • reports DMI identity, hardware inventory, installed packages and the signed-in user;
    • runs remote commands as root with /bin/sh, under the Windows limits, and keeps results until they are acknowledged;
    • applies the capability policy (switched off locally, or by the server, which can only switch off);
    • updates itself from the signed release, with rollback.
    • The panel shows each device's system (Windows/Linux), and Uzak komut knows Linux targets. Not yet built: screen view, quarantine, tray. See Agent-Linux/README.md and decision D-22.
  • Deploy: winget packages. Dağıtım → Paket yükle → winget paketi adds a package from the winget community source. Pick it from a catalog of 69 school and office apps (searchable by name, publisher and category), or type any winget id with an optional version. winget installs it as SYSTEM, silently, for all users.
    • The new step type WINGET ({"id", "version"}) also works through POST /api/v1/tasks.
    • Ids and versions are limited to letters, digits and . + _ -. They are checked on the request, before dispatch and by the agent, which starts winget without a shell.
    • The server sends the new agent message winget_install only to agents that announce X-Agent-Features: winget. On other PCs the step is refused and nothing is sent; the run dialog warns how many selected PCs are affected.
    • winget's "already installed" and "restart pending" codes count as done, and common winget errors are shown in plain words.
    • Endpoint GET /api/v1/deploy/winget/catalog. Migration 0027. Decision D-23.
  • Vision v2 on the server and in the panel.
    • Server: binary screen frames on /ws/vision (18-byte header + JPEG, at most 2 MB, checked with the agent's rules; bad frames are dropped and counted in /metrics). They are relayed only to the session holder's Remote screen page, under the tunnel's own device ID.
    • Slow viewers drop frames rather than queue them: a full frame replaces what is waiting, and regions beyond 32 frames or 4 MB are dropped until the next full frame.
    • Viewer commands: screen selection (monitors, select_monitor) and quality, scale and frame rate (set_quality) are forwarded only from the admin holding the session.
    • Panel: the Remote screen page draws regions onto a canvas. It has a screen picker (one screen or all side by side), image quality, scale, a frame rate up to 10 and a live fps and kbit/s readout. Mouse input maps to the shown screen.
    • Compatibility: server_info lists vision_binary and vision_clipboard. Older agents keep sending JSON frames.
  • Vision clipboard. Text, up to 64 KB, both ways. Only the admin whose "ask the user" session the PC user accepted can use it, and never in a mandatory session. Every transfer is audited (direction, length, admin, time, never the text) before the text is passed on.
  • JavaScript unit tests (tests/unit-js/, Node's built-in test runner, no dependencies) for the shared panel helpers (translation and plurals, escaping, time and duration formats, error texts) and the page-script helpers (CSV formula guard, page numbers, update status). CI runs them in the Dashboard checks job (node --test tests/unit-js/*.test.mjs); how to add page scripts and tests is in docs/dashboard.md.
  • CI: documentation states the current versions. tools/check_docs_versions.py runs in the Version consistency job and fails when README, ROADMAP, docs/, SECURITY.md, CONTRIBUTING.md, the issue templates or the release notes call a version other than VERSION the latest, call an already released version the next one, or name such a version in README or ROADMAP at all. It also fails when they give a Python or PostgreSQL minimum other than the one in Backend/requirements.txt and the CI migrations job, or a .NET runtime requirement that does not fit the self-contained .NET 10 agent. Sentences about already released versions count as history. A line can opt out with docs-versions: ignore.
  • Panel: English for Sistem, Ayarlar, Uzak komut and the sign-in page. Every panel page can now be shown in English. When no language has been chosen yet, the sign-in page follows the browser's language and stores the choice. A Türkçe / English switch under the form works without JavaScript and keeps a pending 2FA step.
  • Panel: server texts with values are shown in English too. Examples: a scheduled task's last result ("Queued for 3 devices"), who created a task from a schedule ("admin (scheduled #1, run by hand)") and notification titles such as agent update results. Stored task titles show their scope in English ("Shut down · 3 selected computers"); lab and computer names stay as typed.
  • Agent: updates download with BITS. The update MSI is fetched with BITS and resumes after network drops and restarts. A download still running after 15 minutes continues with the next update command. When BITS is unavailable the agent downloads directly as before. The signed size and SHA-256 are checked either way. A design for a lab-local peer cache (one download per lab) is in docs/design/peer-cache.md.
  • Agent protocol as JSON Schema with shared test vectors. docs/protocol/ defines both agent WebSockets message by message (9 agent → server and 20 server → agent messages, plus the signed release manifest), with 51 example messages that the backend checks and the agent tests load (docs/protocol/AGENT_TESTS.md). Rules for a second agent: agent messages carry type, server messages action; what exists today is protocol 1; optional behaviour is negotiated with server_info.features; unknown fields and messages are ignored. server_info now also sends "protocol": 1. Backend/tests/test_protocol.py runs the real WebSocket handlers, task queue and endpoints with a fake database and fake sockets and fails on any message that does not match its schema.
  • Public read-only demo (deploy/demo/): a Compose stack with PostgreSQL, the backend and a fake fleet on an internal network and the panel on 127.0.0.1:8180; seed.py (demo viewer, enrollment token, two weeks of history, licences, "POps Demo Okulu"); reset.sh (nightly wipe and reseed); setup-host.sh (owner install with a 03:30 systemd timer); Virtualmin and .htaccess steps.
  • tools/demo_fleet.py: 50 fake school PCs in four labs plus two teacher PCs. They enroll with a token, follow a school calendar, report inventory, software and Windows Update status, raise DNS violations and help-desk tickets, and answer every command with a canned reply. They never execute anything.
  • Demo accounts: backend POPS_DEMO_USERS makes the listed accounts read-only (every request other than GET, HEAD and OPTIONS returns 403 "Demo hesabında değiştirilemez", including the account's own password and 2FA); panel POPS_DEMO_LOGIN shows the demo credentials on the sign-in page. Backend/tests/test_demo.py checks every write endpoint in the route table.
  • Agent: Vision v2. When the server supports binary frames (vision_binary), the tray captures with DXGI Desktop Duplication (GDI where it is unavailable) and sends only changed regions after a full frame, as binary WebSocket messages with an 18-byte header. The viewer can pick a screen or all side by side and set quality, scale and frame rate; the agent lowers quality instead of queueing when the connection is slow. Text clipboard (up to 64 KB) is shared both ways only in a session the PC user accepted; the tray says so and the event log keeps only direction and length (event 1100). Older servers keep the previous JSON frames.
  • API: versioned /api/v1 for integrations. Every HTTP endpoint under /api/... also answers under /api/v1/... (same handler, permissions and response; one middleware rewrites the prefix before routing). /api/v1 is the stable surface for scripts and other systems; plain /api stays for the panel and agents. WebSockets are not versioned. Rate limits and request metrics count both forms together (decision D-21).
  • API tokens for automation. A superadmin creates, lists and revokes them in Ayarlar → Güvenlik → API jetonları (or GET/POST /api/v1/tokens, DELETE /api/v1/tokens/{id}). A token is pops_ + 32 random bytes, shown once; only its SHA-256 hash and an 8-character prefix are stored (migration 0022, table api_tokens). Role Görüntüleyici (viewer, GET only) or Yönetici (admin), never superadmin, with an optional validity in days. Send it as Authorization: Bearer pops_...; it needs no X-Requested-With header (cookie sessions still do). Tokens cannot use superadmin endpoints, manage users or tokens, use 2FA, or open remote screen and remote input. Expiry and revocation apply on the next request. What a token does is recorded as token:<name> (tasks, audit log, event log); creating and revoking tokens is audit-logged.
  • API: REST names for the RPC-style endpoints under /api/v1, calling the same handlers: labs (GET/POST /api/v1/labs, PATCH/DELETE /api/v1/labs/{lab_name}, main PC, layout, wake), devices (move, rename, wake, quarantine, bypass code), settings (task concurrency, auto-enroll), tasks, packages, files, agent policies, scheduled tasks, licences and tickets. The full list is in docs/api.md.
  • OpenAPI specification in the repository: docs/openapi.json (paths under /api/v1), generated by python tools/export_openapi.py; CI fails when it is out of date. The running server still serves no /docs or /openapi.json.
  • Panel: English interface — Dağıtım, Politikalar and Uzak ekran (Deploy, Policies, Remote screen): packages and the run dialog, policy editing and the sample privacy notice (a natural English text, not a word-for-word copy), the Vision wall, consent and session dialogs, and diagnostics. Data sent to the server, such as task titles, stays Turkish.
  • Panel: English interface — Kayıtlar, Raporlar and Destek talepleri (Logs, Reports, Help desk), including filters, drawers, dialogs, licences and Windows updates. The Logs and Hardware CSV exports use the interface language for column headers and the file name; the server's report CSVs keep their language-neutral column names.
  • Panel: English interface — Cihazlar, Sınıflar and İşlemler (Devices, Labs, Jobs): the device list and its CSV export, the lab map and layout editor, lab actions, and jobs and scheduled tasks with their drawers, dialogs and messages. Lab names, task titles and other data sent to the server stay as entered.
  • Panel: end-to-end tests in a real browser (Playwright, Chromium), run by the new CI job "Panel end-to-end". Every page is opened at desktop (1440×900) and phone (390×844) width and must load without console errors, without requests to any other host and without horizontal scrolling on a phone. The main flows are covered too: sign-in and sign-out, device search and details, creating, renaming and deleting labs, task status, paging and filters on Kayıtlar, the Sistem tabs and charts, the organisation name and queue limit in Ayarlar, and what a viewer may see. The tests start their own backend and panel on an empty test database; see docs/testing.md to run them locally.
  • Agent: log retention. The service deletes C:\POpsLogs\POps_*.log and msi-*.log older than 30 days, and the oldest ones while the folder holds more than 200 MB, at start and daily. Today's log is never deleted; the folder's permissions are unchanged.
  • Agent: reports update stages (update_progress): received, downloaded, verified, updater started, "Windows Installer busy, waiting (n/5)" and installing, plus the reason when it refuses an update. The updater writes its stages to C:\POpsData\update-progress.json and the service forwards them; only servers that list the feature receive them. The panel shows them under Sistem → Güncellemeler (server side in this release).
  • Panel: agent update stages. Between sending an agent update and its result, Sistem → Güncellemeler shows per PC the stage the agent reports and how long ago: Alındı, İndirildi, Doğrulandı, Kurulum başladı, "Windows Installer meşgul, bekleniyor (2/5)", Kuruluyor, or "Reddedildi: ". Agents report them with the new update_progress message (see docs/api.md); agents up to 0.1.21 send none and show "Kuruluyor" as before, with "Ajan ilerleme bildirmiyor (eski sürüm olabilir)" after 3 minutes. An agent's refusal (bad signature, version not newer, download failed, …) now ends the update in the panel with its reason, an audit entry and a notification instead of waiting 20 minutes for silence. POST /api/system/update-progress also returns sent_at, stage, detail, attempt, of, stage_at and now. Migration 0023_update_progress.sql. Older servers ignore the message.
  • Docs: Turkish guides for schools in docs/tr/: "Neden POps?" (what POps solves and what it does not, KVKK and transparency, cost, working without internet), "POps ve Veyon birlikte" (who does what, installing both on the same PCs without port conflicts, a typical day; with a light and dark figure from assets/readme/make_figures.py), "Pilot okul kurulumu" (a checklist for one lab of 10 PCs, what to measure for 2–4 weeks, a feedback form) and a case study template. README.tr links them; README lists them as "Turkish guides for schools".
  • Docs: code signing policy (docs/code-signing.md) for Authenticode signing through the SignPath Foundation: what is signed and never signed, how a release is built and approved, roles, key custody and how to verify a download. The application is still to be made; the binaries are not Authenticode-signed yet.
  • Docs: GLPI export design note (docs/integrations/glpi.md): sending inventory and helpdesk tickets to GLPI, with field mappings, sync direction, conflicts and authentication. ROADMAP lists it under Next → Integrations.
  • Docs: "Linux and Pardus" in README and README.tr; ROADMAP names Lider Ahenk integration as an alternative to a Linux agent.
  • GitHub: issue forms and contributor onboarding. Bug report and feature request forms replace the Markdown templates; questions go to Discussions and security reports to SECURITY.md. The pull request template has a checklist (tests, lint, docs, CHANGELOG, no secrets, transparency), and CONTRIBUTING has a "Good first issues" section.
  • Panel: English interface (in progress). Türkçe / English at the bottom of the sidebar switches the panel language for this browser (cookie pops_lang, kept for a year; the default stays Turkish). Translated so far: the sidebar, search, job center and notifications, page titles, dialogs, toasts and error messages (including the most frequent server messages), relative times and dates, the device actions and the device detail panel, task states and failure reasons, the event wording shown on Kayıtlar, and Kontrol merkezi. Other pages are being converted and show Turkish text until then. Translations live in Dashboard/lang/en/ (one file per page); CI checks them with tools/i18n/check_i18n.py. How it works and how to convert a page: docs/i18n.md.

Changed

  • Backend code layout. /ws/agent is split into small functions with one handler per message type, and the system endpoints moved from system_routes.py to the package pops/routers/system/. system_routes still works as an import name. No behaviour or API change.
  • Counts across all workers. Diagnostics, /metrics and the overview count connected agents and panels across all workers; diagnostics shows a cluster block when Redis is on.
  • New backend dependency: redis 7.4.1.
  • CI: the agent's nullable baseline only shrinks. tools/check_nullable_baseline.py (CI's version job) compares the agent files that carry #nullable disable with the fixed list in tools/nullable_baseline.txt (53 files: 41 in the agent service, 9 in the shared library, 2 in the updater, 1 in the MSI custom actions).
    • A file outside the list that turns nullable off fails CI; new files must be nullable-clean.
    • A listed file that is gone or no longer has the line fails too, until its entry is removed, so the list shrinks as files are cleaned. A product project without <Nullable>enable</Nullable> also fails.
  • Agent: a mandatory Vision session that starts while the PC is locked is no longer silent (owner decision 5, docs/vision.md).
    • The countdown still runs on the hidden desktop, but the tray records that the user's desktop was not on screen when the session started.
    • As soon as the user's desktop returns, the tray shows a balloon and a banner at the bottom of the screen that the user cannot close ("Ekranınız Bilgi İşlem tarafından izleniyor (oturum bilgisayarınız kilitliyken başladı)", with the admin, the reason and the session ID). The banner stays until the session ends, and no picture of the user's desktop is sent before it is on screen.
    • The PC writes event 1150 to the Windows event log and posts an agent.vision_locked_start entry to the server's event log (existing POST /api/logs; no protocol change).
    • Routine (consent) sessions still wait for the user's answer.
    • The tray now also stops capturing when it loses its connection to the service.
  • Real timestamps. The older tables' text dates become TIMESTAMPTZ (migration 0031), read in the server's time zone (POPS_TZ, else the system zone). The API returns ISO 8601 with an offset; CSV keeps readable local time. Audit chains written before the update still verify.
  • The deploy backs up the database before a rewriting migration. pops-deploy-backend runs pg_dump before any migration marked -- pops: dump-before. It keeps at most 3 dumps, respects KEEP_BACKUPS and checks free space first.
    • Upgrading: reinstall Installer/server/pops-deploy-backend to /usr/local/sbin/pops-deploy-backend before deploying. 0031 rewrites large tables (logs, tasks, audit), so the update takes longer.
    • To roll back after this migration, restore that dump together with the old code (docs/deployment.md).
    • If the old text dates were written in a zone other than the server's, set POPS_TZ before migrating.
  • New agents get Kapat and Yeniden başlat as power messages, not as a shutdown command; older Windows and Linux agents still get the old command. Sign-out, lock and messages are refused with -8 for agents without the feature, and nothing is sent to them.
  • Power and message tasks are queued apart from commands. They don't use up the concurrency limit, don't depend on the remote command module, and expire after 15 minutes if they are not sent. Mesaj gönder no longer uses the Windows msg command.
  • API: unknown fields in a request body are refused (422, extra_forbidden with the field name) on every panel and integration endpoint.
    • Before, most endpoints dropped them silently and ran with defaults: a misspelt lab on POST /api/modules/{id} turned the module off for the whole organisation.
    • Bodies sent by agents still ignore unknown fields, so an older server keeps a newer agent's data.
    • /api/remote_input still accepts its top-level input fields.
  • Audit log: module changes also record the previous setting, and install-profile changes record the modules they changed and the lab settings they deleted.
  • Agent: nullable reference types. The agent service, the shared library and the MSI custom actions now build with nullable reference types on, as the tray, watchdog and updater already did.
    • The 49 files that were not nullable-clean on 5 October keep a #nullable disable line: a baseline that only shrinks, like the warnings list in Agent/.editorconfig.
    • New files must be nullable-clean: a nullable warning fails the build, now also in the custom actions. No behaviour change.
  • Agent tests are named after the component they test (for example HardwareBindingTests.cs, ResultSpoolTests.cs) instead of the review round that added them. No test was added, removed or changed.
  • Agent: Worker.cs split, step a0. A pure move (1919 → 1717 lines) toward one handler per message type; the design is in the PR (#122). No behaviour change.
  • Backend tests run under pytest. python -m pytest runs Backend/tests (configuration: the root pytest.ini; pytest comes from the hash-locked .github/requirements/pytest.lock). test_units.py is a pytest module, one test per function, so a single test can be selected (-k) and JUnit output written (--junitxml). The older script tests are collected by Backend/tests/conftest.py without being imported: each runs in its own process as one test, a failure lists the checks that failed, scripts that need a running server carry the integration marker and keep the order CI used, and a new script needs no registration. CI and Backend/tests/run_local.sh call pytest instead of listing the scripts; how to run, select and add tests is in CONTRIBUTING.md.
  • Agent updates per platform. A signed release carries the MSI and the .deb. fetch-release stages both; deploy-update copies both to /updates/ and skips PCs whose platform has no package (skipped_no_package). Migration 0026 adds clients.platform.
  • Agent results: a [REDDEDİLDİ] result with exit code -5 now marks the task Denied directly.
  • /api/devices returns agent_features. Closing the Dosya dağıtımı module in a lab denies its pending winget tasks.
  • The Vision tunnel survives a single bad message. It no longer closes on one message the server cannot process; as on /ws/agent, 20 in a minute close it.
  • Panel and API: the device list is cheap to poll.
    • GET /api/devices returns a weak ETag, and a matching If-None-Match gets 304 without a body.
    • ?since=<version> returns only what changed: changed rows, removed IDs and seen (last_seen refreshes). It returns the whole list with full: true when the change log (10 minutes or 5,000 changes) cannot answer.
    • Panel sockets opened as /ws/panel?topics=devices get {"type": "devices_changed", "version": n} at most once a second.
    • The panel's device pages fetch changes as soon as they are told, and poll every 30 s instead of 5 s while the socket is open.
    • With 2,000 agents, an open tab now pulls 76 KB a minute instead of 14.4 MB when nothing changes. A device going offline shows within about a second. Measurements: BENCHMARKS.md.
  • Panel: page scripts. The JavaScript of Raporlar and Kayıtlar moved out of the PHP pages into Dashboard/assets/pages/reports.js and logger.js. The pages have no inline script, and PHP values reach the script through a JSON block. Behaviour is unchanged. check_html_sinks.py and check_i18n.py --missing also cover assets/pages/.
  • Docs: README and README.tr match main. They link the public read-only demo (demo.pashacore.com.tr, user and password demo) and have feature rows for API v1 and API tokens, the organisation's name and logo, both panel languages, the Sistem charts and agent log retention. They also describe the stable and preview self-update channels, agent update stages and BITS downloads, and Vision v2 as agent side only, with the panel viewer still to come. The test and supply-chain summary and the roadmap paragraph are current; the paragraph no longer calls 0.1.14 the next round.
  • Docs: ROADMAP. API v1, the panel end-to-end tests, the English interface, the protocol schema, the demo and this week's agent work are listed as done. Code signing through the SignPath Foundation is the first item under Next.
  • Docs: positioning.
    • POps is Apache 2.0 (OSI-approved), while Tactical RMM's licence is source-available and restricts commercial services.
    • POps works with GLPI instead of competing with it; the export is a design note.
    • A section on Pardus, ETAP and Lider Ahenk.
  • Docs: release channels. CHANNEL=release is the stable channel: the newest release tag, weekly releases, SSH-signed tags. CHANNEL=main is the preview channel, for test servers. See docs/self-update.md and docs/deployment.md.
  • Docs: agent contributors. CONTRIBUTING describes the agent warnings gate and the Agent/.editorconfig baseline.
  • Panel: Vision diagnostics and Remote command quick-action titles are translated on every page that lists jobs, not only on the page that sent them.
  • Agent: warnings gate. The agent, tray, watchdog, updater and shared library build with the .NET 10 recommended analyzers and treat warnings as errors. The warnings that existed on 5 October are listed per file in Agent/.editorconfig, and any new one fails the build. 60 warnings were fixed, including all of them in the service's Worker.cs and the tray pipe code.
  • Panel: browser configuration object renamed to window.POPS_API (assets/pops_config.js). Upgrading: the file ships with the panel and is replaced on update; if you edited it locally, rename the global in your copy.
  • API: the old RPC paths are deprecated but keep working (/api/create_lab, /api/move_pcs, /api/set_concurrent_limit, /api/deploy_orchestration, … — the list is in docs/api.md); the panel still uses them. Unlike /api/set_main_pc, the REST name does not toggle: PUT sets the main PC, DELETE clears it.
  • API: task_sequence. POST /api/deploy_orchestration (and POST /api/v1/tasks) accepts task_sequence as well as taskSequence; sending both is 422.
  • Server: the large-upload rule of the reverse proxy also covers /api/v1. The nginx and Apache templates match ^/api/(v1/)?(upload|files|system/upload-release)$. Upgrading: an existing server keeps its own site configuration; to upload files over 8 MB through /api/v1, change that line to the new pattern. Plain /api/upload is unchanged.
  • Accounts: user names can no longer start with token: (reserved for API tokens in the logs).
  • CI: shellcheck also checks docker/dashboard-entrypoint.sh and Backend/tests/run_local.sh (#82, thanks @GhostCoder6969 — the first outside contribution).
  • An agent update is not sent twice. POST /api/system/deploy-update skips a PC whose update to the same version was sent less than 15 minutes ago, or that reported a stage in that time (the agent ignores a second command while its update lock is fresh), and lists it in already_pending; the panel says "N bilgisayara zaten gönderildi, kurulum sürüyor" and keeps following it. The 20-minute "no result" notice counts from the last reported stage.

Fixed

  • Agent: an unreadable-hardware heartbeat follows the protocol schema. When the hardware could not be read, the heartbeat now carries an empty dna_payload object instead of null.
  • Agent: Vision shows a notice while the secure desktop is up.
    • During a live session, a UAC prompt, Ctrl+Alt+Del, the lock screen or the sign-in screen no longer leaves the viewer on a frozen picture. The tray sends a "Güvenli masaüstü etkin" picture instead, every 5 seconds, and resumes the live picture when the user's desktop returns.
    • With Vision v2, capture no longer stops for good after the first UAC prompt or lock. The secure desktop itself is still neither shown nor controlled.
  • Wrong-typed fields in an agent's first message are ignored, as the protocol rules say. A wrong-typed dna_payload, hardware, capabilities or hostname, or a number like 1e999 in agent_health, no longer closes the connection (1011).
  • A malformed signed release manifest is rejected with 400 instead of failing with 500. This covers a manifest that is not a JSON object and one with malformed artefact entries.
  • The agent policy drops domain entries that contain whitespace or control characters. A NUL used to make saving the policy fail.
  • Quarantine on a Linux PC answers 409 instead of marking it locked. An unsupported quarantine reported by an authenticated Linux agent clears the pending lock.
  • Docs: stale facts. README no longer says there are no API tokens or that the panel is Turkish only. The FAQ, docs/dashboard.md and the Turkish "Neden POps?" guide no longer say English covers part of the panel. Decision D-15 no longer implies the panel loads fonts and icons from CDNs. CONTRIBUTING no longer lists the removed POpsVision source. docs/security.md lists the sign-in branding endpoints among those reachable without signing in. The pilot guide writes the MSI log to C:\Windows\Temp. Troubleshooting shows the agent's current message for an unreadable configuration.
  • Docs: agent behaviour that was only in the CHANGELOG is now in docs/agent.md and troubleshooting: update stages and update-progress.json, log retention in C:\POpsLogs, event 1090 for an unreadable configuration, and the tray pipe that stays open while the server is unreachable. SECURITY.md and the architecture pages mention API tokens and hash-locked dependencies.
  • Kontrol merkezi: viewers see "Göster", not "Güncelle", for old agents. A viewer cannot update agents. The tile and the attention item already opened the device list.
  • Docs: Vision channel authentication. docs/api.md said /ws/vision accepts an enrollment token; it accepts only the device secret.
  • Apache template: WebSocket rule. Installer/server/apache-htaccess.example forwarded /ws/panel as /ws/ because the path came from the wrong condition. Upgrading: if your Dashboard/.htaccess has the Upgrade condition after the /ws/ path condition, swap them (sites with a vhost ProxyPass /ws/ were not affected).
  • Agent: an unreadable configuration is reported. When appsettings.json cannot be read or has no valid ServerUrl, the agent still falls back to http://127.0.0.1:8000, but now logs an error, writes Windows event 1090 and shows "POps - yapılandırma okunamadı" with a warning icon in the tray instead of looking healthy.
  • Panel: on phones the scheduled tasks table scrolls inside its card instead of widening the page.
  • Panel: viewers no longer see pages they cannot open. A viewer whose permission list still contained Dağıtım, Uzak komut or Ayarlar (for example an admin demoted to viewer) saw those pages in the menu and the Ctrl+K search but got "Yetkisiz Erişim". They are now hidden for viewers.
  • Panel: İşlemler no longer asks for the admin-only scheduled-task list as a viewer every 30 seconds (it only produced 403 errors in the browser console); the Zamanlanmış tab is hidden for viewers.
  • Docs: stale facts. ROADMAP no longer lists pentest findings F9 and F11 as open (closed in 0.1.4 and 0.1.13); the FAQ gives the measured capacity (5,000 agents on one process) instead of 1,000; the MSI install examples write their log to C:\Windows\Temp, which always exists, instead of C:\POpsLogs, which a fresh PC does not have yet (msiexec would refuse with 1622); docs/api.md no longer links a design note that is not in the repository.
  • Agent: offline bypass works while the server is unreachable. The tray pipe stays open for the whole life of the service; before, it was closed after every lost server connection until the next attempt, and an offline code typed in the lock screen during that wait (up to a minute, 10 minutes after a 4409) was dropped silently. The lock screen now says when a code could not reach the service. Needs the agent from the next release.
  • Docs: a broken link to the agent setup section in CONTRIBUTING; getting-started no longer says the panel loads fonts and icons from CDNs.

Security

  • Agent: update packages may come from lab peers. The signed manifest still comes only from the agent's own server. The package may also come from PCs in the same subnet that the server lists, and is accepted only if its size and SHA-256 match the signed manifest. A PC listens on TCP 8817 (local subnet only, at most 2 hours) only after the server asked for it with peer_cache: true. Switch it off per PC with PEER_CACHE_ENABLED=0.
  • Agent folders: owner checked on every start. If the data, secure or log folder is owned by another account (for example a student who created C:\POpsData before the agent was installed), the service and the MSI set the owner to SYSTEM when they lock the folder; before, that account could open the permissions again.
  • A scoped account cannot reach another school's devices. That holds for lists and direct ids, through the panel, the REST API, API tokens and the panel WebSocket, including the device-list version and devices_changed.
  • The peer cache is off by default, and a PC listens only when told. update_agent carries "peer_cache": true only while the setting is on, and only to the PCs that take part in a staged rollout. Without it the agent keeps no package, starts no cache server and opens no port. The README's "no inbound ports on PCs" holds unless an admin turns the peer cache on.
  • Directory and OIDC sign-in are verified end to end.
    • Directory sign-in works only over LDAPS or StartTLS, with the certificate and host name verified.
    • OIDC uses the code flow with PKCE, state and nonce. The ID token's signature (asymmetric algorithms only), issuer, audience, expiry and nonce are checked.
    • A one-time ticket bound to the starting browser prevents login CSRF, and redirect targets come only from the configured redirect URI.
  • Local accounts are protected from directory and OIDC identities. Local accounts never reach the directory, the first local superadmin stays local, and a directory or OIDC identity cannot take over a local account. Disabled, removed or unmapped directory accounts are refused and their sessions end.
  • Provider secrets stay secret. They are encrypted at rest, never returned, and not reused when the server, connection or CA changes. Every change is audit-logged without secrets, and 2FA still applies.
  • Notes and message texts never reach a shell. They go as JSON fields. The server strips control and bidirectional characters, and the note for old agents keeps only characters that are safe inside quotes.
  • Messages need a person. They require a panel session (API tokens get 403), and audit records store only the length and first 60 characters of a note or message.
  • CI installs every Python tool from hash-locked files (.github/requirements/*.lock, generated and checked with tools/backend_lock.sh, refreshed on Dependabot PRs). No unpinned pip install is left in the workflows.
  • The Docker base images are pinned by digest (python:3.12-slim, php:8.3-apache). Dependabot proposes the new digest of the same tag weekly.
  • Workflow tokens are read-only at the top level. CodeQL's security-events: write moved into its job, and the remaining write grants say why they are needed.
  • vision_rejected is forwarded to the panels with the connection's device ID. Before, any connected agent could end an administrator's remote-control session on another device.
  • An agent string containing \u0000 or a lone surrogate no longer breaks database writes. Before, such a task result was never stored and was resent forever, and one such heartbeat dropped the batched heartbeats of every device.
  • Exam mode is a network restriction and a notice, not proctoring. A local administrator can switch it off (the exam capability), and the panel then shows the PC as left or refused. See docs/security.md "Exam mode".
  • Vision commands and frames are bound to the session and the device. Viewer commands and clipboard text reach the agent only from the admin holding the session. Binary frames, screen lists and clipboard text are always filed under the tunnel's authenticated device.
  • Remote screen frames are filed under the tunnel's own device. /ws/vision forwarded the hw_id the agent put in a frame, so an enrolled agent could put frames into another device's tile for an admin who had a session open on it. The server now sets it to the device that authenticated the tunnel.
  • Backend dependencies are hash-locked. Backend/requirements.lock pins every dependency, indirect ones included, with the SHA-256 of each file; install.sh, pops-deploy-backend, the Docker image and CI install it with pip install --require-hashes, so a changed file on the package index is refused before anything is installed. Checkouts without the lock fall back to requirements.txt. Regenerate it with tools/backend_lock.sh after changing requirements.txt (CI checks it); Dependabot PRs get it from the lock-refresh workflow. A native server uses the lock once /usr/local/sbin/pops-deploy-backend is updated.