API на Matrix
Уеб приложението на Matrix е обикновен клиент на REST API под /api/v1.
Всичко, което прави приложението, можете да направите и чрез API.
Пълният справочник на крайните точки е справочникът на API на
Matrix, генериран от
entrosity-matrix.backend/api/openapi.yaml – спецификацията, от която се
генерират маршрутизаторът и валидирането на заявките на сървъра (и
TypeScript типовете на приложението).
Конвенции
- Базов URL:
https://hub.entrosity.com/matrix/api/v1(проксито премахва/matrix). API на конектора е под/matrix/api/connector/v1(Протокол на конектора на Matrix). - Удостоверяване:
Authorization: Bearer <product token>– петминутен токен от Entrosity Hub за продуктаmatrix(POST /api/platform/v1/auth/product-token {"product":"matrix"}със сесията в Hub, API на Hub). Matrix няма собствено влизане; потребителите, тенантите и ролите идват от Hub. Докато Matrix е в бета версия, Hub издава токените му само на администраторите на платформата. - Правила за достъп: всеки маршрут има правило в
entrosity-matrix.backend/internal/http/portal/access.go: публичен, влязъл потребител, глобален администратор или право в тенанта (Роли и права). Потребителите на тенант могат да извикват само/tenants/{id на техния тенант}/…; всеки друг тенант връща 404. Липсващо право връща 403. Маршрутите за промяна (състояние на зала, групово състояние, отмяна на график, промяна на адрес, създаване и повторен опит, промяна на списък с разрешени сайтове) изискват в маршрутизатора самоrooms:read/addresses:read: обработчикът проверяваrooms:operateзаедно с правата по зали на учителя,addresses:manageилиallowed_sites:manage, така че отказаният опит да бъде записан в историята, преди да се върне 403. - Учителите виждат само своите зали: за учител
GET /rooms, разрешените сайтове, адресните групи, графиците, историята и потокът от събития пропускат всяка зала, която не му е предоставена (и защитните стени без нито една от неговите зали); задача, групово действие, адресна група или операция на такава зала, както и разрешените сайтове или адресните групи на такава защитна стена, връщат 404, а промяна на такава зала връща 404unknown_room(записва се като отказана), точно както несъществуваща зала. Наблюдателите и администраторите виждат всички зали (entrosity-matrix.backend/internal/visibility). - JSON тела с ключове в
snake_case, най-много 1 MB. Схемите на заявките отказват непознати полета (422). - Списъци: одитните журнали и администраторският списък с тенанти
приемат
?page=&page_size=(по подразбиране 50, най-много 200) и връщатitems,page,page_sizeиtotal. Историята използва курсор (История). Другите списъци връщат всички елементи. - Ограничения на заявките: 20 заявки в секунда за IP адрес на клиент,
пик 60 (
MATRIX_API_RATE_PER_SECOND,MATRIX_API_RATE_BURST); над това 429 без код. Промените се броят и към ограниченията от 10 на потребител и 30 на тенант в минута (429 rate_limited, записва се като отказана). - Всеки отговор съдържа
X-Request-ID.
Грешки
Грешките са RFC 7807 application/problem+json със стабилен code:
{
"type": "/problems/snapshot_stale",
"title": "Conflict",
"status": 409,
"code": "snapshot_stale",
"detail": "the firewall's state is out of date; wait for the next report",
"request_id": "…"
}
Грешките при валидиране имат code: "validation" и fields със
съобщение за всяко поле (например policy_pattern без (?P<room>…),
reenable_at извън интервала от 5 минути до 7 дни, reenable_at с
enable). Най-честите:
| Код | Кога |
|---|---|
unknown_room (404) | Учител превключва зала, която не му е предоставена, или несъществуваща зала (при групова промяна: която и да е от залите; нищо не се изпраща). |
forbidden (403) | Ролята няма правото (зрител превключва, учител променя адрес). |
snapshot_stale (409) | Залите на защитната стена са неактуални (Зали → Неактуални данни). |
connector_offline (409) | Конекторът на защитната стена не е свързан. |
writes_disabled (409) | writes_enabled (зали), address_writes_enabled (адреси) или site_writes_enabled (разрешени сайтове) на защитната стена е изключено. |
busy (409) | Промяна на залата (или адресна промяна на защитната стена) все още е в ход. |
unknown_room (409) | Защитната стена не отчита залата. |
conflict (409) | Адреси: групата се е променила след group_version или IP адресът – след expected_ip. Разрешени сайтове: списъкът се е променил след list_version. |
rate_limited (429) | Ограничения на промените. |
Всички кодове: Кодове на грешки.
Защитни стени
POST /tenants/{tenantID}/firewalls добавя защитна стена зад един от
конекторите на тенанта; PATCH я променя, а DELETE я премахва. Всяка
промяна изпраща конфигурацията на конектора
(matrix.firewall.apply, протокол).
По подразбиране writes_enabled, address_writes_enabled и
site_writes_enabled са false,
verify_tls е true, port е 443, vdom е root, а
report_interval_s е 30. Firewall има и sites_supported (само за
четене: конекторът ѝ е обявил възможността sites). services_enabled
(премахнатите услуги на Entrosity) вече не е част от API: заявка, която
го изпраща, се отказва (422).
API ключът на FortiGate е само за запис: token в заявката за
създаване или PUT /tenants/{tenantID}/firewalls/{firewallID}/token {"token": "…"}
(204). Той никога не се връща; съхраняването му увеличава
credentials_version и прилага отново защитната стена.
POST …/check (202, Job) изпълнява проверката само за четене;
result на задачата е CheckResult (version, direction_ok, rooms,
groups, members, warnings, guard_state, policy_names, а от
конектори, които поддържат разрешени сайтове, и sites).
POST …/refresh {"scope": "rooms" | "addresses" | "all"} прочита
защитната стена веднага.
Превключване на зали
GET /tenants/{tenantID}/rooms[?firewall_id=] връща firewalls (със
stale, updated_at, error, error_code, simulated, guard_state,
writes_enabled, site_writes_enabled, sites_supported,
shared_allowed_sites: домейните от списъка за всички зали) и rooms
(със status, present, can_manage за извикващия, busy_job_id,
schedule, last_change и allowed_sites: домейните от собствения
списък на залата, 0, когато няма такъв).
POST /tenants/{tenantID}/firewalls/{firewallID}/rooms/SB1-102/status
{"status": "disable", "reenable_at": "2026-10-01T14:00:00Z"}
връща 202 с {change, job}: реда в историята (резултат pending) и
задачата matrix.policy.set. Резултатът пристига асинхронно:
- следете
job.updateиmatrix.changeв потока на живо или проверявайтеGET /tenants/{tenantID}/jobs/{jobID}; resultна промяната ставаsuccess,unconfirmed,deniedилиerror(Промени и резултати), сdetail(кода на грешката) за последните три;resultна задачата съдържаPolicySetResultна конектора (previous,status,wrote,confirmed).
Правила: reenable_at само с disable, от 5 минути до 7 дни напред; то
създава график за включване. Всяка по-нова промяна на залата отменя
чакащия ѝ график. Отказана заявка също записва промяна denied.
Групово: POST …/firewalls/{firewallID}/bulk-status {"status", "rooms": […] | "building": "SB1", "reenable_at"?} връща
{bulk: {id, items: [{room, change_id, job_id, error?}]}}; залите с
промяна в ход се пропускат (error: "busy"). GET /tenants/{tenantID}/bulk-actions/{bulkID} показва резултата за всяка
зала.
Графици: GET /tenants/{tenantID}/schedules[?status=], POST …/schedules/{scheduleID}/cancel (само pending; иначе 409 schedule_not_pending).
Адресни операции
POST /tenants/{tenantID}/firewalls/{firewallID}/addresses/update
{"policy_id": 102, "group": "SB1-102-Students address", "name": "pc-102-01.coding.local",
"ip": "192.0.2.21", "expected_ip": "192.0.2.11", "group_version": "<64 hex>",
"request_id": "<uuid>"}
…/addresses/create приема същите полета без expected_ip. И двете
изискват addresses:manage и връщат 202 с {operation, job, change}.
request_id(UUID, генериран от клиента) прави заявката идемпотентна: повторното ѝ изпращане връща същата операция;request_id_reused, когато принадлежи на друга операция.group_versionеversionна групата отGET …/address-groups/detail?policy_id=&group=, което връща и незавършените операции на извикващия.stageна операцията следва дневника на конектора (prepared,update_sent,create_sent,created,member_sent,done), аresultѝ – този на промяната.POST /tenants/{tenantID}/address-operations/{operationID}/retry {"group_version": "…"}продължава прекъсната операция: само от автора ѝ и само когато еretryable(запис е започнал и тя не е приключила; иначе409 not_retryable).
Разрешени сайтове
GET /tenants/{tenantID}/firewalls/{firewallID}/allowed-sites
(rooms:read) връща AllowedSitesView:
firewall:stale,updated_at(кога списъците са прочетени последно),connector_online,enabled,guard_state,site_writes_enabled,sites_supported;sharedиrooms(всяка налична зала, подредени като на страницата със залите):AllowedSitesListсlist(sharedили кодът на залата),room,building,group,exists,policy({policy_id, name, status, covers}),setup(ok,group_missing,policy_missing,policy_disabled,policy_not_coveringилиunknown, когато конекторът не е изпратил списъците),version,editableиreason,domains(SiteEntry{domain, object, owned, editable, reason}),can_edit(за извикващия),busy_job_idиlast_change;can_setup: извикващият може да получи командите за настройка.
PUT /tenants/{tenantID}/firewalls/{firewallID}/allowed-sites/SB1-102
{"domains": ["classroom.google.com", "*.moodle.example.org"], "list_version": "<64 hex>"}
{list} е shared или код на зала. domains е пълното желано
множество от домейни, управлявани от Matrix (с малки букви, сортирани,
без повторения; най-много 200); list_version е version на списъка.
Общият списък изисква allowed_sites:manage (администратори на тенанта);
списък на зала приема и учител с rooms:operate и право за залата.
Отговорът е 202 с {change, job}: промяната sites и задачата
matrix.sites.set, чийто резултат пристига като при превключване на зала
(success, unconfirmed, denied, error; result на задачата е
SitesSetResult с added, removed, wrote и confirmed). Откази,
всеки записан като промяна denied:
| Код | Кога |
|---|---|
forbidden (403) | Общият списък от потребител, който не е администратор, или наблюдател. |
unknown_room (404) | Учител без право за залата. |
group_read_only (403) | Matrix не може да променя групата във FortiGate (подробностите казват защо). |
unknown_room (409) | Залата не е налична зала на защитната стена. |
invalid_domain (422) | Домейн не е валиден (fields.domains казва кой) или са повече от 200. |
connector_offline (409) | Конекторът е офлайн. |
connector_outdated (409) | Конекторът не поддържа разрешени сайтове (няма възможността sites). |
snapshot_stale (409) | Данните от защитната стена са неактуални или списъците никога не са изпращани. |
writes_disabled (409) | site_writes_enabled на защитната стена е изключено. |
sites_not_set_up (409) | Групата на списъка или нейното ACCEPT правило липсва. |
conflict (409) | list_version не е текущата версия на списъка. |
busy (409) | Друга промяна на този списък все още се изпълнява. |
rate_limited (429) | Ограниченията на промените (запазването се брои като превключване на зала). |
GET /tenants/{tenantID}/firewalls/{firewallID}/allowed-sites/setup-cli?rooms=SB1-102,SB1-108
(allowed_sites:manage) връща {cli, lists, warnings}: командите за
FortiOS, които създават липсващото от общия списък и списъците на
посочените зали (празно, когато нищо не липсва), списъците, които
настройват (shared, кодове на зали), и проблемите, които не могат да
оправят (Настройка на FortiGate).
Права по зали
GET /tenants/{tenantID}/room-grants[?user_id=] (администраторите на
тенанта виждат правата на всички, останалите – своите); PUT /tenants/{tenantID}/users/{userID}/room-grants {"firewall_id", "rooms": […]} заменя залите на учител в една защитна стена (grants:manage;
422 not_teacher за всеки, който не е учител в тенанта).
Добавяните зали трябва да се отчитат от защитната стена; премахването е
винаги възможно. Записва се като промяна grants.
История
GET /tenants/{tenantID}/history връща промените от най-новите, с
филтрите from, to, user_id, room (част от код на зала, без
значение на главни и малки букви), kind (policy, address_update,
address_create, grants, schedule, bulk, sites, а services за стари записи) и
firewall_id, и
limit (1–200, по подразбиране 50). Когато има още, страницата съдържа
next_cursor: подайте го като cursor. Всяка промяна има source
(matrix или stop-internet за прехвърлените редове) и
metadata.steps (упълномощаванията в момента на запис и етапите на
адресите и сайтовете).
Промените sites имат room (празно за общия списък), group_name,
previous и desired (обобщения като 3 domains) и metadata
{list, domains, added, removed, previous_domains} (при отказ:
{list, requested}). Промените services са само история отпреди
премахването на услугите на Entrosity: някогашният превключвател
(previous / desired enable или disable) или системно
синхронизиране на групата; нови не се правят.
Обновления на живо
GET /tenants/{tenantID}/stream е text/event-stream. Браузърите се
удостоверяват с ?sse_token= от POST /auth/sse-token {"tenant_id": "…"} (валиден 60 секунди; така токенът за достъп не
попада в адреси). Събития:
| Събитие | Данни | Значение |
|---|---|---|
matrix.rooms | {firewall_id} | Заредете отново залите (пристигнал е отчет, зала се е променила, защитната стена е станала неактуална или отново актуална). |
matrix.addresses | {firewall_id} | Заредете отново адресните групи. |
matrix.sites | {firewall_id} | Заредете отново разрешените сайтове (отчет с нови списъци, изпратена или завършена промяна). |
matrix.change | промяната | Промяна е приключила. |
job.update | задачата | Състоянието или резултатът на задача се е променил. |
connector.status | конекторът | Конектор е станал онлайн или офлайн. |
Инсталатори на конектора
GET /tenants/{tenantID}/connector-release (администратори на тенанта)
връща най-новото издание от канала за обновявания с връзка за изтегляне,
валидна един час, или 404, когато няма съхранено издание. Глобалните
администратори виждат всички съхранени издания в
GET /admin/connector-releases. CI качва изданията в
/api/releases/v1/connector (Работа с Entrosity Matrix → Издания на
конектора).
Чувствителни действия
Окончателното изтриване на токен за регистриране (глобални
администратори) изисква step-up токен от Hub срещу паролата на
извикващия (POST /api/platform/v1/auth/step-up, продукт matrix),
изпратен като step_up_token към POST /tenants/{tenantID}/enrollment-tokens/{tokenID}/delete.
API ключовете на FortiGate никога не се връщат от нито една крайна точка,
а всяко изтегляне от конектор се записва в одитния журнал
(firewall.credentials_fetch). Всяка приета промяна на разрешени
сайтове се записва в одитния журнал като allowed_sites.set (ресурс:
защитната стена).
Вътрешен API (Entrosity Axis)
Стената с екрани на Entrosity Axis чете
залите на отделен слушател, MATRIX_INTERNAL_ADDR (:8086), който никога
не се публикува. Всяка заявка изисква Authorization: Bearer <MATRIX_AXIS_TOKEN> (иначе 401; без токена слушателят е изключен).
GET /internal/v1/screen-rooms?tenant=<id>&all=true връща всички зали от
включените защитни стени на тенанта; ?tenant=<id>&user=<id> — само
залите, дадени на този потребител като активен учител на активен
тенант (нищо за другите роли). Идентификаторите са тези от Hub. Всяка зала
има room, building, building_name, firewall_id, firewall_name и
computers: имената на /32 членовете на адресните групи на залата от
последния отчет на защитната стена. Договорът е proto/matrix.ScreenRooms
в entrosity-shared-go.