Модел на данните
Първоизточник: entrosity-axis.backend/db/migrations/*.sql и заявките в
entrosity-axis.backend/db/queries/*.sql.
PostgreSQL 18. Конвенции:
- Първичните ключове са
uuid(UUIDv7, генерирани в Go, така че се сортират по време). - Всички времеви отметки са
timestamptz;created_at/updated_atсе поддържат от приложението. - Изброимите типове са
textс ограниченияCHECK(по-лесни за разширяване от enum типовете на Postgres). - Всяка таблица, принадлежаща на тенант, има
tenant_id uuid NOT NULL REFERENCES tenants(id)иtenant_idкато първа колона във всеки индекс, използван за списъци. - Меко изтриване (
deleted_at) само заdevicesиpackages(историята трябва да се запази); всичко останало се изтрива окончателно сON DELETE CASCADE, където това е безопасно. - Разширения:
citext(имейли),pg_trgm(търсене),pgcrypto(незадължително),pg_stat_statements, когато е предварително зареден; собственият набор от миграции на river за опашката за задачи. - Защитата на ниво ред (row-level security) (0007) е включена и наложена принудително за всяка таблица, принадлежаща на тенант. Ролята на приложението
rmm_appвижда само редовете на тенанта на сесията (app.tenant_id) или всички редове приapp.bypass. Редовете сtenant_id IS NULLса споделени и могат да се четат от всички. Вижте Множество тенанти. - Препратките между таблици на тенантите са съставни
(tenant_id, x_id) → parent(tenant_id, id), така че ред никога не може да сочи към друг тенант.
Идентичност и тенанти
tenants
| колона | тип | бележки |
|---|---|---|
| id | uuid PK | |
| name | text | показвано име |
| slug | citext UNIQUE | използва се в URL адреси/имейли |
| status | text | active / suspended |
| settings | jsonb | настройки на ниво тенант, управлявани от Axis (часова зона по подразбиране, канал за обновяване, отмени на сроковете за съхранение) |
| created_at, updated_at | timestamptz |
sites
Физическо/логическо местоположение в рамките на тенант; един AD домейн обикновено съответства на един обект. timezone (IANA) се използва за прозорците за поддръжка.
users
Копие на потребителите на Entrosity Hub (със същите идентификатори),
поддържано от синхронизацията за атрибуция (одитен журнал, задачи,
изпълнения на скриптове) и роли. Потребителите са глобални редове; към
кои тенанти принадлежи даден потребител и каква е ролята му във всеки от
тях, се съхранява в tenant_memberships. Axis не съхранява
идентификационни данни, сесии или броячи на влизания.
| колона | бележки |
|---|---|
citext, уникален сред потребителите, които не са deleted | |
| display_name | както в Hub |
| status | active / disabled / deleted (изтритите потребители остават, за да запазят миналите действия своя автор) |
| is_global_admin | администраторите на платформата в Hub: всички права във всеки тенант |
| created_at, updated_at |
Защита на ниво ред: всяка сесия може да чете потребителите; само глобалният обхват (синхронизацията с Hub) може да ги записва.
tenant_memberships
По един ред за потребител и тенант: role (tenant_admin / technician /
viewer) и предпочитанията на потребителя за имейли за аларми в този
тенант (notification_prefs, alert_digest_at, а от 0014 и маркерите за
доставка offline_rollup_at, resolved_mailed_at, quiet_held_since;
синхронизацията с Hub ги запазва). Защитата на ниво ред го ограничава до
тенанта.
platform_sync_state, platform_sessions_revoked, platform_step_ups_used
ETag на последната моментна снимка на достъпа, приложена от Entrosity Hub;
приключилите сесии на платформата (токените им се отказват преди да
изтекат; пазят се един час); и идентификаторите (jti) на вече
използваните step-up токени, така че всеки от тях да потвърждава само едно
действие на която и да е реплика (изчистват се след изтичане).
Агенти, конектори, устройства
enrollment_tokens
kind е agent или connector. max_uses NULL = неограничено. Токените се показват веднъж при създаването; след това се пази само token_hash. Агентите, регистрирани с токен, записват enrollment_token_id за одит.
devices
Централната таблица. По един ред за всеки управляван или открит компютър.
| група | колони |
|---|---|
| идентичност | hostname, fqdn, domain, machine_sid, machine_guid (HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid), agent_id |
| ОС/хардуер | os_name, os_version, os_build, os_arch, manufacturer, model, serial_number, cpu_model, cpu_cores, ram_bytes, last_boot_at, last_user |
| обобщение на мрежата | ip_addresses jsonb, mac_addresses jsonb (подробности в device_network_adapters) |
| състояние | status (online/offline/never_connected/decommissioned), source (agent/ad/both), agent_version, last_seen_at, last_inventory_at |
| AD | ad_object_guid, ad_dn, ad_ou, ad_enabled, ad_last_logon_at |
| потребителски данни | tags text[], custom_fields jsonb, site_id |
Индекси: UNIQUE(tenant_id, machine_guid) WHERE machine_guid IS NOT NULL, (tenant_id, lower(hostname)), (tenant_id, ad_object_guid), (tenant_id, status), GIN върху tags, триграмен GIN върху hostname за търсене.
От 0008:
search_textе генерирана колона (име на хоста, FQDN, показвано име, последен потребител, сериен номер, IP адреси) с триграмен индекс, който обслужва търсенето на устройства.inventory_hashes(jsonb) съдържа отпечатък за всяка секция на инвентаризацията. При приемането секциите, които не са се променили, се пропускат.- Индексите за сортиране покриват колоните на списъка с устройства и
offline_since.
agents
По един ред за всяка инсталация на агента, device_id е уникален. Съдържа auth_key_hash, version, connection_state, last_heartbeat_at, config_version (увеличава се, когато конфигурацията на агента от страна на сървъра се промени, за да я изтегли агентът отново).
Таблици на инвентаризацията
device_inventory_snapshots пази суровия отчет (payload jsonb, съхранение 30 дни). Нормализираните таблици (device_software, device_disks, device_network_adapters, device_services, device_updates, device_local_users) се заменят за всяко устройство при всеки отчет в една транзакция (изтриване + вмъкване или upsert със seen_at и изтриване на остарелите). device_software има UNIQUE(device_id, name, version) и триграмен индекс върху name за филтъра „устройства, на които има софтуер X“.
device_metrics
device_metrics(device_id uuid, ts timestamptz, cpu_pct real, mem_pct real, disk_pct jsonb, cpu_temp_c real) -- cpu_temp_c NULL without a sensor (0016)
PARTITION BY RANGE (ts) -- monthly partitions, created 2 months ahead by the daily metrics.partitions job
PRIMARY KEY (device_id, ts)
jobs
Обща единица работа за агент (device_id) или конектор (connector_id). payload и result са jsonb, типизирани според type (схемите са в entrosity-shared-go/proto). Индекси върху (device_id, status), (connector_id, status), (deployment_target_id), (expires_at) WHERE status IN ('created','sent','acked','running').
remote_sessions
По един ред за всяка сесия на отдалечен работен плот: device_id, agent_id (агентът на устройството към момента на създаване на сесията; отнемането му прекратява сесията), job_id (задачата remote_desktop), mode (control/view), require_consent, show_banner (лентата на сесията на устройството; миграция 0011), status (pending → active → ended или failed, когато сесията изобщо не е започнала), created_by, ticket_hash (SHA-256 на еднократния билет за преглед, изчиства се при използване или при приключване на сесията) и ticket_expires_at, agent_node (URL адресът на репликата, която държи потока на агента), end_code (код за затваряне на потока) и end_reason, created_at, started_at, ended_at. Уникален частичен индекс позволява една отворена (pending или active) сесия на устройство. Приключилите сесии се изчистват заедно с историята на задачите (RMM_RETENTION_JOB_DAYS или отмяната за тенанта).
AD синхронизация
connectors
По един ред за всяка инсталация на конектор за обекта: име на хоста, домейн, версия, възможности, auth_key_hash, присъствие (status, last_seen_at), revoked_at. UNIQUE(tenant_id, lower(hostname), lower(domain)) WHERE revoked_at IS NULL — повторното инсталиране на същата машина завърта ключа. Изтриването на конектор го отнема и изтрива неговите конфигурации за синхронизация.
ad_sync_configs
По една за всяка синхронизация на домейн; принадлежи на конектор. bind_password_enc, push_credential_enc и push_token_enc (суровият токен за регистриране на агента, предаван на инсталациите чрез push, push_token_id/push_token_expires_at) са AES-GCM шифротекст (base64, с префикс на идентификатора на ключа) с допълнителни данни, които ги обвързват с идентификатора на конфигурацията. tls_mode е ldaps/starttls/none; computer_filter се комбинира чрез AND с (objectCategory=computer); ou_include/ou_exclude съдържат DN. Състояние на планирането: last_sync_at, last_sync_status, consecutive_failures, next_sync_at (интервал × 2^неуспехи, максимум 24 h). auto_push_agent инсталира агента чрез push на новооткритите активни компютри след всяка синхронизация.
ad_sync_runs
По един за всяко изпълнение (trigger manual/schedule, status pending/running/succeeded/failed, job_id), най-много едно активно за конфигурация (частичен уникален индекс). Броячите seen, created_count, updated_count, gone_count, matched_count; received_seqs и expected_total правят приемането на частите идемпотентно и независимо от реда. ad_sync_seen(run_id, object_guid) записва GUID идентификаторите, видени от изпълнението (безопасно „маркиране като изчезнали“ при няколко реплики); изчиства се при приключване на изпълнението.
ad_computers
Огледално копие на компютърните обекти в AD. object_guid е уникален в рамките на тенанта; device_id сочи към съпоставеното устройство. gone_since се задава, когато обект изчезне от AD, и се изчиства, ако се появи отново; устройствата само от AD, чийто обект липсва повече от 30 дни, получават custom_fields.ad_missing = "true" (никога не се изтриват автоматично). Състояние на push инсталирането: push_status (queued/connecting/copying/installing/success/failed), push_error_code, push_error, push_at, push_job_id.
Пакети и внедрявания
packages
tenant_id NULL означава глобален пакет, създаден от глобален администратор и видим за всички тенанти (само за четене за тях). kind определя кои полета са задължителни:
msi/exe:object_key,sha256,size_bytes,install_args,uninstall_argspowershell:object_keyна.ps1(или вграденscript_content)winget:winget_id, незадължителенwinget_version,winget_source(winget/msstore)
status е draft, докато каченият файл не бъде финализиран (обектът е проверен със stat, SHA-256 и размерът са сверени с декларираното от браузъра, метаданните на MSI, като msi_product_code, са прочетени, доколкото е възможно), след което става ready; winget пакетите и вграденият PowerShell са ready още при създаването. Обектите се намират в packages/<tenant or global>/<package id>/<sanitized file name>. Колони за изпълнението: install_args, uninstall_args, success_exit_codes (по подразбиране {0,3010,1641}), requires_reboot, timeout_seconds (60–86400), run_as (system/logged_on_user), arch, min_os_build. Пакетите се изтриват меко (deleted_at, обектът се запазва, защото приключилите внедрявания все още сочат към пакета); изтриването на пакет, използван от неприключило внедряване, се отказва с package_in_use.
Примери за detection jsonb:
{"type":"msi_product_code","product_code":"{GUID}"}
{"type":"registry","key":"HKLM\\SOFTWARE\\Vendor\\App","value":"Version","op":">=","expected":"2.0"}
{"type":"file","path":"C:\\Program Files\\App\\app.exe","min_version":"2.0.0"}
{"type":"winget","id":"Vendor.App"}
deployments
action е install или uninstall. target_kind: all (целият тенант), devices (изричен списък, съхраняван в deployment_targets), filter (филтър за устройства в target_filter jsonb, включително условия software_present/software_absent {name, version_op?, version?}; определя се при стартирането и отново при „добавяне на нови устройства“). Устройствата без агент или изведените от експлоатация се изключват и се отчитат в excluded_count.
Планиране: schedule_kind now / at (scheduled_at) / window (ежедневен window_start–window_end във формат HH:MM; край < начало преминава през полунощ; оценява се в часовата зона на обекта на всяко устройство или в settings.default_timezone на тенанта). max_concurrency (1–1000), retry_count (0–10), retry_backoff_seconds, reboot_policy, expires_at. status: scheduled → running ⇄ paused → completed | failed | cancelled (failed, когато има неуспешни цели или цели с изтекло време и нито една не е успешна или пропусната, иначе completed). Колоните-броячи count_pending, count_queued, count_running (downloading + installing), count_success, count_failed, count_skipped, count_cancelled, count_timeout се преизчисляват от целите след всяка промяна за евтино показване на списъците.
deployment_targets
По един ред за (внедряване, устройство), UNIQUE(deployment_id, device_id); две внедрявания на един и същ пакет на едно устройство са позволени. Колони: attempt, next_attempt_at (неуспешна цел, която чака повторен опит, е pending с бъдещ next_attempt_at), job_id (текущата задача; jobs.deployment_target_id сочи обратно), exit_code, error_code, last_error, output_tail, started_at, finished_at. Преходи между състоянията:
pending → queued → downloading → installing → success
→ failed (retry ≤ retry_count → pending)
pending → skipped (detection says already installed)
pending/queued → cancelled | timeout
failed | timeout → pending (manual retry)
winget_index
Моментна снимка на общностния източник на winget (id, name, publisher, versions[]), опреснявана приблизително ежедневно от задачата winget.refresh от source.msix (RMM_WINGET_SOURCE_URL), в която съветникът за пакети търси чрез триграмни индекси.
SQL функции
deployment_in_window(ts, tz, window_start, window_end)– далиtsв часовата зонаtzпопада в ежедневния прозорец; празен прозорец винаги съвпада, непозната зона – никога.version_cmp(a, b)– сравнение на версии, разделени с точки (числово за всяка част, когато и двете части са числа, иначе текстово без отчитане на регистъра; началнотоvсе игнорира), връща -1/0/1; използва се от условията за софтуер при целите.
Скриптове, аларми, издания, одит
scripts, script_versions
tenant_id NULL е глобалната библиотека (управлява се от глобалните администратори, може да се изпълнява от всеки тенант, но там е само за четене). language powershell (5.1) / pwsh / cmd; params_schema е подмножество на JSON Schema (properties от тип string/integer/number/boolean с title, default, enum, minimum/maximum, maxLength; required; x-order), което порталът показва като формуляр и което се валидира на сървъра преди изпълнение. Промяната на content или params_schema увеличава current_version и добавя запис в script_versions (script_id, version, content, params_schema, created_by, created_at); изтриването е меко (deleted_at) и запазва изпълненията.
script_runs
По един за всяко устройство при всяка заявка за изпълнение: script_id + script_version + script_name (запазва се, ако скриптът бъде изтрит), job_id (уникален), params (валидирани, типизирани), run_as, status queued → running → succeeded|failed|timeout|cancelled, exit_code, error_code/error. output съдържа поточно предадения текст (частите в реално време се добавят по ред на своето байтово отместване); когато агентът качи пълния изход (PUT /jobs/{id}/output, ≤ 10 MiB), той отива в обектното хранилище под output_object_key, а output пази края му; output_bytes, output_truncated.
alert_rules, alert_rule_overrides, alerts
alert_rules.tenant_id NULLса глобални правила, които се прилагат за всеки тенант; тенант може да изключи глобално правило за себе си чрезalert_rule_overrides (rule_id, tenant_id, enabled). Типове и ключове наcondition:device_offline {minutes},disk_free_pct {below, drive?},agent_outdated {min_version?}(по подразбиране: най-новото стабилно издание на агента),cpu_pct/mem_pct {above, minutes}(средна стойност за прозореца),cpu_temp {above_c, minutes}(°C, средна стойност на пробите, които имат температура; 0016 създава глобалното правило CPU temperature high, 90 °C за 10 min),deployment_failed {threshold_pct}(по подразбиране 20: дял на неуспешните цели и целите с изтекло време от внедряване, създадено през последната седмица),adsync_failed {},connector_offline {minutes}. Миграция 0006 създава пет глобални правила по подразбиране (офлайн 60 min, диск под 10 %, остарял агент, конектор офлайн 15 min с критична сериозност, неуспешна AD синхронизация).alertsса за правило и субект (device,connector,deployment,adsync_config), с частичен уникален индекс, така че даден субект има най-много една активна (отворена или потвърдена) аларма за правило. Механизмът сам ги отваря, опреснява съобщението им и ги разрешава; потребителите могат да ги потвърждават (алармата пак се разрешава автоматично) или разрешават.tenant_memberships.notification_prefs{alert_email: off|immediate|digest, min_severity, digest_interval: 15m|hourly|daily, digest_hour, timezone, offline_batch_minutes: 5|15|30|60, muted_types[], notify_resolved, quiet_hours{enabled, start, end, critical_bypass}}(стойности по подразбиране: immediate, warning, 15m, 8, зоната на тенанта, 15, няма, false, off/22:00–07:00/bypass) иalert_digest_at(последното изпратено обобщение).
agent_releases
(component agent|connector, version) е уникален; channel stable/beta; status draft → published; object_key (releases/<component>/<version>/rmm-<component>.msi), size_bytes, sha256 (проверява се спрямо качения обект при публикуване), signature (Ed25519 върху манифеста на изданието, създава се при публикуване), rollout_pct, notes. agents и connectors получиха update_version / update_requested_at (последното предложение, за едночасовия период на изчакване преди повторен опит).
device_logons
По един ред за всяко влизане в Windows, докладвано от агента: device_id, username, session_id, session_type (console/remote), client, logon_at, logoff_at (NULL, докато потребителят е влязъл), logoff_estimated. Уникален (device_id, session_id, logon_at); RLS както при другите таблици на тенантите. Редовете се изтриват 7 дни след logoff_at (фиксирано; категория за съхранение device_logons), както и заедно с устройството.
audit_log
Само за добавяне (без права за UPDATE/DELETE за ролята на приложението); before/after съдържат редактирани моментни снимки (тайните са премахнати). Автоматизацията на изданията записва записи без потребител (actor_user_id NULL).
Миграции
| № | файл | съдържание |
|---|---|---|
| 0001 | init | разширения, tenants, sites, users, refresh_tokens, password_resets, invitations, audit_log |
| 0002 | agents_devices | enrollment_tokens, devices, agents, таблици на инвентаризацията, jobs |
| 0003 | metrics | device_metrics с дялове + помощна функция за създаване на дялове |
| 0004 | adsync | connectors, ad_sync_configs, ad_sync_runs, ad_computers |
| 0005 | packages_deployments | packages, deployments, deployment_targets |
| 0006 | scripts_alerts | scripts, script_versions, script_runs, alert_rules (+ глобални правила по подразбиране), alert_rule_overrides, alerts, agent_releases; users.notification_prefs; колони за предложения за обновяване в agents/connectors |
| 0007 | rls | роля rmm_app, rmm_tenant_ok(), политики за всяка таблица, съставни външни ключове по тенант, одитен журнал само за добавяне с purge_audit_log(), помощни функции за дялове със SECURITY DEFINER |
| 0008 | perf_indexes | devices.search_text (триграмен) и inventory_hashes; индекси за сортиране, състояние и препратки към задачи; rmm_devices_with_software(); pg_stat_statements, когато е наличен |
| 0009 | login_attempts | броячи на влизанията с фиксиран прозорец, споделени от всички реплики (хеширани ключове, изчистват се след един ден) |
| 0010 | remote_sessions | сесии на отдалечен работен плот (RLS, съставни ключове по тенант, една отворена сесия на устройство) |
| 0011 | remote_sessions_banner | remote_sessions.show_banner |
| 0012 | platform_mirror | tenant_memberships (роля за всеки потребител и тенант, предпочитания за аларми за всеки тенант; попълнени от users.tenant_id/role), users.is_global_admin, platform_sync_state, platform_sessions_revoked, platform_step_ups_used; потребителите могат да се четат като глобални редове; имейлът е уникален сред неизтритите потребители; тригер за локално влизане |
| 0013 | drop_local_auth | премахва локалното влизане: таблиците refresh_tokens, password_resets, invitations, login_attempts; колоните на users tenant_id, role, password_hash, totp_*, must_change_password, failed_logins, locked_until, last_login_at, notification_prefs, alert_digest_at; тригера от 0012, който поддържаше users.tenant_id/role и tenant_memberships в синхрон. users.status става active/disabled/deleted (потребителите с invited стават deleted); политики за users: четене за всички, записи само в глобалния обхват. Обратната миграция създава отново таблиците и колоните празни: идентификационните данни не могат да бъдат възстановени, така че връщането назад преди 0013 означава възстановяване от резервно копие, направено преди нея. |
| 0014 | notification_controls | маркери в tenant_memberships за групирани имейли за офлайн устройства, имейли за разрешаване и тихи часове (offline_rollup_at, resolved_mailed_at, quiet_held_since); индекс върху разрешените аларми |
| 0015 | device_logons | влизания в устройствата (RLS, изтриват се 7 дни след изхода) |
| 0016 | cpu_temperature | device_metrics.cpu_temp_c; тип правило за аларма cpu_temp и глобалното правило CPU temperature high |
| 0017 | screen_wall | Роля teacher в tenant_memberships; remote_sessions.profile ('' или wall, само преглед), room_firewall_id, room |
Собствените миграции на river се прилагат от migrate up преди тези. Нови миграции се създават с make migration name=… (Make цели); не забравяйте да ги добавите в тази таблица.
База данни на Entrosity Hub
Hub пази данните си в собствена база данни, platform, на същия
PostgreSQL сървър (собственик rmm, услугата се свързва като
platform_app, без защита на ниво ред: всяка заявка се ограничава от
собствените правила за достъп на Hub). Миграциите са в
entrosity-hub.backend/db/migrations (platform-server migrate);
таблиците за задачи на river се създават първи, както в Axis.
| Таблица | Съдържа |
|---|---|
users | По един акаунт в Entrosity за всеки човек: имейл (уникален сред неизтритите потребители), argon2id хеш на паролата, показвано име, състояние (active, disabled, deleted), is_platform_admin, тайна за двуфакторна автентикация (шифрована с PLATFORM_MASTER_KEY), хешове на кодовете за възстановяване, броячи за заключване. Идентификаторите се запазват при импортиране от Axis (import-rmm, което изисква база данни на Axis отпреди миграция 0013). |
organizations | Клиенти: име, уникален slug (^[a-z0-9-]{3,40}$), състояние (active, suspended), настройки (require_totp). Тенантите в Axis имат същите идентификатори. |
products | Продуктите, в които Hub вписва потребителите: id (rmm), име, базов път (/axis), познатите му роли, дали е включен. Първоначално съдържа Axis; стартовият панел и пренасочването след влизане следват базовия път. |
organization_products | Кои продукти има дадена организация. |
memberships | Потребител в организация, с роля в организацията admin или member. |
product_roles | Ролята на член в един продукт на организацията (за Axis tenant_admin, technician, viewer, teacher); изисква членството и продукта на организацията, а тригер проверява ролята спрямо списъка на продукта. |
refresh_tokens | Бисквитки за сесия (хеширани), групирани в семейства, които се завъртат; отнето семейство без наследник е приключила сесия (моментната снимка ги изброява за продуктите). |
password_resets, email_changes, invitations | Еднократни връзки (хеширани токени) със срок на валидност; поканата дава роля в организация, роли в продукти и/или администратор на платформата. |
login_attempts | Броячи на влизанията и на ограничението на честотата на заявките, споделени от репликите. |
audit_log | Всяка промяна, само за добавяне за platform_app (тригер отказва обновявания и изтривания). |
Миграции на Hub:
| № | файл | съдържание |
|---|---|---|
| 0001 | init | таблиците по-горе; роля platform_app; products с първоначален запис за Axis (rmm, базов път /manage) |
| 0002 | axis_path | базовият път на Axis става /axis |
| 0003 | edge_product | регистрира Entrosity Edge (edge, изключен) |
| 0004 | edge_enable | включва Edge |
| 0005 | product_beta | products.beta: продукт в бета виждат само администраторите на платформата |
| 0006 | sphere_product | регистрира Entrosity Sphere (sphere, изключен, бета) |
| 0007 | sphere_enable | включва Sphere |
| 0008 | matrix_product | регистрира Entrosity Matrix (matrix, изключен, бета) |
| 0009 | matrix_enable | включва Matrix |
| 0010 | email_changes | чакащи смени на имейл (нов адрес, хеширан токен, срок) |
| 0011 | matrix_teacher | ролята operator на Matrix става teacher (членове и отворени покани) |
| 0012 | axis_teacher | Axis получава ролята teacher (само стената с екрани) |