Entrixy v2 — arquitetura final

Acordado a 13 de abril de 2026. Documento de trabalho para a implementação.

Conteúdo
  1. Privacidade: o servidor não sabe nada
  2. Quatro tipos de objeto: chamada, URL, dispositivo (WS), controlador BLE
  3. O webhook (URL) em detalhe
  4. O dispositivo (ESP32 / ESP8266, WebSocket) em detalhe
  5. O controlador BLE (ESP32) em detalhe
  6. Gatilhos: GPS e Wi-Fi
  7. Três níveis de confirmação
  8. Proprietário contra convidado — a divisão das definições
  9. A interface da aplicação
  10. Limites do plano
  11. Ícones de objeto para convidados
  12. Previsão de carga
  13. Estrutura da base de dados
  14. Plano de implementação

1. Privacidade: o servidor não sabe nada (por omissão)

A promessa que fazemos: Não conhecemos nem os telefones dos nossos utilizadores nem as suas coordenadas

Todos os dados sensíveis ficam apenas no telefone. Parte deles o proprietário pode, se quiser, passar pelo servidor — com um aviso explícito.

DadosPor omissãoOpcionalmente pelo servidor
Coordenadas (lat, lon)Apenas no telefonePode ser partilhado com convidados (com aviso)
Impressão Wi-Fi (BSSID[])Apenas no telefoneNunca é transmitida
URL e segredo do webhookApenas no telefonePode ser guardado no servidor (com aviso)
Raio, tempo, nível de confirmaçãoPelo servidor
Uma impressão Wi-Fi equivale a coordenadas. A partir de um conjunto de BSSID, os serviços localizam-no com 20–50 m de precisão. A imagem Wi-Fi não passa pelo servidor em circunstância alguma.

2. Quatro tipos de objeto

Ao adicionar um objeto, o proprietário escolhe o tipo de ligação. A seguir abre-se a janela de definições desse tipo.

IconNomeComo funcionaFeedbackExample
📞CallUma chamada a partir do telefone do proprietárioNenhum (a chamada saiu ou não)Uma cancela, um intercomunicador
🌐URL (webhook)Um pedido HTTP para o endereço do dispositivo, a partir do telefone ou do servidor EntrixySim — uma resposta JSON com estadoUma fechadura inteligente com API, Tasmota, Shelly Cloud
📟Controlador de internet (WS)Um comando por WebSocket a um controlador ligadoSim — uma resposta pelo WebSocketESP32 / ESP8266 caseiros, Shelly Plus 1, NodeMCU
📶Controlador BLEUm canal Bluetooth direto entre telefone e controlador, sem internetSim — uma resposta por BLE (notify)Um controlador a pilhas num portão sem Wi-Fi

O configurador de firmware para os dois últimos tipos: /controller/ (BLE e Internet, no navegador).

Para o utilizador todos os tipos são iguais: um cartão, um botão Ligar, um estado. Só o mecanismo de entrega difere.

3. O webhook (URL) em detalhe

Dois modos de envio

A janela de definições do webhook tem um seletor, com as vantagens e desvantagens escritas no ecrã:

◉ A partir do telefone do proprietário (por omissão)
O pedido sai do seu telefone. O endereço e o segredo do dispositivo ficam consigo — o servidor Entrixy nunca os fica a saber.
A favor: privacidade máxima
Contra: o seu telefone tem de estar em linha
○ A partir do servidor Entrixy
O pedido é enviado pelo nosso servidor. Funciona mesmo com o seu telefone offline.
A favor: funciona sem o telefone do proprietário
Contra: o endereço do dispositivo fica guardado no servidor

Onde vivem os dados

ModeOnde estão o URL e o segredoO servidor conhece o URL?
A partir do telefonePrefs no telefone do proprietárioNo
A partir do servidorA tabela numbers na base de dadosYes

Protocolo do pedido

Segurança: uma assinatura HMAC. O dispositivo e o Entrixy partilham um webhook_secret.

POST https://device-url.com/open
Content-Type: application/json

{
  "action": "open",
  "object_id": 42,
  "timestamp": 1713020000,
  "nonce": "a1b2c3",
  "signature": "hmac-sha256(secret, timestamp + nonce + action)"
}

O dispositivo verifica que a assinatura é válida, que a marca temporal tem menos de 30 segundos e que o nonce não se repete. Se estiver tudo bem, atua e responde:

{"status": "ok", "message": "Door opened"}
or
{"status": "error", "message": "Lock jammed"}

O estado é mostrado ao utilizador na aplicação.

Fluxo de dados (modo telefone)

The guest presses Call
  → the server receives the request
  → sends it to the owner over the WebSocket: {type: "do_webhook", ...}
  → the owner's phone makes the HTTP call to the device
  → receives the JSON response
  → sends it to the server: {type: "webhook_result", status, message}
  → the server passes it to the guest
  → both see the status in the log

Logging

Em ambos os modos o registo no telefone do proprietário e do convidado anota quem, quando, que objeto e com que estado.

4. O dispositivo (ESP32 / ESP8266, WebSocket) em detalhe

Como funciona

O controlador — são suportados ESP32, ESP32-S3, ESP32-C3 e ESP8266 — não tem IP público. Mantém uma ligação WebSocket de saída ao servidor Entrixy, tal como o telefone do proprietário.

O firmware e o configurador vivem em /esp/socket/. Fontes: ESP32, ESP8266.

O protocolo WebSocket do dispositivo

// Device → server: connect
{"type": "device_hello", "device_key": "xyz789", "device_secret": "..."}

// Server → device: OK
{"type": "device_ok"}

// Server → device: command
{"type": "device_command", "action": "open", "command_id": "abc123"}

// Device → server: result
{"type": "device_response", "command_id": "abc123", "status": "ok", "message": "Opened"}

O servidor encaminha o estado aos clientes — proprietário e convidado — pelas suas ligações WebSocket.

Registar um dispositivo

  1. Na aplicação o proprietário escolhe Adicionar objeto → tipo Dispositivo
  2. Geram-se device_key + device_secret
  3. Mostra-se um código QR com os dados do firmware
  4. O utilizador grava o ESP32 ou o ESP8266 (ver o configurator) e o dispositivo liga-se a wss://entrixy.com/ws

Indication

O cartão do objeto mostra o estado de ligação do dispositivo, em linha ou offline, tal como os convidados veem o estado do proprietário.

4a. O controlador BLE (ESP32) em detalhe

Como funciona

Um controlador em ESP32, S3, C3, C6 ou H2 não precisa nem de Wi-Fi nem de servidor para disparar. Fica em sono profundo, acorda uma vez por segundo durante cerca de 200 ms e emite um anúncio BLE com um contador assinado. Quando se aproxima um telefone com uma chave válida, o Android — ou a aplicação, se estiver em primeiro plano — apanha o anúncio, liga-se por GATT, envia um comando fire com um nonce de uso único, e o controlador fecha o relé num impulso curto. Para motores biestáveis e fechaduras há um modo com abrir e fechar em separado e seguimento da posição, onde aberto/fechado viaja como um byte cifrado dentro do anúncio.

O servidor Entrixy não participa neste canal. Só é preciso ao criar uma chave de convidado: o proprietário assina um GuestToken e passa-o ao convidado pelo canal cifrado ponta a ponta. Depois o convidado trabalha offline.

O protocolo BLE em breve

A especificação completa do protocolo, a disposição dos bits e os vetores de teste estão em entrixy.com/ble. O modelo de ameaças está num documento à parte.

Quando o BLE ganha ao WebSocket

Quando o WebSocket ganha ao BLE

Pode pôr BLE e WebSocket no mesmo motor — na aplicação aparecem como dois objetos distintos. BLE para abrir de perto sem internet, WebSocket para quando está longe.

Registar um dispositivo

  1. Na aplicação o proprietário escolhe Adicionar objeto → tipo Controlador BLE
  2. A aplicação varre o ar à procura de dispositivos em modo de emparelhamento (com o nome entrixy-pair)
  3. Escolher um dispositivo lança uma troca ECDH por GATT; o segredo partilhado é guardado nas Prefs do telefone e na NVS do controlador
  4. Nem código QR nem carregar botões — um dispositivo novo entra sozinho em modo de emparelhamento assim que o programa é carregado

O firmware e o configurador vivem em /esp/ble/. Fonte: /esp32-example/.

Indication

No cartão do objeto: cinzento significa não visível no ar, longe demais ou desligado; laranja, visível mas a automação não disparou; verde, disparou ou está a disparar; cinzento com contorno verde, disparou há pouco e pode tocar-se para abrir de novo.

Database

CREATE TABLE devices (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  host_id INT UNSIGNED NOT NULL,
  device_key VARCHAR(64) NOT NULL UNIQUE,
  secret_hash VARCHAR(64) NOT NULL,
  label VARCHAR(128) DEFAULT '',
  last_seen DATETIME NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

In server.php acrescenta-se um terceiro papel de WebSocket: device (ao lado de host e guest).

5. Gatilhos: GPS e Wi-Fi

O GPS e o Wi-Fi funcionam de forma independente. Se ambos estiverem configurados, dispara a condição que se cumprir primeiro. A pausa comum entre disparos é de 20 segundos.

GPS — tal como está hoje

Wi-Fi — dois modos

Modo 1: a imagem Wi-Fi (impressão)

Modo 2: ligado a uma rede

Consumo de energia

Armazenamento (Prefs, nunca sai do telefone)

wifi_trigger_<actionKey> = {
  "mode": "fingerprint" | "connect",
  "bssids": ["AA:BB:CC:DD:EE:FF", ...],
  "min_match": 5,
  "connect_bssid": "AA:BB:CC:DD:EE:FF",
  "connect_ssid": "TP-Link_5G",
  "enabled": true
}

6. Três níveis de confirmação

O proprietário decide se é preciso confirmação. Como se confirma a identidade fica a cargo do cliente, com o que o dispositivo oferecer.

LevelO que o utilizador vêExample
AutomaticNada — o objeto abre-se sozinhoCancelas, portões
ConfirmUma notificação na cortina: Abrir [objeto]? → um toqueUma garagem, um portinhola, proteção contra disparos falsos
Confirmar a identidadeBiometria, o PIN do telefone ou o PIN da aplicaçãoUma porta de entrada

Confirmar não é um passo desperdiçado. A notificação aparece sozinha na cortina no momento certo. Sem procurar a aplicação, sem desbloquear, sem encontrar o objeto. Um toque e a porta está aberta.

Prioridade dos métodos de confirmação de identidade

  1. Biometria, se houver sensor → BiometricPrompt
  2. O PIN ou a palavra-passe do telefone, quando não há biometria → KeyguardManager
  3. O PIN próprio da aplicação, num dispositivo sem bloqueio → o nosso próprio diálogo

A janela horária — um fator adicional

O proprietário define Horas permitidas: 07:00–23:00. Fora dessa janela a condição é ignorada em silêncio. Um intervalo noturno também funciona (22:00–06:00).

Três fatores de proteção independentes

  1. WHERE — GPS, uma impressão Wi-Fi ou uma ligação
  2. WHO — automático, confirmação ou biometria
  3. WHEN — a janela horária
Uma porta de entrada: impressão Wi-Fi mais confirmação de identidade mais 07:00–23:00 — mais sólido do que a maioria das fechaduras inteligentes.

7. Proprietário contra convidado — a divisão das definições

SettingProprietário (o seu)Proprietário → convidadosConvidado
Tipo de objetoChoosesTransmiteReceives
CoordinatesDefine-osOpcional (com aviso)Define-os ou recebe-os
Raio do GPSDefine-oTransmiteReceives
Impressão Wi-FiScansNeverVarre ele próprio
Correspondências mínimas de Wi-FiDefine-oTransmiteReceives
Janela horáriaDefine-oTransmiteReceives
ConfirmaçãoDefine-oTransmiteReceives
GPS ligado/desligadoyesyes
Wi-Fi ligado/desligadoyesyes

8. A interface da aplicação

Adicionar um objeto (SettingsScreen)

O primeiro passo é escolher o tipo de ligação:

┌────────────────────────────┐
│  Connection type           │
│                            │
│  📞  Call                  │
│  Opening with a phone      │
│  call                      │
│                            │
│  🌐  URL                   │
│  Sending a command to      │
│  a device address          │
│                            │
│  📟  Device                │
│  Connected through Entrixy │
│  (ESP32, Arduino)          │
└────────────────────────────┘

Após a escolha abre-se a janela de definições desse tipo.

O cartão de objeto expandido (ActionCard)

┌──────────────────────────────────────────┐
│  Corner barrier             📶 3/5  312m │
│  Mine • ●                                │
├──────────────────────────────────────────┤
│  [Edit]  [Icon]                          │
│  ─────────────────────────               │
│  [📍 Geo]   [📶 Wi-Fi]                   │
└──────────────────────────────────────────┘

A janela 📍 Geo

A janela 📶 Wi-Fi

◉ Pela imagem Wi-Fi

○ Pela ligação a uma rede

Por baixo: a janela horária e a confirmação, comuns com o Geo

A janela do webhook (🌐 URL)

◉ A partir do telefone (por omissão)
O URL fica apenas no seu telefone. O servidor Entrixy nunca fica a sabê-lo.
A favor: privacidade máxima
Contra: o seu telefone tem de estar em linha
○ A partir do servidor Entrixy
O pedido é enviado pelo servidor. Funciona sem o seu telefone.
A favor: funciona sempre
Contra: o endereço do dispositivo fica guardado no servidor

Indicação no cartão recolhido

TriggerIndicator
GPS"312 m"
Impressão Wi-Fi📶 barras (0–4 conforme correspondências)
Ligação Wi-Fi📶 verde ou cinzento
Webhook/Device● verde (em linha) ou cinzento (offline)

9. Limites do plano

Planos internos. Não são mostrados ao utilizador, mas os limites aplicam-se.

Plano por omissãoLimit
Objetos (numbers)10
Convidados (user_keys)10

Ao ultrapassar, uma mensagem suave: O número de objetos é limitado. Contacte o suporte para o aumentar.

CREATE TABLE plans (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(64) NOT NULL,
  max_numbers INT NOT NULL DEFAULT 10,
  max_keys INT NOT NULL DEFAULT 10
);
INSERT INTO plans (name) VALUES ('default');

ALTER TABLE hosts ADD COLUMN plan_id INT UNSIGNED DEFAULT 1;

10. Base de dados — o conjunto completo de alterações

-- Plans
CREATE TABLE plans (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(64) NOT NULL,
  max_numbers INT NOT NULL DEFAULT 10,
  max_keys INT NOT NULL DEFAULT 10
);
INSERT INTO plans (name) VALUES ('default');

ALTER TABLE hosts ADD COLUMN plan_id INT UNSIGNED DEFAULT 1;

-- Object type and settings
ALTER TABLE numbers
  ADD COLUMN type ENUM('call','webhook','device') DEFAULT 'call',
  ADD COLUMN radius INT DEFAULT 80,
  ADD COLUMN time_from TIME NULL,
  ADD COLUMN time_to TIME NULL,
  ADD COLUMN security_level ENUM('auto','confirm','identity') DEFAULT 'auto',
  ADD COLUMN geo_available TINYINT(1) DEFAULT 1,
  ADD COLUMN wifi_available TINYINT(1) DEFAULT 1,
  ADD COLUMN wifi_min_match INT DEFAULT 5,
  ADD COLUMN share_lat DOUBLE NULL,
  ADD COLUMN share_lon DOUBLE NULL,
  ADD COLUMN webhook_url VARCHAR(512) NULL,
  ADD COLUMN webhook_secret VARCHAR(64) NULL,
  ADD COLUMN webhook_mode ENUM('phone','server') DEFAULT 'phone',
  ADD COLUMN device_id INT UNSIGNED NULL;

-- Devices (ESP32)
CREATE TABLE devices (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  host_id INT UNSIGNED NOT NULL,
  device_key VARCHAR(64) NOT NULL UNIQUE,
  secret_hash VARCHAR(64) NOT NULL,
  label VARCHAR(128) DEFAULT '',
  last_seen DATETIME NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

share_lat / share_lon — preenchidos só se o proprietário tiver ativado Partilhar coordenadas com os convidados.

webhook_url / webhook_secret — preenchidos só no modo servidor. No modo telefone vivem nas Prefs.

11. Ícones de objeto para convidados

O proprietário põe um ícone próprio num objeto e o convidado deve vê-lo. Mas o ícone não é guardado no servidor — o servidor faz de carteiro: entrega e esquece.

Preparação do lado do proprietário

Ao ser definido, o ícone é logo reduzido para 64×64 PNG (cerca de 3–5 KB) e guardado localmente nas Prefs ou num ficheiro.

Como viaja

1. The guest connects
   → guest_ok carries has_avatar: true for objects that have an icon

2. The guest client checks: has_avatar=true but no icon locally?
   → Yes → it asks the server

3. The server has no icon
   → it asks the owner over the WebSocket: {type: "avatar_request", number_id: 42}

4. The owner's phone
   → reads the local file
   → sends: {type: "avatar_data", number_id: 42, data: "base64..."}

5. The server passes it to the guest
   → {type: "avatar_data", number_id: 42, data: "base64..."}

6. The guest stores it locally and never asks again

7. The server stored nothing — the data merely passed through

Casos-limite

SituationBehaviour
O proprietário está offlineO convidado vê o ícone por omissão até o proprietário voltar a ficar em linha.
50 convidados perguntam ao mesmo tempo50 × 5 KB = 250 KB pelo WebSocket — insignificante.
O proprietário muda o íconehas_avatar_hash muda e os convidados voltam a pedi-lo
O proprietário apaga o íconehas_avatar: false e os convidados mostram o ícone por omissão
A privacidade mantém-se. O ícone de uma cancela não são dados sensíveis, mas mesmo assim não assenta no servidor: passa em trânsito pelo WebSocket e nunca é escrito em disco.

12. Previsão de carga

Um proprietário ativo gera

Um convidado ativo

Scaling

SubscribersEm linha em simultâneo (~15 %)Ligações WebSocketServer
1 000~150~150Easy
10 000~1 500~1 500Easy
50 000~7 500~7 500um trabalhador no limite
100 000~15 000~15 0002–3 trabalhadores

Workerman num só trabalhador: 5000–10 000 ligações simultâneas, de cerca de 20 KB cada.

Webhook e dispositivo

O estrangulamento

é o MySQL, não os WebSockets. Mas 250 000 inserções por dia — 50 mil subscritores × 5 chamadas — não são nada para o MySQL num SSD.

Conclusion: до 50 000 абонентов текущий сервер справится без изменений. После — увеличиваем worker->e acrescentar índices.

13. Plano de implementação

  1. Database — planos, as novas colunas em numbers e hosts, a tabela devices
  2. Limites no servidor — verificações em number_add e key_create
  3. Tipo de objeto — escolher o tipo ao adicionar, com diálogos diferentes
  4. API — host_sync e guest_ok com os novos campos de perfil
  5. Webhook — o protocolo HMAC, dois modos de envio, o estado da resposta
  6. WS do dispositivo — device_hello, device_command e device_response no server.php
  7. O gatilho Wi-Fi — impressão e ligação
  8. Janela horária — time_from/time_to
  9. Confirmação — três níveis: automático, confirmar, confirmar a identidade
  10. Icons — entrega em trânsito pelo WebSocket, comprimida para 64×64
  11. UI — os botões Geo e Wi-Fi, os diálogos, os indicadores, o seletor de tipo
  12. Partilha de coordenadas — uma opção com aviso
  13. Compilar e publicar
PWA: O gatilho Wi-Fi não está disponível — os navegadores não têm API de Wi-Fi. O webhook e o dispositivo funcionam pela PWA: botão → servidor → dispositivo. A janela horária e a confirmação só existem na aplicação nativa.
Retrocompatibilidade: quase não há clientes antigos. Fazemos como for cómodo e, se preciso, forçamos a atualização por version_check.

Entrixy — arquitetura final v2, acordada a 13 de abril de 2026