diff --git a/docs/superpowers/specs/2026-07-13-opc-gateway-design.md b/docs/superpowers/specs/2026-07-13-opc-gateway-design.md index 8e64a0d..4b22711 100644 --- a/docs/superpowers/specs/2026-07-13-opc-gateway-design.md +++ b/docs/superpowers/specs/2026-07-13-opc-gateway-design.md @@ -58,7 +58,7 @@ Mongo `sientia`: - **Segurança já é `Basic256Sha256`**; cert de plataforma **único**, baixado do MinIO (bucket `streamlit-connectors`, prefixo `platform/certificates/`) para temp e passado ao `asyncua`. - **Multi-nome por endereço já é permitido** (unicidade em `(connector_id, tag_name)`, nunca em `address`). -- **Não existem** `metadata`, `model_id/model_name`, `collection`, `frequency` em `connector_tags`. `metadata` é trabalho do subtask 1600 (pendente). +- **Não existem** `metadata`, `model_id/model_name`, `collection`, `frequency` em `connector_tags`. O subtask 1600 (pendente) adiciona `Category` + **atributos custom como chaves flat na raiz** do doc (não um campo `metadata` nested) — Vitor pediu um campo `metadata`, o que diverge do 1600 (ver §17). - Fix aplicado: `projects-templates/app/src/operations/db_infra.py:16` corrigido para o caminho local correto da lib (import validado). ### Escrita hoje (Laborious) @@ -139,7 +139,9 @@ ReadTagResponse { string value; string opc_status_code; string source_timestamp; Lê agora, persiste no pipeline e devolve. ## 7. Gestão de certificados -Reaproveita o 1569: `platform_certificates` (metadados) + MinIO (`streamlit-connectors`, `platform/certificates/`). O `OpcConnectionPool` baixa `client_cert`/`private_key` para **`emptyDir`/tmpfs** (root FS é read-only, §12) e passa ao `asyncua` com `Basic256Sha256`, espelhando `_create_opc_ua_client`. Cert de plataforma é único/compartilhado. **Questão de fronteira:** o bucket `streamlit-connectors` pertence ao app Streamlit; padrão de plataforma é bucket/secret scoped por serviço (§10/§12) — decidir se o Gateway lê desse bucket ou se ganha bucket `opc-gateway` próprio (ver §17). +Reaproveita o mecanismo do 1569: metadados em Mongo `platform_certificates` + arquivos no MinIO. O `OpcConnectionPool` baixa `client_cert`/`private_key` para **`emptyDir`/tmpfs** (root FS é read-only, §12) e passa ao `asyncua` com `Basic256Sha256`, espelhando `_create_opc_ua_client`. Cert de plataforma é único/compartilhado. + +**Bucket (default proposto):** a estratégia de MinIO (`03-database-storage/minio-strategy.md`, Bruno Domingues) é **bucket + user scoped por serviço**, e o bucket do domínio DataOps/`sientia-core` é **`sientia-dataops`**. Hoje o CRUD do 1569 grava os certs no bucket do frontend `streamlit-connectors`. Default do spec: o Gateway lê os certs de **`sientia-dataops`** (domínio dele); reconciliar com a plataforma **onde os certs passam a ser gravados** — mover a escrita do 1569 para `sientia-dataops`, ou conceder ao Gateway policy de leitura no bucket do frontend. Ver §17. ## 8. NATS como nova dependência de plataforma **Decisão: NATS** (definido pelo brief oficial `opc-gateway/topics.md` e confirmado pelo dono da feature, Vitor Pimentel — fonte mais atualizada). O padrão de subjects/queue groups do NATS casa com o comando de escrita ponto-a-ponto de baixa latência. @@ -166,7 +168,7 @@ Nota de plataforma: os docs air-gapped diziam *"do not include RabbitMQ/NATS unl - **NetworkPolicy default-deny:** liberar egress p/ PG/Mongo/Redis/MinIO/**NATS**/servidores OPC (4840) e ingress do Prometheus (9090); GRPC exposto é **net-new** (hoje o único gRPC da plataforma é o do Temporal) → adicionar regra. - **Migração de banco (C3):** **Flyway** (repo `Aignosi/sientia-dataops-database-migrations`, schema `sientia_data`; substituiu Alembic/SQLAlchemy). Coluna nova = `.sql` versionado (V2+), imutável, aplicado por Job Helm hook `pre-upgrade`; backward-compatible. - **Imagem:** assinada (Cosign) e escaneada (Trivy) no bundle; tags imutáveis (SemVer/SHA), nunca `latest`; `Chart.lock` commitado. -- **MinIO:** padrão é bucket + user scoped por serviço; existe bucket `opc-ingestor`. Ver fronteira de certs em §7/§17. +- **MinIO:** padrão é bucket + user scoped por serviço; o bucket do `sientia-core`/DataOps é **`sientia-dataops`**. Ver fronteira de certs em §7/§17. ## 11. Convenções de desenvolvimento - **Nome de branch dirige versionamento e é checado no CI:** `feature/`→minor, `fix/|hotfix/|bugfix/`→patch, `release/`→major, `rc/`, etc.; descrição em kebab-case; **branch fora do padrão falha o CI**. ✅ Branch já renomeada para `feature/SIENTIAPDE-1972-...`. @@ -205,7 +207,8 @@ Nota de plataforma: os docs air-gapped diziam *"do not include RabbitMQ/NATS unl **Dependências externas:** banco de tags 1569 no Mongo alvo; broker NATS provisionado (§8); bucket de certs no MinIO; `dataops-library` com métodos genéricos (1590) publicada/pinada; migração da tabela de predições (C3). ## 15. Trilhas paralelas (nível de contrato) -- **Orchestrator (1974):** montar slot v2 do banco de tags (JOIN `connector_tags` × modelo), propagar `model_id/model_name/collection`, multi-nome. Alvos: `orchestrator/utils/orchestrator_functions.py` (`gather_read_tags`, `build_tag_config`), `orchestrator/activities/formatters.py` (`process_slots`), `orchestrator/activities/slot_manager.py`. +- **Orchestrator (1974):** montar slot v2 do banco de tags, propagar `model_id/model_name/collection`, multi-nome. **Integração é net-new** (o orchestrator hoje só usa `connectors_config` de conexão e `pipelines.read_tags` embutidas; não toca `connector_tags`). Alvos: `orchestrator/utils/orchestrator_functions.py` (`gather_read_tags`, `build_tag_config`), `orchestrator/activities/formatters.py` (`process_slots`), `orchestrator/activities/slot_manager.py`. + - **Mecânica proposta do JOIN:** `connector_tags` **não tem** `model_id`/`collection`/`frequency`. Então: (a) **identidade da tag + conexão** (address, data_type, `tag_name`, `category`, url/server_uri/security) vêm de `connector_tags`+`connectors` — fonte única; (b) **associação modelo→tag + `collection` + `frequency`** continua vindo do lado modelo/pipeline (hoje `collection = raw_{schedule_name}`, `frequency` no read config). **Chave de junção a confirmar com Vitor:** casar por `(connector/server + address)` (menos migração) **ou** o pipeline passar a referenciar `connector_id`/`tag_id` explicitamente. - **Laborious (1975):** publicar no NATS (C2) em vez de `node.write_value`; gravar `write_status` (C3) nas escritas próprias (PI/simples); remover sentinelas `12/13/14` de `process_confidence`/`api.py`. ## 16. Testes @@ -219,14 +222,22 @@ Nota de plataforma: os docs air-gapped diziam *"do not include RabbitMQ/NATS unl - **Broker = NATS** (Core NATS agora; §8). Definido pelo brief/Vitor. - **Nome da branch:** renomeada para `feature/SIENTIAPDE-1972-...` ✅. -**Em aberto:** -1. **Bucket de certs:** Gateway lê do bucket `streamlit-connectors` (do app 1569) ou ganha bucket `opc-gateway` scoped? Default do spec: reutilizar o cert de plataforma do 1569 (MinIO+Mongo, conforme brief); confirmar scoping com plataforma. (§7/§10) -2. **Provisionamento do NATS no air-gapped:** alinhar com plataforma o subchart/versão/sizing (a *escolha* já é NATS; falta o provisionamento). (§8) -3. **Stack de migração de banco (RESOLVIDO):** **Flyway** — repo `Aignosi/sientia-dataops-database-migrations`, schema Postgres `sientia_data` (substituiu o antigo Alembic/SQLAlchemy). A coluna de write_status vira um `.sql` versionado (V2+) nesse repo, aplicado por Job Helm hook. Confirmar apenas o nome exato do schema/tabela de predições em produção (`sientia_data` vs `predictions_schema`). -4. **Campo `metadata` em `connector_tags`** (subtask 1600): formalizar; tags de teste criadas com `metadata: {}` (pedido do Vitor). -5. **Durabilidade da escrita:** Core NATS agora; reavaliar JetStream (TTL curto p/ descartar comando obsoleto) se perda em restart for problema. -6. **Origem de `collection`/`frequency`/`active_read`** no JOIN do 1974: confirmar (pipeline vs config de modelo) ao detalhar o spec do Orchestrator. -7. **Chamador do GRPC on-demand:** confirmar quem chama (Laborious/Orchestrator/UI-API) p/ dimensionar auth/limites. +**Resolvidas nesta rodada (lendo docs/código):** +- **Migração = Flyway** — repo `Aignosi/sientia-dataops-database-migrations`, schema Postgres `sientia_data` (substituiu Alembic/SQLAlchemy). Coluna de write_status = `.sql` versionado (V2+), Job Helm hook. Falta só confirmar o nome exato da tabela de predições em prod (`sientia_data` vs `predictions_schema`). +- **Bucket de certs (default) = `sientia-dataops`** — padrão de MinIO é bucket scoped por serviço; DataOps usa `sientia-dataops`. Falta só a plataforma decidir *onde os certs passam a ser gravados* (mover escrita do 1569 ou dar policy de leitura). (§7) +- **JOIN do 1974:** mecânica proposta em §15 (identidade/conexão do banco de tags; associação/collection/frequency do pipeline). Falta só a *chave de junção* (address+server vs tag_id) — pergunta ao Vitor. +- **`metadata`:** o subtask **1600 salva atributos custom como chaves flat na raiz** do doc da tag (não um campo nested). Vitor pediu um **campo `metadata`** — há **discrepância** a confirmar (nested `metadata` vs chaves flat). Pro gateway: tags de teste com `metadata: {}`. + +**Em aberto (precisam de humano):** +1. **Provisionamento do NATS no air-gapped:** subchart/versão/sizing/network policy com a plataforma (a *escolha* já é NATS). (§8) — **plataforma/Bruno** +2. **Escrita canônica dos certs:** mover 1569→`sientia-dataops` ou policy cross-bucket. (§7) — **plataforma** +3. **Chave de junção do 1974:** `(server+address)` vs `connector_id/tag_id` no pipeline. (§15) — **Vitor** +4. **`metadata`:** nested vs chaves flat (discrepância 1600 × pedido do Vitor). — **Vitor** +5. **Chamador do GRPC on-demand:** quem chama `ReadTag` e modelo de auth. — **Vitor** +6. **Tabela de predições em prod:** schema/nome exato para a migração Flyway. — **Vitor / dono do repo de migrations** + +**Adiada por design (nós):** +- **Durabilidade da escrita:** Core NATS agora; reavaliar JetStream (TTL curto) se houver perda real em restart. ## 18. Riscos - **Introdução de NATS** (decidida): a diretriz antiga "não adicionar NATS sem requisito" é atendida — o Gateway é o requisito; resta cumprir o onboarding de plataforma (§8/§10).