# IronDome — Handoff Document

**Versão atual**: 1.14.0 (2026-04-24)
**Público-alvo**: outro agente Claude que vai continuar o desenvolvimento.

---

## 0. TL;DR em 30 segundos

IronDome é um sistema local (roda no Mac do cliente) que observa o jogo
Aviator da Spribe via Brave Browser + DevTools Protocol, analisa padrões
de crashes históricos e executa **auto-click** de apostas com cashout no
target configurado. Tem licenciamento comercial online (chaves HMAC),
auto-updater via Vercel, telegram para notificações e um backend HTTP
local (porta 8766 TLS) que serve o dashboard + APIs.

O produto é distribuído como ZIP baixável em
<https://irondome-site.vercel.app>. Clientes instalam, colam a chave e
usam até expirar. Sem chave válida, sistema trava.

---

## 1. Arquitetura

### 1.1 Componentes locais (na máquina do cliente)

```
~/estudos/aviator-signals/
├── server.py            # HTTP server local (porta 8766 TLS). Serve dashboard + APIs.
├── autoclick_daemon.py  # Daemon que observa Brave via CDP e executa cliques
├── daemon_dom.py        # Daemon legacy (DOM scraper do iframe Spribe)
├── license.py           # Gate de licenciamento (HMAC online + grace 24h)
├── updater.py           # Auto-updater (baixa ZIP, extrai, preserva dados)
├── db.py                # SQLite helpers (data/aviator.db)
├── version.py           # VERSION constant (SemVer)
├── daemon_cdp.py        # Biblioteca CDP WebSocket
├── static/index.html    # Dashboard SPA (tabs: PAINEL 10x, ANÁLISE PRO, AUTO-CLICK, TELEGRAM, SOBRE)
├── install.sh           # Instalador (brew, python3.12, Brave, cert TLS)
├── start.sh             # Sobe Brave com CDP + server + daemons
├── stop.sh              # Mata processos
├── restart.sh           # Restart pós auto-update
├── data/
│   ├── aviator.db       # SQLite (crashes, bets). Persiste entre restarts.
│   ├── license.json     # Chave ativada + token (24h TTL)
│   └── telegram.json    # Config bot token + chat + preferências
├── _snapshots/          # Backups pré-edição (rollback local)
├── cert.pem, key.pem    # TLS self-signed pro server local
├── CHANGELOG.md         # SemVer history
└── HANDOFF.md           # este documento
```

**Arquivos legacy** (não usados pela v1.14.0 mas ainda presentes): `daemon.py`,
`inject.py`, `poller.py`. Manter por compatibilidade com installs antigos.

### 1.2 Componentes remotos (Vercel, projeto `irondome-site`)

```
~/estudos/irondome-site/
├── public/
│   ├── index.html            # Landing page de download
│   ├── admin.html            # Admin UI (/admin) protegido por senha
│   ├── irondome-1.14.0.zip   # Pacote versionado
│   ├── irondome-latest.zip   # Cópia do pacote mais recente
│   ├── version.json          # Manifesto consumido pelo updater
│   └── CHANGELOG.md          # Changelog público
├── api/
│   ├── _lib.js               # HMAC, gen/verify de keys e tokens, admin guard
│   ├── license/verify.js     # POST — cliente ativa ou revalida chave
│   └── admin/
│       ├── issue.js          # POST — emite chave nova (admin-only)
│       ├── parse.js          # POST — decompõe chave existente (admin-only)
│       └── debug.js          # ⚠ REMOVER: expõe env var lengths
└── vercel.json               # Headers, rewrites, CORS
```

**Deploy**: `cd ~/estudos/irondome-site && vercel deploy --prod --yes --scope dribeiro81s-projects`

---

## 2. Fluxo operacional completo

### 2.1 Observação de crashes (sempre rodando)
1. Brave Browser com `--remote-debugging-port=9222` aberto no site do Aviator.
2. `daemon_dom.py` se conecta via CDP, injeta helper JS no iframe da Spribe.
3. Helper JS intercepta WebSocket do jogo e extrai cada crash (valor multiplicador).
4. Daemon POSTa `/api/events` no server local com `{kind: 'crash', value: X}`.
5. `server.register_crash(value)` adiciona a `state['crashes']` (deque maxlen=2000)
   e a `db.insert_crash(value, ts)`.
6. Dashboard polla `/api/state` a cada 1s e atualiza UI.

### 2.2 Auto-click (quando user ativa BOT ON)
1. `autoclick_daemon.py` roda em background.
2. Main loop a cada 50-150ms:
   - Fetch `/api/state` (crashes + stats).
   - Fetch `/api/autoclick/config` (config do user salvo no localStorage).
   - Fetch `/api/autoclick/status` (panel_cooldowns, paused_until, shadow_stats).
   - Pra cada painel (0 e 1):
     - Lê estado do painel Spribe via CDP (phase, cashout button state).
     - Registra cooldown atual no server (pra UI mostrar countdown).
     - Resolve `pending_hits` (espera crash da rodada registrar no feed).
     - Se phase='betting' e painel livre: decide entrada (trigger + janela + IA).
     - Se phase='flight': confere se o JS arm clicou cashout (caminho feliz)
       ou fallback Python (mult >= target → clickCashout CDP).
     - Se miss: `_register_miss` com razão (fim do flight / invisível / phase return / timeout).
3. Eventos importantes (BET, HIT, MISS) são POSTados pro server que:
   - Atualiza `stats` (pnl_today, bets_today, loss_streak).
   - Atualiza `panels_stats` (V/D/IA counter).
   - Grava linha em `bets` (id, ts, panel, target, amount, ia_approved, pattern, probability, hit, delta_pnl, resolved_ts).
   - Envia notificação Telegram se configurado.

### 2.3 Decisão de entrada (quando `useIA=true`)
1. Daemon chama `ia_gate(target, panel, margin=X)` → bate em `/api/intelligence/gate`.
2. Server calcula prob estimada por pattern matching (ver seção 3).
3. Se prob >= threshold → `approve`. Senão → `reject`.
4. Daemon só coloca a aposta se aprovado.

### 2.4 Parada técnica (stop-loss / stop-win / streak)
1. Daemon detecta gatilho (ex: streak de N perdas).
2. POST `/api/autoclick/pause` com `{minutes: N, reason: "..."}`.
3. Server grava `paused_until = now + N*60`, zera shadow_stats, zera loss_streak.
4. Banner âmbar aparece no topo da UI com countdown.
5. Durante pausa, daemon continua simulando (shadow bets) pra alimentar pattern_bet_history.
6. Ao expirar: se shadow_hit_rate >= 50% e >= 5 amostras → retoma real.
   Senão → estende +10 min, mantendo shadow_stats.

### 2.5 Licenciamento (a cada startup + revalidação 6h)
1. Client chama `license.status()`.
2. Se não há chave → overlay "Ativar licença" bloqueia UI.
3. User cola chave → `POST /api/license/verify` no Vercel.
4. Vercel valida HMAC + expiração + blacklist → retorna token (24h TTL).
5. Cliente grava em `data/license.json`.
6. Middleware `_guard_licensed` no server local bloqueia todas as rotas `/api/*`
   (exceto version/updates/license/*) se não-licenciado.

### 2.6 Auto-update (aba SOBRE ou automático a cada 30min)
1. Client fetch `/api/updates/check` → server bate em `https://irondome-site.vercel.app/version.json`.
2. Se latest > current: UI mostra banner azul "Nova versão disponível".
3. User clica Atualizar → POST `/api/updates/install`.
4. `updater.install_update()`:
   - Backup em `_snapshots/pre-update-vX.Y.Z-timestamp/`.
   - Download `/irondome-latest.zip` do Vercel.
   - Extrai em `/tmp`.
   - Copia arquivos novos por cima, **preservando** `data/`, `_snapshots/`,
     `cert.pem`, `key.pem`.
   - Spawn `restart.sh` → mata processos antigos → sobe novos.
5. UI recarrega em 4s.

---

## 3. Sistema de aprendizado da IA (v1.14.0 SIMPLIFICADO)

### 3.1 Histórico
Até v1.13.x o sistema tinha 7 camadas empilhadas (pattern, calibração,
aprendizado global, auto-ajuste, margin, piso, pattern history). Era opaco
e tinha feedback loop ruim (perdia → apertava → não apostava → não aprendia).

Em **v1.14.0** removemos 3 camadas:
- ❌ CALIBRAÇÃO por faixa de prob
- ❌ APRENDIZADO GLOBAL por painel (win rate vs teórico)
- ❌ AUTO-AJUSTE dinâmico (margin/piso escalando por pnl)

E mantivemos + adicionamos:
- ✅ Pattern matching + Bayesian smoothing
- ✅ Pattern bet history (causal)
- ✅ Modo de operação (margem fixa: 5% / 10% / 15%)
- ✅ Piso absoluto 25%
- ✅ **Pattern blacklist temporário** (NOVO) — 3 losses seguidas → 2h timeout daquele padrão

### 3.2 Algoritmo de `evaluate_target_probability`

```python
# Entrada: target (ex: 2.0), panel (0 ou 1), margin_override (fração, ex: 0.10)
# Saída: {decision: approve|reject, probability, approve_threshold, reasoning, ...}

1. Fetch state['crashes'][-1000:] (janela 1000 rodadas)
2. p_base = sum(1 for v in vals if v >= target) / n   # baseline observado
3. best_pattern = _find_best_pattern(vals, target, min_support=3)
   - Quantiza cada crash em bucket: B (<1.3), M (<2), A (<5), V (≥5)
   - Pra k em (6, 5, 4, 3, 2):
     - Pega cauda atual (últimos k buckets)
     - Conta ocorrências históricas dessa mesma cauda
     - Calcula hit rate do crash seguinte
     - Score = rate × (1 − 1/(support+1))
   - Retorna o melhor candidato
4. Suavização Bayesiana (prior k=5 centrado no teórico):
   p_smoothed = (hits + 5 × teórico) / (support + 5)
   → evita 2/5 = 40% em padrão raro
5. p_adj = p_smoothed (ou p_base se não achou padrão)
6. APRENDIZADO CAUSAL (pattern_bet_history):
   - Query bets table pra esse pattern+target (±0.25x)
   - Se count >= 3, calcula real_rate smoothed
   - pattern_bet_corr = (real_smoothed − p_adj) × min(1, count/6)
   - p_adj += pattern_bet_corr
7. PATTERN BLACKLIST:
   - Query últimas 3 apostas desse pattern+target
   - Se todas miss e mais recente < 2h atrás:
     - blacklisted = True
     - força decision='reject'
8. Critério:
   - MARGIN_BASE = margin_override (fallback 0.10)
   - approve_threshold = max(theo × (1 + MARGIN_BASE), 0.25)
   - decision = 'approve' se p_adj >= threshold e não blacklisted
```

### 3.3 Modos de operação (dropdown por painel)

| Modo | Margem | Critério target 2x |
|---|---|---|
| Conservador | 15% | ~56.9% |
| **Equilibrado** (default) | 10% | ~54.4% |
| Agressivo | 5% | ~52.0% |

Mudar dropdown → save automático → próxima consulta usa novo critério.

### 3.4 Aposta dinâmica por confiança (opcional, por painel)

Se `useIA=true` e `confidenceSizing=true`:
- Se prob ≥ X% (ex: 85%) → aposta = base × multiplicador (ex: 5x)
- Se prob ≥ Y% (ex: 70%) → base × 2x
- Senão → base × 1

---

## 4. Estrutura do banco de dados (SQLite)

`data/aviator.db` com 2 tabelas + índices:

```sql
CREATE TABLE crashes (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  ts REAL NOT NULL,           -- timestamp UNIX
  value REAL NOT NULL         -- multiplicador do crash (ex: 2.47)
);
CREATE INDEX idx_crashes_ts ON crashes(ts);

CREATE TABLE bets (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  ts REAL NOT NULL,
  panel INTEGER NOT NULL,     -- 0 ou 1
  target REAL NOT NULL,
  amount REAL NOT NULL,       -- 0 em shadow bets (durante parada técnica)
  ia_approved INTEGER DEFAULT 0,
  hit INTEGER,                -- 1=win, 0=loss, NULL=pendente
  delta_pnl REAL,
  resolved_ts REAL,
  pattern TEXT,               -- cauda quantizada (ex: "AAB")
  probability REAL            -- prob estimada pela IA no momento
);
CREATE INDEX idx_bets_panel_resolved ON bets(panel, resolved_ts);
CREATE INDEX idx_bets_pattern ON bets(pattern, target);
CREATE INDEX idx_bets_panel_target_prob ON bets(panel, target, probability);
```

**Regra importante**: shadow bets (simuladas durante parada técnica) têm
`amount=0`. Helpers de AUTO-AJUSTE filtram `amount > 0` pra ignorá-las.

---

## 5. API endpoints

### 5.1 Server local (https://localhost:8766)

**Públicos (sem gate de licença)**:
- `GET  /` — dashboard SPA
- `GET  /CHANGELOG.md` — changelog servido como texto
- `GET  /snippet.js` — JS helper pro iframe
- `GET  /api/version` — `{version, release_name, release_date, db_totals}`
- `GET  /api/updates/check` — consulta site/version.json, compara com local
- `GET  /api/license/status` — estado da licença
- `POST /api/license/activate` — `{key}` → ativa
- `POST /api/license/deactivate` — reset licença local
- `POST /api/updates/install` — baixa e aplica update

**Protegidos (retornam 403 se não-licenciado)**:
- `GET  /api/state` — crashes + signals + stats
- `GET  /api/autoclick/status` — stats + config + events + panel_cooldowns + shadow_stats + paused_until
- `GET  /api/autoclick/config` — só o config
- `GET  /api/intelligence/gate?target=X&panel=N&margin=Y` — decisão de aposta
- `GET  /api/intelligence/state` — estado da inteligência ampla
- `GET  /api/analytics/ia` — top padrões, prob band, por target
- `GET  /api/control` — dados consolidados (inexistente após v1.13.1; endpoint ainda existe mas não usado)
- `POST /api/autoclick/config` — atualiza config
- `POST /api/autoclick/event` — daemon posta eventos
- `POST /api/autoclick/pause` — `{minutes, reason, extend?}` (minutes=0 cancela)
- `POST /api/autoclick/shadow` — daemon reporta resultado shadow
- `POST /api/autoclick/reset` — zera stats
- `POST /api/autoclick/panel_state` — daemon reporta cooldown de painel
- `POST /api/telegram/config` — salva config + grava em disco
- `GET  /api/telegram/config` — lê config
- `POST /api/telegram/test` — envia mensagem de teste (síncrono)
- `POST /api/intelligence/config` — config da inteligência
- `POST /api/events` — snippet posta crashes (`{kind:'crash',value}`)

### 5.2 Vercel (https://irondome-site.vercel.app)

**Estáticos**:
- `GET /` → landing (`public/index.html`)
- `GET /admin` → admin UI (senha via sessionStorage)
- `GET /version.json` → manifest do auto-updater
- `GET /CHANGELOG.md`
- `GET /irondome-1.14.0.zip`, `/irondome-latest.zip` → downloads

**Funções**:
- `POST /api/license/verify {key}` → `{ok, token, expires_at}` — PÚBLICO
- `POST /api/admin/issue {owner, days}` ou `{owner_id, days}` → `{key}` — admin
- `POST /api/admin/parse {key}` → decompõe — admin
- `GET  /api/admin/debug` → ⚠ REMOVER (audit C2)

**Autenticação admin**: header `x-admin-password: <valor>`.

---

## 6. Credenciais e secrets

| Onde | Nome | Como obter |
|---|---|---|
| Vercel env | `LICENSE_SECRET` | `vercel env ls` (cifrado; valor original no memory do usuário) |
| Vercel env | `ADMIN_PASSWORD` | idem |
| Vercel env | `BLACKLIST` | comma-separated de keys revogadas |
| Cliente | `data/telegram.json` | criado ao usuário salvar no painel TELEGRAM |
| Cliente | `data/license.json` | criado ao ativar |
| Cliente | `cert.pem`, `key.pem` | gerados pelo `install.sh` |

Credenciais específicas estão salvas em:
- `~/.claude/projects/-Users-deividsoares/memory/implementations/2026-04-23-irondome-licensing.md`
- Nunca incluí-las em commit público.

---

## 7. Formato da chave de licença

```
IRDM-{owner_id}-{expires_unix_ts}-{hmac_sig_16_chars}

Exemplo: IRDM-testeeqt-1779576411-ae5bdae797015238
```

- `owner_id`: 2-16 chars alfanum lowercase (ex: `joao1234`).
- `expires_unix_ts`: segundos Unix. Expiração ≥ 1 dia, ≤ 3650 dias.
- `sig`: `hmac_sha256(LICENSE_SECRET, "owner_id-expires_ts")` truncado a 16 chars.

Verificação no `_lib.js`: `parseKey → BLACKLIST check → HMAC timingSafeEqual → expiry check`.

---

## 8. Fluxo de versionamento

**Rígido**: toda alteração bumpa `version.py` e adiciona entrada no `CHANGELOG.md` **antes** do restart.

```python
VERSION = '1.14.0'  # SemVer
RELEASE_NAME = 'IronDome'
RELEASE_DATE = '2026-04-23'
```

Regras SemVer:
- PATCH (1.14.0 → 1.14.1): fix / tuning sem mudança de contrato.
- MINOR (1.14.0 → 1.15.0): feature nova compatível (nova coluna, endpoint, tela).
- MAJOR (1.14.0 → 2.0.0): mudança que quebra clientes existentes.

**Snapshots de segurança**: antes de qualquer edição em código, fazer:
```bash
SNAP="~/estudos/aviator-signals/_snapshots/snap-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$SNAP" && cp *.py "$SNAP/" && cp -r static "$SNAP/"
```

---

## 9. Pipeline de distribuição (pra publicar nova versão)

1. Bump `version.py` e edit `CHANGELOG.md` (obrigatório).
2. Montar ZIP limpo:
   ```bash
   rm -rf /tmp/irondome-pkg && mkdir -p /tmp/irondome-pkg/irondome
   cp ~/estudos/aviator-signals/*.py /tmp/irondome-pkg/irondome/
   cp ~/estudos/aviator-signals/*.sh /tmp/irondome-pkg/irondome/
   cp ~/estudos/aviator-signals/*.md /tmp/irondome-pkg/irondome/
   cp -r ~/estudos/aviator-signals/static /tmp/irondome-pkg/irondome/
   find /tmp/irondome-pkg -name "__pycache__" -type d -exec rm -rf {} +
   cd /tmp/irondome-pkg && zip -rq ~/estudos/irondome-site/public/irondome-X.Y.Z.zip irondome
   cp ~/estudos/irondome-site/public/irondome-X.Y.Z.zip \
      ~/estudos/irondome-site/public/irondome-latest.zip
   cp ~/estudos/aviator-signals/CHANGELOG.md ~/estudos/irondome-site/public/CHANGELOG.md
   ```
3. Atualizar `public/version.json` com nova versão.
4. Atualizar `vercel.json` — trocar referências a versões antigas.
5. Atualizar `public/index.html` — versão mostrada no landing.
6. Deploy: `cd ~/estudos/irondome-site && vercel deploy --prod --yes --scope dribeiro81s-projects`.
7. Clientes (1.10.0+) detectam update automático em ≤30 min.

**NUNCA esquecer**: excluir arquivos runtime (`data/`, `_snapshots/`, certs privados) do ZIP.

---

## 10. Decisões de design importantes

### 10.1 Por que deque maxlen=2000 vs DB query?
- Hot path: `evaluate_target_probability` chamada a cada gate (2-5 vezes/s no polling).
- Query DB tem I/O. Deque in-memory é O(1).
- DB é usada só no boot pra carregar últimos 2000 + para `pattern_bet_history`.

### 10.2 Por que Python puro e não .app?
- Simplicidade: zero build tooling.
- Distribuição: 108KB ZIP universal.
- Contra: source code acessível. Licenciamento fica bypassável. **Aceito conscientemente pra MVP**; futuro tem PyArmor/nuitka (audit C1).

### 10.3 Por que HTTP local TLS self-signed?
- Snippet JS dentro do iframe precisa falar com algo. `localhost:8766` é confiável na origem local.
- Auto-signed cert gera warning na primeira vez, user confirma, navegador lembra.
- Anti-MITM local (outros apps no mesmo Mac não conseguem interceptar sem permissão).

### 10.4 Por que keys self-contained (sem DB no Vercel)?
- Vercel Functions são serverless (cold start = DB connection lenta).
- Key contém tudo (owner, expiry, sig). Server verifica assinatura sem lookup.
- Revogação via env var (lista estática). Pra escala, migrar pra KV.

### 10.5 Por que cooldown cresce +1 na fórmula?
- `loss_wait_until = api_total + caf + 1` (não `+ caf`).
- Razão: quando miss é registrado, `api_total` já está incrementado (crash aconteceu). Sem o +1, `cooldownAfterLoss=1` bloqueava 0 rodadas efetivas.
- Mesma lógica pro `cooldownAfterWin`.

---

## 11. Problemas conhecidos & decisões pendentes

### Audit findings (2026-04-24)

| # | Severidade | Descrição | Status |
|---|---|---|---|
| C1 | 🔴 CRITICAL | Bypass trivial: `license.py` em `.py` puro — user edita `is_licensed()` | Pendente (compilar `.pyc` + PyArmor) |
| C2 | 🔴 CRITICAL | `/api/admin/debug` expõe length dos secrets | **ALTA prioridade** — deletar arquivo antes de commercial scale |
| C3 | 🔴 CRITICAL | Zero rate limit em `/api/admin/*` e `/api/license/verify` | Pendente (Upstash KV throttle) |
| H1 | 🟠 HIGH | Auto-updater não valida SHA256 do ZIP | Pendente |
| H2 | 🟠 HIGH | Cliente sem certificate pinning | Pendente |
| H3 | 🟠 HIGH | Chave + token em plain-text no `data/license.json` | Pendente (machine fingerprint binding) |
| H4 | 🟠 HIGH | Autoclick sem anti-detecção sofisticada | Pendente |
| H5 | 🟠 HIGH | Revogação via env var = redeploy manual | Pendente (KV dinâmico) |

### Features discutidas mas não implementadas
- Machine fingerprint binding (1 key = 1 máquina)
- Admin UI com lista de keys emitidas (precisa persistência)
- Dashboard mobile-friendly
- Backup automático do DB

---

## 12. Debugging — comandos úteis

```bash
# Status geral do sistema
curl -sk https://localhost:8766/api/version
curl -sk https://localhost:8766/api/autoclick/status | python3 -m json.tool
curl -sk https://localhost:8766/api/license/status

# Gate da IA em detalhes
curl -sk "https://localhost:8766/api/intelligence/gate?target=2&panel=0&margin=0.10" \
  | python3 -m json.tool

# Logs em tempo real
tail -f ~/estudos/aviator-signals/_snapshots/daemon.log
tail -f ~/estudos/aviator-signals/_snapshots/server.log

# Inspecionar DB
sqlite3 ~/estudos/aviator-signals/data/aviator.db "SELECT COUNT(*) FROM crashes;"
sqlite3 ~/estudos/aviator-signals/data/aviator.db \
  "SELECT panel, COUNT(*), SUM(hit), SUM(delta_pnl) FROM bets GROUP BY panel;"

# Restart completo
~/estudos/aviator-signals/stop.sh && ~/estudos/aviator-signals/start.sh

# Só server (mantém daemon)
pkill -f "server.py" && sleep 1 && \
  nohup python3 ~/estudos/aviator-signals/server.py \
  > ~/estudos/aviator-signals/_snapshots/server.log 2>&1 &

# Emitir chave nova (via curl admin)
curl -X POST https://irondome-site.vercel.app/api/admin/issue \
  -H "Content-Type: application/json" \
  -H "x-admin-password: <SENHA_ADMIN>" \
  -d '{"owner":"nome_cliente","days":30}'

# Ver versões no Vercel
curl -s https://irondome-site.vercel.app/version.json

# Forçar reset de stats local
curl -sk -X POST https://localhost:8766/api/autoclick/reset
```

---

## 13. Regras de trabalho com este código

### 13.1 Persistência de memória
Usuário tem sistema de memória em `~/.claude/projects/-Users-deividsoares/memory/`
que é versionado em repo privado GitHub `DRibeiro81/claude-memory`. Auto-commit
via hook. Ler MEMORY.md pra contexto antes de começar.

Histórico detalhado das implementações está em `memory/implementations/2026-04-*.md`.

### 13.2 Regras de segurança
- **SEMPRE fazer snapshot antes de editar** em `_snapshots/snap-<timestamp>/`.
- **Versionar toda alteração** (bump `version.py` + entry em `CHANGELOG.md`).
- **Preservar `data/`** em qualquer operação de limpeza (contém DB + licença).
- **Não commitar credenciais** no código. Sempre em env var ou memory privada.
- **Não quebrar o auto-updater**: clientes existentes dependem de `/api/updates/install`.

### 13.3 Convenções de código
- Python: sem tipagem estrita, mas docstrings em funções importantes.
- Strings em português (UI, logs, comments).
- Single-file server.py (1600+ linhas) é aceito — fácil de navegar por grep.
- JS no static/index.html é vanilla (zero framework).

### 13.4 Teste e validação
- Sem test suite formal. Validação por:
  - Restart do server + curl dos endpoints.
  - Observação em tempo real da UI + logs.
  - Snapshot antes → rollback manual se der ruim.

### 13.5 Comunicação com o usuário
- Usuário prefere respostas **diretas, técnicas, sem floreio**.
- Decisões são tomadas rapidamente — oferecer opções em vez de perguntar múltiplas vezes.
- Quando há trade-off, **recomendar** uma opção com justificativa curta.
- Se o usuário diz "faça" = autorização pra proceder sem mais perguntas.

---

## 14. Links rápidos

- **Produção**: <https://irondome-site.vercel.app>
- **Admin**: <https://irondome-site.vercel.app/admin>
- **Dashboard local**: <https://localhost:8766> (após ativação de licença)
- **Source**: `~/estudos/aviator-signals/`
- **Site source**: `~/estudos/irondome-site/`
- **Memory**: `~/.claude/projects/-Users-deividsoares/memory/`

---

## 15. Contato operacional

- **Dono do sistema**: Deivid Soares
- **Email**: matheushilariolopes@gmail.com (user's Claude account)
- **Responsável pela emissão de chaves**: ele mesmo, via admin UI.
- **SLA**: sistema é experimental, sem SLA formal. Auto-update traz correções.

---

**Fim do handoff.** Qualquer dúvida não coberta, grep o source + CHANGELOG + essa memory. Boa sorte.
