Keychain, Kuma e tour interativo — sete frentes hardware-free num dia

Tuner agora guarda token no Keychain do OS, smoke:prod publica heartbeat no Uptime Kuma, app mobile ganhou tela Connect, dashboard sim ficou com 5 telas, /sobre virou tour visual. Mais um dia sem hardware com leverage real.

O hardware Fase 0 ainda em trânsito. Dia inteiro de leverage hardware-free — sete commits, sete frentes, todas amarrando coisas que estavam soltas.

Tuner desktop — token no Keychain do OS

Até agora, o cloudState do Tuner guardava apiBase + deviceId + token em localStorage. Funciona, mas em produção (Tauri shell) localStorage é o WebView local — não criptografado, fácil de extrair com qualquer ferramenta de inspeção de perfil. Apps profissionais usam Keychain (macOS) / Credential Manager (Windows) / Secret Service (Linux).

Adicionei a crate keyring = "3" no backend Rust e expus três comandos Tauri: secret_get, secret_set, secret_delete. O frontend tem um wrapper único (secretGet/Set/Delete) que detecta o runtime via __TAURI_INTERNALS__ e roteia automaticamente:

  • Em produção (Tauri shell) — vai pro keychain nativo do OS.
  • Em vite dev (browser) — fallback transparente pra localStorage.
  • Em vitest (node) — mesmo fallback, sem precisar mockar.

A loja Svelte continua inicializando síncrono pelo LS (rune Svelte exige sync), e logo após boot o App.svelte dispara hydrateSecretsFromKeychain() num $effect que substitui os valores em memória pelos do keychain quando ele tem coisa nova.

Empacotei o trade-off em ADR-013. A crate keyring ganhou em cima de tauri-plugin-stronghold (que exige password do usuário no boot — UX ruim pra ferramenta) e keytar (descontinuado em 2024).

Garage — heartbeat no Uptime Kuma

O pnpm smoke:prod rodando diariamente em GitHub Actions tinha um problema sutil: se quebrasse de madrugada, o sinal era um email do GitHub. Email do GitHub vai pra spam. Resultado: descobre que a API caiu quando alguém abre o painel de Actions.

Mas a vps-pomatti já roda Uptime Kuma servindo 5+ apps próprios (Frigoo, Lumê, etc.). É o canal que eu já consulto no celular pra ver estado do ecossistema. Então o smoke virou push monitor: depois de rodar os 5 checks, ele faz GET ${UPTIME_KUMA_PUSH_URL}?status=up|down&msg=…&ping=… com status do conjunto, mensagem one-liner com nomes dos checks que falharam, e ping médio em ms.

Detalhe importante: falha pra alcançar o Kuma é não-fatal. Se o Kuma cair ou o cron rodar sem internet, o smoke ainda exit-0 com 5/5 verde. A verdade do produto não depende da saúde do monitor.

Documentado em ADR-014 com as alternativas que descartei (Datadog Synthetic, Better Stack — US$ 30-100/mês quando já tenho Kuma free; Prometheus — over-engineering pra escala atual).

Pistonix Ride — tela Connect

O ConnectionRepository (ADR-012) tinha 4 testes verdes mas nenhuma tela usava ainda. Agora tem: nova tela Connect com círculo grande de estado (160×160), cor mudando por estado da máquina (idle cinza / connecting+connected laranja Pistonix / streaming verde / lost+error vermelho), spinner durante connecting, botão Conectar/Desconectar/Cancelar contextual ao estado.

O main.dart agora monta um MockConnectionRepository(telemetry: MockTelemetryRepository()) e auto-conecta no boot — o app abre direto em “streaming” depois de ~500ms de handshake mock. Quando BLE for real, troca uma linha no boot (MockConnectionRepository → BleConnectionRepository) e nada mais muda.

Três widget tests novos cobrem render-idle, transição on-tap e o caminho de erro do BLE stub (que joga UnimplementedError no connect() até Phase 5+).

Dashboard sim — Settings + Datalog

O simulador SDL2 do dashboard tinha 3 telas (home, race, diagnostic). Adicionei 2: Settings (slider de brilho 10-100%, switches mock km/h↔mph e noite↔dia, status ”● pareado” do Garage) e Datalog (lista mockada de 5 sessões com bike/duração/tamanho/sync indicator).

Tecla S toggla settings, tecla D toggla datalog. Cada toggle hide todas as outras telas — overlay mutex explícito, sem stack de modais. TAB e R continuam toglando diagnostic e race respectivamente.

MOCK_ENTRIES[] é um array static no datalog.c; quando o cloud client real entrar (Phase 5+), substitui pelo payload do GET /telemetry.

Web /sobre — tour interativo

O /sobre ganhou seção “Como funciona — Cinco peças que conversam”: 5 cards numerados (01-05) descrevendo Forge (ECU) → Dash (display) → Ride (app) → Garage (cloud) → Tuner (desktop), conectados por setas horizontais que mostram o protocolo de cada link (CAN bus 500kbps, BLE GATT, HTTPS, USB · HTTPS).

Hover anima translateX(4px) + border vermelho, gradiente nas linhas horizontais entre cards, seta apontando pra baixo no fim. Build completo em 75s, sem regressão de Lighthouse.

ADR-012 — ConnectionRepository pattern

Documentei o pattern do ConnectionRepository (que extende TelemetryRepository em vez de viver paralelo) em ADR-012. Justifica o trade-off contra duas alternativas que rejeitei: estados de transporte dentro do TelemetryRepository (acopla coisas que não pertencem juntas) e bus global de eventos (singleton implícito que re-introduz o acoplamento que ADR-010 tinha rejeitado).

STATE.md

Sincronizado com a batch — entrada 2026-05-06 batch (hardware-free, continuação) lista os 4 commits da janela e atualiza totais de cobertura (200 ECU + 67 integration cloud + 22 Rust+8 vitest tuner + 11 Ride).

O que NÃO fez parte desta sessão

  • Mobile BLE de verdade — depende de hardware Dash existir.
  • Telemetry binary upload no Garage — schema decidido, handler ainda é stub.
  • DTC details modal no dashboard sim — entrou na próxima fila.

Mas os pontos soltos do dia anterior (token em LS, smoke sem broadcast, ConnectionRepository sem UI consumindo, dashboard com 3 telas mas sem settings/datalog) ficaram todos amarrados. Um dia hardware-free com leverage real — não é trabalho que aparece no Lighthouse, mas é a diferença entre “abre a app e funciona” e “abre a app e funciona, e é fácil de operar”.

Lista de espera

Quer saber em primeira mão.

Pistonix está em desenvolvimento ativo. Inscreva-se pra receber atualizações de produto, basemaps prévios, convites pra rodar nos eventos do Milwaukee Garage Drag Racing e prioridade na lista de compra do Pistonix Forge.

  • Atualizações mensais de roadmap e firmware
  • Convite para sessões de tuning na pré-venda
  • Prioridade para o lote inicial do Pistonix Forge
  • Acesso antecipado a basemaps por motor

Ao se inscrever você concorda em receber emails do Pistonix. Não compartilhamos seu email. Você pode descadastrar a qualquer momento. (LGPD)