Documento de design · arquitetura

O intermediário entre a Senior e o hardware de controle de acesso.

O Ennor assume o equipamento inteiro por API — cadastro, gestão, atuação, telemetria e observabilidade — com dominância completa do dispositivo. Substituição futura do Senior Orquestrador; vira ferramenta Senior homologada depois.

A forma diferente não é rodar código dentro do device (não dá oficialmente) — é inverter a relação: em vez do driver antigo que fica perguntando ao equipamento (polling), o equipamento passa a consultar e obedecer o Ennor em tempo real. O Ennor enxerga tudo que o hardware entrega e lê os logs — e não guarda uma base de dados própria: a fonte da verdade é o sistema Senior.

Construído53 testes verdes · repo privado
Sul + Norte19 .fcgi + 20 serviços SDK
Multi-tenant + RBACsuper-admin e por dono
Alta disponibilidadePostgres + fila durável
Sem base própriaa Senior é a fonte
Entrar no painel →
01 · o fluxo

Como o Ennor conversa com os dois lados

Dois sentidos, sem driver externo no meio. O sul fala a API nativa do equipamento (Control iD .fcgi); o norte, o sistema Senior (fase 2), a fonte da verdade dos dados. O Ennor no meio: ingestão em tempo real → tradução/relay → observabilidade.

Sul · equipamentos

Control iD

iDFace · iDAccess · iDFlex · iDBlock · iDBox. Linux embarcado, API REST local.

http://<device>:80/*.fcgi
device → Ennor

Monitor (push)

Cada acesso, alarme, porta e heartbeat empurrados no ato. Zero polling.

POST /dao · /door · /device_is_alive

Modo Online

No acesso, o device pergunta "libero?"; o Ennor responde a decisão + ação.

→ new_user_identified
Ennor → device

Gestão + atuação

Cadastro (pessoa, cartão, facial), regras, abrir porta, config, backfill de logs.

create/modify_objects · user_set_image · execute_actions
O Ennor · appliance on-premise
E
EnnorJava 25 · Spring Boot
Ingestão em tempo realwebhook do Monitor + autoridade do Modo Online + backfill incremental por id>last
Adaptador de device (agnóstico)contrato único; Control iD completo (sessão reusada, leitura + escrita)
Tradução + relaynormaliza o device ao modelo comum e relaia para a Senior (a fonte)
Observabilidade + suportesaúde, alarmes, análise de log, auditoria de config, métricas
Estado efêmerosó o mínimo para operar (sessão, cursor de log, fila) — não é um banco de negócio
Norte · Senior (fase 2)

Sistema Senior

A fonte da verdade. Pessoas, credenciais, níveis de acesso e histórico vivem aqui — o Ennor não duplica.

Senior X (cloud) ou XT (concentradora)

Painel do dono

Observabilidade, telemetria e suporte da frota — a superfície de quem opera.

API + dashboards (tempo real)
device → Ennor (tempo real, sem polling) Ennor → device (gestão, atuação, backfill)

02 · a inversão

O device passa a obedecer o Ennor — em 3 passos

Configura-se o equipamento, por API, para consultar e notificar o Ennor. Tudo oficial. Fallback local garante que a porta nunca trava se o Ennor cair.

Registrar o Ennor como servidor

O Ennor entra como um "device" na frota do próprio equipamento, com a URL que ele vai chamar.

// create_objects
{ "object":"devices",
  "values":[{ "name":"Ennor",
    "ip":"http://ennor.local/api" }] }

Ligar Monitor + Modo Online

Monitor empurra eventos; Online pergunta antes de liberar. local_identification mantém as regras no device como rede de segurança.

// set_configuration
{ "monitor":{ "hostname":"ennor.local",
    "port":"8000","path":"api/notify" },
  "general":{ "online":"1",
    "local_identification":"1" } }

Decidir e ver, em tempo real

No acesso, o device chama o Ennor; o Ennor responde a ação. Todo evento chega por push.

// device → Ennor (libera)
{ "event":7, "user_id":6, "portal_id":1,
  "actions":[{ "action":"door",
    "parameters":"door=1" }] }
Ressalvas práticas. O Ennor precisa ser alcançável pelos devices na LAN. Os modelos iDFlex e iDAccess Nano exigem licença Enterprise para o modo online. A API do device é HTTP:80 sem TLS confiável → rede dos equipamentos em VLAN isolada.

03 · endpoints · Ennor → device

A API .fcgi inteira, endpoint por endpoint

Toda chamada é POST JSON (salvo indicado), autenticada por ?session=<token>. A sessão é reusada e serializada por device (o firmware tem poucas sessões simultâneas). O device devolve quase tudo como string, inclusive números. R = leitura · W = escrita.

EndpointMétodoCorpo / parâmetrosRetorno / efeito
login.fcgiPOST{login, password}{session} · TTL ~3600s
logout.fcgiPOST?sessionencerra a sessão (best-effort)
session_is_valid.fcgiPOST{session}valida o token
load_objects.fcgi RPOST{object, where?, fields?, limit?}{<object>:[ … ]} — qualquer tabela
create_objects.fcgi WPOST{object, values:[ … ]}cria (users, cards, rules, templates…)
modify_objects.fcgi WPOST{object, values, where}altera registros
destroy_objects.fcgi WPOST{object, where}remove registros
user_set_image.fcgi WPOST?user_id + octet-stream <1MBgrava a foto facial
user_get_image.fcgi RGET/POST?user_idimage/jpeg (404 se sem foto)
user_destroy_image.fcgi WPOST{user_id | user_ids[] | dangling | all}apaga foto(s)
user_list_images.fcgi RPOST{get_timestamp?}{user_ids[]} ou image_info[]
execute_actions.fcgi WPOST{actions:[{action, parameters}]}abrir porta/relé/catraca (door · sec_box · catra)
get_configuration.fcgi RPOST{<módulo>:[chaves]}valores dos parâmetros (ver §Config)
set_configuration.fcgi WPOST{<módulo>:{ … }}grava módulos (monitor, online_client…)
set_system_network.fcgi WPOST{ip, netmask, gateway, dns…}configura a rede do device
system_information.fcgi RPOST{}saúde completa (ver §Telemetria)
(raiz) /GET—ping: 200/401 = online (isAlive)
reboot · import · exportPOSTvia set_configuration / açõesreinício, backup/restore de cadastro
firmware (upload)POSTpacote / iDCloudatualização (fluxo próprio)
Remote enrollment. O Ennor dispara a captura de uma credencial no leitor do device (digital, face, cartão, PIN, senha); o resultado volta por push ao Ennor (Monitor: /template, /user_image, /face_template, /card, /pin, /password). Assim o cadastro biométrico nasce no equipamento e chega ao Ennor sem o operador digitar nada.

04 · endpoints · device → Ennor

O que o Ennor expõe para o equipamento chamar

É o coração da inversão: o Ennor sobe um servidor HTTP e o device o chama. Monitor empurra eventos (sob o path configurado); Modo Online pergunta a decisão de acesso. Nenhum polling.

Endpoint (no Ennor)OrigemQuando disparaPayload
POST <path>/daoMonitorinsert/update/delete em access_logs, alarm_logs, cards, templates{object_changes:[{object,type,values}], device_id}
POST <path>/doorMonitorporta/relé abre ou fecha{door:{id,open}, access_event_id, device_id}
POST <path>/secboxMonitorrelé da SecBox (acionamento externo){secbox:{id,open}, access_event_id}
POST <path>/device_is_aliveMonitorheartbeat periódico (default 30s){access_logs, device_id, time}
POST <path>/operation_modeMonitortroca de modo (contingência/exceção){operation_mode:{mode,mode_name,…}, device_id}
POST <path>/catra_eventMonitorgiro/desistência de catraca (iDBlock){event:{type,name,uuid}, device_id}
POST <path>/access_photoMonitorfoto da identificação (se habilitado){device_id, event, access_photo:<jpeg b64>}
POST <path>/template · /face_templateEnrollmentdigital/face capturada no device{user_id, template}
POST <path>/user_imageEnrollmentfoto facial capturada no device{user_id, image}
POST <path>/card · /pin · /passwordEnrollmentcartão/PIN/senha capturados{user_id, value}
POST new_user_identified.fcgiOnlinealguém apresenta a credencial — o device pergunta "libero?"device_id, user_id, event, portal_id, duress, card_value…
A resposta do Modo Online é o que abre a porta. Ao receber new_user_identified, o Ennor decide e devolve {result:{event:7, actions:[{action:"door","parameters":"door=1"}]}} para liberar, ou event:6, actions:[] para negar. O Ennor decide na borda — e, no modelo Controle de Acesso, repassa a decisão à Senior (§07), a autoridade de negócio.

05 · modelo de dados

Tudo que o device guarda, por load_objects

O adaptador lê, escreve e concilia estes objetos — mas o dono do dado é a Senior. Agrupados por domínio; cada nome é um object do CRUD genérico.

Pessoas & credenciais quem & como entra

usersuser_groupsgroupscardsqrcodesuhf_tagspinstemplatesuser_rolescustom_thresholds

Acesso & regras quando & onde

access_rulesuser_access_rulesgroup_access_rulesportal_access_rulesarea_access_rulesaccess_rule_time_zonestime_zonestime_spansholidaysportalsportal_actionsareasactions

Alarme zonas & sirene

alarm_zonesalarm_zone_time_zonestimed_alarms

Hardware & contingência catraca, secbox

sec_boxscatra_infosnetwork_interlocking_rulescontingency_cardscontingency_card_access_rulesscheduled_unlockscontacts

Logs & auditoria o que aconteceu

access_logsalarm_logschange_logslog_types

Frota modo servidor

devices
Chaves que não são id. Alguns objetos têm PK própria: actions.group_id, user_roles.user_id, alarm_zones.zone, sec_boxs.id (constante 65793), e as associações (user_groups, *_access_rules) têm chave composta, sem id. O adaptador conhece cada uma.

06 · configuração

Os módulos que o Ennor lê e grava

Via get/set_configuration.fcgi. O Ennor tira snapshot para auditar drift e grava o que precisa (ligar Monitor/Online, ajustar relés e leitores).

Operação

Comportamento

  • general — relés, sensores, porta, idioma, DST, online, local_identification
  • identifier — métodos, antipassback, MFA, verbose_logging
  • catra · gpio — catraca e relés de giro
Integração

Servidor & push

  • monitor — hostname/port/path/alive_interval do push
  • online_client — server_id, max_request_attempts, timeout
  • push_server · ntp
Leitores

Protocolos

  • card_reader · RFID · HID
  • mifare · OSDP · RS485
  • uhf — potência, canal, modo de leitura
Biometria & vídeo

Sensores

  • bio_id — limiar facial 1:N
  • face_module — brilho IR/LED
  • alarm · onvif/rtsp (stream iDFace) · energy

07 · o norte · Ennor ↔ Senior

O outro lado: a Senior é a fonte e a autoridade

A fase 2 liga o Ennor ao sistema Senior (Ronda senior X SDK). Aqui a figura muda: no modelo Controle de Acesso, quem decide o acesso é a Senior — o Ennor traduz e repassa. Autenticação por driver_key (tenant do cliente) + partner_key (identidade do parceiro). REST em sam-api.senior.com.br/sdk/v1.

A autoridade é em camadas — e isso é o que dispensa banco próprio. A Senior é a autoridade de negócio: no /device/accessrequest ela decide com regras que só ela tem (férias, interjornada, dupla passagem, "carona", afastamento, horário, nível, antidupla). O Ennor é a autoridade de borda: traduz, repassa e só decide sozinho no fallback offline. Por isso o Ennor não guarda base — a fonte é a Senior.

serviços SDK (o Ennor ↔ Senior)

ServiçoMétodoPapel
/device · /device/{id}GETconfigs dos equipamentos
/device/accessrequestPOSTvalidação online — Senior decide
/device/accessrequest/qrcode · /vehiclePOSTonline por QRCode (Ronda Pass) · veículo
/pendencyGETbusca a fila de pendências (lotes de 50)
/pendency/success · /updatePOSTpendência ok · erro (+ KEEP/REMOVE)
/device/access/{id}/card · biometry · photoGETlistas de liberação por device
/device/biometryPOSTbiometria capturada no device → Senior
/notify/person/access · /vehicle/accessPOSTevento de acesso → Senior
/notify/device/event · input/alarm · device/resourcePOSTstatus/alarme/recurso → Security Hub
/notify/person/clockinPOSTmarcação de ponto (PIS, NSR) — REP
/datamart/areacontrol · /holidayGETlocais físicos + regras · feriados
/driver/status · /driver/datetimePOST/GETstatus do driver · hora do servidor

o adaptador = pendência(Senior) → .fcgi(device)

Pendência SeniorVira no Control iD
includeCard / excludeCardcreate/destroy_objects{cards}
loadAllowCardListcreate_objects{cards} em lote
includeBiometry / loadBiometryListcreate_objects{templates}
includePhoto / loadCredentialFacialListuser_set_image.fcgi
excludePhotouser_destroy_image.fcgi
loadHolidayList / removeHolidayListcreate/destroy_objects{holidays}
activateDeviceOutputexecute_actions{door·sec_box}
blockDevice / setDeviceEmergencyset_configuration / scheduled_unlocks
deviceDateTime / deviceset/get_configuration
collectEventload_objects{access_logs} id>cursor
updatePersonRepcreate/modify_objects{users,cards}

A Senior avisa por WebSocket

Zero polling: wss://…/websocket/pendency?driver_key= empurra {driverId, deviceId, pendencyType}. Pooling a cada 1 min só se o WS cair — a mesma filosofia do Monitor no sul.

O Ennor puxa e aplica

GET /pendency (até 50 por lote, repete até vir menos de 50) → executa cada uma na .fcgi do device → /pendency/success ou /update (mantém a pendência se o device está offline).

E devolve o que acontece

Todo evento do device (Monitor) vira /notify/* para a Senior e o Ronda Security Hub. Presença obrigatória: 1 min sem chamada e a Senior marca os devices como offline.

Quatro tipos de gerenciador. Controle de Acesso (online, a Senior valida — nosso foco) · Coletor (offline, o device decide por lista de liberação) · REP (ponto, Portaria MTE, memória inviolável, AFD) · REP-P (ponto via programa). Homologação (fase 2) exige auto-update do driver. Falta ainda a spec do Senior XT (concentradora on-premise) para travar o norte.

08 · ler os logs

O dicionário que vira código

Dominância de verdade é entender o que o equipamento diz. Cada evento e alarme chega ao Ennor já traduzido. Referência completa em docs/CONTROLID-DADOS-E-LOGS.md.

access_logs.event — o acesso

eventleituraclasse
7Acesso concedidogrant
6Acesso negadodeny
3Não identificadoinfo
4 · 8Identificação / acesso pendenteinfo
5Timeout de identificaçãoinfo
9Usuário não é administradorinfo
10Acesso de não-identificadoinfo
11Botoeira / saída (REX)info
12Acesso pela interface webinfo
13Desistência na catracainfo
15Acesso por interfoneinfo

alarm_logs.cause — o alarme

causeorigemclasse
9Tamper (violação do equipamento)alarme
8Dedo de pânico (coação)alarme
10Cartão de pânico (coação)alarme
7Porta forçada / arrombamentoalarme
6Porta mantida abertaalarme
1–5Zonas de alarme 1 a 5zona

alarm_logs.event / templates.finger_type

campovalores
alarm.event1 = ligado · 2 = desligado
finger_type0 = dedo comum · 1 = dedo de pânico
user_roles.role1 = administrador
actions.run_at0 = device · 1 = todos · 2 = servidor
O leitor também é dado. O campo identifier_id diz por qual módulo veio o acesso — 3 bytes ASCII: fac facial · bio digital · rfi RFID · win Wiegand · qrc QR · kbd PIN · uhf UHF. O cursor de leitura incremental é o id (monotônico), por device.

09 · telemetria

Tudo que o device diz sobre si

Um POST /system_information.fcgi por device dá a saúde inteira. O Ennor coleta em paralelo (virtual threads); a ausência de heartbeat marca o device como caído.

Vida

Disponibilidade

  • Uptime days/hours/min/sec
  • Estado online · online_available
  • Heartbeat device_is_alive
  • Relógio do device (sync < 60s)
Recursos

Memória e capacidade

  • RAM livre × total memory.ram
  • Disco livre × total memory.disk
  • Biometria biometrics.max_num_records
  • Licença license.users
Identidade

Firmware e rede

  • Modelo · firmware · serial · device_id
  • MAC · IP · máscara · gateway · DNS
  • Versão da SecBox
  • Código iDCloud (para NAT)
Config

Auditoria de estado

  • Snapshot de get_configuration por módulo
  • Drift via change_logs
  • Relés, leitores, antipassback
  • Limiar facial bio_id.similarity_threshold

10 · camadas

Do que o Ennor é feito

Herda as lições provadas do Orquestrador (virtual threads, credencial cifrada em memória, salvaguardas anti-wipe, fila durável) — reescritas limpas, sem carregar um banco de negócio.

Sul

Adaptador de device

Contrato interno único, multi-fabricante por design. Control iD primeiro, a .fcgi inteira.

  • Sessão reusada e serializada por device
  • Trata erro no corpo JSON (a .fcgi responde 200 com erro)
  • Leitura + escrita + facial + atuação
  • Novo fabricante = uma implementação do contrato
Ingestão

Tempo real, sem polling

O device é quem fala. Webhook do Monitor + callback do Modo Online.

  • Eventos e logs no ato /dao
  • Porta e heartbeat /door · /device_is_alive
  • Backfill access_logs id>last
  • Cursor por device (não perde evento)
Núcleo

Tradução + relay

Normaliza qualquer fabricante a um modelo comum e relaia para a Senior, a fonte. Sem base própria.

  • Modelo comum de evento/credencial/estado
  • Fila de escrita com retry + coalescing
  • Reconciliador device × Senior (anti-wipe)
  • Estado efêmero mínimo (sessão, cursor, fila)
Norte

Observabilidade + suporte

A dominância que o dono pediu: enxergar tudo e agir. Superfície de operação e suporte da frota.

  • Saúde: RAM/disco/uptime/firmware/biometria
  • Alarmes: tamper, pânico, porta forçada
  • Auditoria de config change_logs
  • Métricas + logs estruturados Micrometer/Prometheus

11 · limites

O que o Ennor deliberadamente NÃO é

Definir o que fica de fora é parte da arquitetura — evita reconstruir o que a Senior já faz.

Não é um banco de dados

Não guarda a base de pessoas, credenciais nem histórico — isso é do sistema Senior, a fonte da verdade. O Ennor só tem estado efêmero para operar.

Não roda dentro do device

Control iD não expõe SDK embarcado nem execução própria. O Ennor domina por API, de fora — a inversão (Online+Monitor) é o edge de decisão.

Não é o cérebro do negócio

Regras de negócio, níveis de acesso e cadastro-mestre vivem na Senior. O Ennor traduz, relaia, observa e atua no hardware.


12 · construído

O que já está de pé

Appliance construído e testado (github.com/silvanobarbosa/ennor, 53 testes verdes). Auth validada contra o Senior X real; norte e sul rodam contra simuladores. Mesma stack do que substitui. Sem banco de negócio — a Senior é a fonte.

Java 21 · Virtual Threads Spring Boot 3.5 HttpClient nativo H2 / Postgres · HA Micrometer · Prometheus Credenciais cifradas (AES-GCM) base de negócio própria

✓ Sul — Control iD

19 endpoints .fcgi (leitura, escrita, facial, atuação), sessão reusada, confirma-relendo. Simulador para testar sem hardware.

✓ Ingestão + robustez

Monitor/Online; fila durável (outbox) + audit trail + contingência (503, nunca inventa); telemetria, evidências/logs, métricas Prometheus; diagnóstico completo + probe de rede.

✓ Norte + multi-tenant

Cliente Senior X, loop de pendências (norte→sul), notify como lista, presença, leitora-por-tecnologia; tenants com RBAC + roteamento device→tenant. Auth validada no Senior real; login com lockout.


13 · conectar & operar

Como o time conecta os devices locais

O appliance está construído (github.com/silvanobarbosa/ennor, Java 21 + Spring Boot, 24 testes verdes). Roda on-premise, na rede dos equipamentos — a API do device é HTTP:80 sem TLS e o device empurra eventos para o Ennor, então nuvem não alcança a LAN.

Rodar na LAN

Instale o jar numa máquina (Win/Linux) que alcança os devices, como serviço (systemd/NSSM/Docker). Requer JDK 21.

// vira http://<host>:8080
ENNOR_ADMIN_PASSWORD=…
java -jar ennor-0.1.0.jar

Conectar os devices

Mesma VLAN isolada. Firewall nos dois sentidos: Ennor→device :80 e device→Ennor :8080 (Monitor/Online, com token).

// no device: Monitor + Online →
http://<ennor>:8080/monitor?token=…

Time acessa

Browser na LAN: http://<host>:8080. Remoto = VPN (nunca expor à internet). 1º acesso por magic link no e-mail → define a senha.

// Adicionar device → IP:porta
painel: testar todos os recursos
Rede

Bidirecional

  • Ennor → device TCP 80 (a .fcgi)
  • device → Ennor 8080 (Monitor/Online)
  • Equipamentos em VLAN isolada (sem TLS)
  • Só o host do Ennor toca os devices
Segurança

Produção

  • Login deny-by-default · BCrypt · magic link no 1º acesso
  • Credenciais cifradas AES-256-GCM
  • ENNOR_MONITOR_TOKEN forte na ingestão
  • UI atrás de HTTPS (reverse proxy)
Sem hardware

Simulador

  • Device falso que fala a .fcgi
  • O time pratica o painel de teste
  • Troca para o device real por IP depois
  • Nada mais muda
Diagnóstico & validação de rede. Cada device tem um diagnóstico completo (dispara todos os comandos, mede a latência de cada um). E há um probe de rede baixável na tela Diag. rede — roda de um notebook na ponta (Windows/PowerShell ou curl, sem instalar Java, offline na LAN) e valida latência/perda/jitter antes de plugar o hardware.
Guia completo: DEPLOY.md, CONEXAO.md e README.md no repositório. Faça backup da pasta data/ (store operacional + chave-mestra).