nanobot — Documentacao Tecnica

Referencia completa dos internos: cada arquivo, cada limite, cada camada de protecao

1. Mapa dos Programas

Cada arquivo do projeto, onde mora e o que faz. A coluna Linhas e aproximada — o projeto todo soma ~3.400 linhas de Python.

Agent — O cerebro

ArquivoLinhasO que faz
nanobot/agent/loop.py 406 Motor principal — recebe mensagens, chama LLM, executa tools
nanobot/agent/context.py ~130 Monta o prompt — junta bootstrap files, memoria, skills, historico
nanobot/agent/subagent.py ~90 Gerencia subagentes (max 5 simultaneos)
nanobot/agent/memory.py ~80 Memoria do agente (lembrar coisas entre sessoes)
nanobot/agent/skills.py ~100 Carrega skills sob demanda

Tools — As maos do agente

ArquivoLinhasO que faz
nanobot/agent/tools/registry.py ~50 Registro central de tools disponiveis
nanobot/agent/tools/filesystem.py ~120 Tools: read_file, write_file, edit_file, list_dir
nanobot/agent/tools/shell.py ~130 Tool: exec (executa comandos no terminal)
nanobot/agent/tools/web.py ~170 Tools: web_search, web_fetch
nanobot/agent/tools/message.py ~40 Tool: message (envia mensagem pro usuario)
nanobot/agent/tools/spawn.py ~60 Tool: spawn (cria subagentes)
nanobot/agent/tools/cron.py ~110 Tool: cron (agenda tarefas)

Message Bus — O correio interno

ArquivoLinhasO que faz
nanobot/bus/queue.py 92 Fila de mensagens (asyncio.Queue, max 1000)
nanobot/bus/events.py ~50 Tipos de mensagem (InboundMessage, OutboundMessage)

Channels — As portas de entrada

ArquivoLinhasO que faz
nanobot/channels/base.py ~130 Classe base de todos os canais
nanobot/channels/telegram.py 400 Canal Telegram (polling + envio)
nanobot/channels/discord.py ~220 Canal Discord (WebSocket)
nanobot/channels/whatsapp.py ~150 Canal WhatsApp (bridge Node.js)
nanobot/channels/feishu.py ~270 Canal Feishu (WebSocket)
nanobot/channels/dingtalk.py ~100 Canal DingTalk (Stream)

Providers — Conexao com a IA

ArquivoLinhasO que faz
nanobot/providers/base.py ~60 Interface base do provider LLM
nanobot/providers/registry.py ~100 Registro de providers (auto-detect, prefixing)

Configuracao e Sessoes

ArquivoLinhasO que faz
nanobot/config/schema.py ~100 Schema de configuracao (Pydantic)
nanobot/session/manager.py 249 Gerencia sessoes (JSONL, cache LRU)

Seguranca

ArquivoLinhasO que faz
nanobot/security/ratelimit.py ~100 Rate limiter (token bucket por sessao)
nanobot/security/sanitize.py ~50 Sanitiza erros e resultados de tools
nanobot/security/validators.py ~70 Valida paths, filenames, inputs

Servicos

ArquivoLinhasO que faz
nanobot/cron/service.py 346 Servico de tarefas agendadas
nanobot/heartbeat/ ~40 Heartbeat para wake-up proativo
nanobot/cli/commands.py 846 Todos os comandos CLI (gateway, agent, status, etc.)

2. Limites e Protecoes

O nanobot tem dezenas de limites espalhados pelo codigo. Cada um existe para evitar que o sistema trave, consuma memoria demais, ou seja abusado. Esta secao documenta todos eles.

Fila de Mensagens (MessageBus)

ConstanteValorOndeO que acontece
MAX_QUEUE_SIZE 1000 bus/queue.py Tanto a fila de entrada quanto a de saida seguram no maximo 1000 mensagens
Timeout da fila 5 segundos bus/queue.py Se a fila nao tiver espaco em 5s, a mensagem e perdida com warning "backpressure"

Importante: a fila e in-memory

A fila do MessageBus e uma asyncio.Queue — ela existe apenas na memoria RAM. Se o processo morrer, tudo que esta na fila desaparece. Quando a fila enche (1000 mensagens), as novas sao descartadas (dropped), nao salvas. Nao ha persistencia.

A fila e o "corredor" entre channels e agent. Mensagens passam por ela rapidamente. Se estiver acumulando 1000, significa que o agent esta muito lento ou parado.

Sessoes (Session)

ConstanteValorOndeO que acontece
MAX_MESSAGES_PER_SESSION 1000 session/manager.py Cada sessao guarda no maximo 1000 mensagens em memoria e disco. Quando excede, as mais antigas sao descartadas (mantem as ultimas 1000)
MAX_MESSAGE_SIZE 100 KB session/manager.py Mensagens individuais maiores que 100KB sao truncadas
MAX_CACHED_SESSIONS 100 session/manager.py So 100 sessoes ficam na RAM (cache LRU). As demais sao carregadas do disco sob demanda
SESSION_EXPIRY_DAYS 30 dias session/manager.py Sessoes com mais de 30 dias sao deletadas no proximo acesso
get_history(max_messages=50) 50 session/manager.py Quando monta o contexto para o LLM, envia apenas as ultimas 50 mensagens (nao as 1000)

Por que 1000 no disco mas so 50 para o LLM?

O arquivo .jsonl guarda 1000 mensagens como backup completo da conversa. Mas o LLM recebe apenas as ultimas 50 porque: (a) modelos tem limite de tokens, (b) mensagens muito antigas raramente sao relevantes, e (c) menos mensagens = resposta mais rapida e mais barata.

Fila vs. Sessao — A diferenca crucial

Os dois tem limite de 1000, mas sao coisas completamente diferentes:

Fila (MessageBus)

  • Onde: Memoria RAM apenas
  • O que guarda: Mensagens em transito (entrada e saida)
  • Quando enche: Mensagens novas sao descartadas (perdidas pra sempre)
  • Duracao: Segundos (mensagens passam rapido)
  • Persistencia: Nenhuma — morre com o processo
  • Analogia: Uma esteira rolante. Se nao cabe mais, cai no chao

Sessao (Session)

  • Onde: Memoria RAM (cache) + disco (.jsonl)
  • O que guarda: Historico completo de uma conversa
  • Quando enche: As mensagens mais antigas sao removidas (mantem as 1000 mais recentes)
  • Duracao: Ate 30 dias
  • Persistencia: Arquivo no disco, escrita atomica
  • Analogia: Um caderno. Quando enche, arranca as primeiras paginas
FILA (transito)                        SESSAO (historico)
=================                      ==================

Channel --> [msg][msg][msg] --> Agent   Arquivo .jsonl no disco:
            ^^^^^^^^^^^^^^^^            +---------------------------+
            Max 1000 in-memory          | msg 1  (mais antiga)      |  ← descartada
            Se cheia: DROP              | msg 2                     |     quando > 1000
                                        | ...                       |
Mensagem fica SEGUNDOS aqui.           | msg 999                   |
Sem persistencia.                       | msg 1000 (mais recente)   |
                                        +---------------------------+
                                        Mensagem fica ATE 30 DIAS.

Rate Limiting (Token Bucket)

OperacaoLimiteJanelaPor
channel_message 10 por minuto sessao
tool_exec 5 por minuto sessao
web_fetch 20 por minuto sessao
file_write 30 por minuto sessao
subagent_spawn 3 a cada 5 minutos sessao
cron_job 10 por hora sessao

Contexto e Arquivos

ConstanteValorO que faz
MAX_BOOTSTRAP_FILE_SIZE 500 KB Bootstrap files (SOUL.md, AGENTS.md etc.) maiores que 500KB sao ignorados
MAX_IMAGE_SIZE 5 MB Tamanho maximo por imagem enviada
MAX_IMAGES_PER_MESSAGE 5 Maximo de imagens por mensagem
MAX_READ_SIZE 10 MB Tamanho maximo que o tool read_file consegue ler
MAX_WRITE_SIZE 10 MB Tamanho maximo que o tool write_file consegue gravar
Saida do shell 10.000 chars Saida do tool exec e truncada em 10.000 caracteres

Outros Limites

ConstanteValorO que faz
MAX_CONCURRENT_SUBAGENTS 5 Maximo de subagentes rodando ao mesmo tempo
MAX_REDIRECTS 5 Maximo de redirecionamentos HTTP no web_fetch
MAX_ATTACHMENT_BYTES 20 MB Tamanho maximo de anexo no Discord
Channel message size 50 KB Tamanho maximo de mensagem no canal base
Feishu dedup cache 1000 → 500 Cache de deduplicacao do Feishu: quando atinge 1000 entradas, faz trim para 500

3. Rate Limiting Explicado

O sistema de rate limiting do nanobot usa o algoritmo Token Bucket (balde de fichas). Cada tipo de operacao tem seu proprio balde, e cada sessao (usuario) tem seus proprios baldes independentes.

Como funciona o Token Bucket

Analogia: tanque de agua

Imagine um tanque de agua que enche lentamente por uma torneira. Cada vez que voce quer fazer uma operacao (mandar mensagem, executar tool, etc.), voce precisa tirar 1 litro do tanque.

Se o tanque estiver vazio, voce nao pode fazer nada — tem que esperar a torneira encher mais um pouco.

O tanque tem um tamanho maximo (capacidade). Mesmo que voce nao use, ele nao transborda — a torneira para quando ta cheio.

Isso permite rajadas curtas (usar varios litros de uma vez) mas impede abuso continuo (a torneira nao enche rapido o suficiente).

Mecanica passo a passo

  1. Cada tipo de operacao tem um balde com capacidade fixa (exemplo: channel_message = 10)
  2. O balde comeca cheio (10 fichas disponiveis)
  3. Cada request consome 1 ficha
  4. Fichas sao reabastecidas ao longo do tempo (exemplo: 10 fichas por minuto = 1 ficha a cada 6 segundos)
  5. Se o balde esta vazio, o request e negado (nao enfileirado, negado imediatamente)
  6. Cada sessao tem seus proprios baldes — o abuso de um usuario nao afeta os outros

Exemplo pratico

Operacao: channel_message (10/minuto)
Tempo: 00:00  Balde: [10 fichas]

00:01  Usuario manda msg   → consome 1  → Balde: [9]   OK
00:02  Usuario manda msg   → consome 1  → Balde: [8]   OK
00:03  Usuario manda msg   → consome 1  → Balde: [7]   OK
...
00:10  Usuario manda msg   → consome 1  → Balde: [0]   OK
00:11  Usuario manda msg   → balde vazio →              NEGADO
00:12  (6s se passaram)    → +1 ficha   → Balde: [1]
00:12  Usuario manda msg   → consome 1  → Balde: [0]   OK

Por que Token Bucket e nao uma janela simples?

Vantagem do Token Bucket

Com uma janela fixa (ex: "10 por minuto"), um usuario pode mandar 10 mensagens no segundo 59 e mais 10 no segundo 61 — totalizando 20 em 2 segundos. O Token Bucket evita isso porque as fichas gastam e demoram para reabastecer, distribuindo o uso de forma mais uniforme ao longo do tempo.

Todos os baldes configurados

OperacaoCapacidadeReabastecimentoDescricao
channel_message 10 10 por minuto Mensagens recebidas de um usuario em um canal
tool_exec 5 5 por minuto Execucoes de comandos no terminal
web_fetch 20 20 por minuto Requisicoes HTTP feitas pelo agente
file_write 30 30 por minuto Escritas em arquivo
subagent_spawn 3 3 a cada 5 minutos Criacao de subagentes em background
cron_job 10 10 por hora Execucoes de tarefas agendadas

O que acontece quando negado?

Quando o rate limit nega uma operacao:

4. Ciclo de Vida de uma Mensagem

Passo a passo completo do que acontece desde o usuario digitar no Telegram ate receber a resposta, com todos os limites que se aplicam em cada etapa.

Usuario envia mensagem no Telegram O usuario digita "Qual o clima em SP?" e aperta enviar. A mensagem vai para os servidores do Telegram.
Channel Telegram recebe O polling do TelegramChannel detecta a mensagem nova. Primeira verificacao: o sender_id esta na lista allowFrom? Se nao, a mensagem e ignorada silenciosamente. allowFrom: whitelist de IDs
Rate limit do channel Verifica se este usuario ja mandou muitas mensagens. O token bucket channel_message consome 1 ficha. Se o balde estiver vazio, a mensagem e descartada. channel_message: 10/minuto por sessao
Cria InboundMessage e enfileira O channel cria um InboundMessage padronizado e coloca na fila de entrada do MessageBus. A fila aceita no maximo 1000 mensagens. Se estiver cheia, espera ate 5 segundos. Se nao abrir espaco, a mensagem e perdida com warning de backpressure. MAX_QUEUE_SIZE: 1000 | Timeout: 5s
Agent loop consome da fila O AgentLoop esta em await bus.consume_inbound() e recebe a mensagem. Carrega a sessao correspondente (telegram:123456789). Se a sessao nao esta no cache LRU, carrega do disco. Se tem mais de 30 dias, deleta e cria nova. MAX_CACHED_SESSIONS: 100 | SESSION_EXPIRY: 30 dias
Context builder monta o prompt O ContextBuilder monta o sistema de prompt na ordem: identidade → bootstrap files (SOUL.md, AGENTS.md, USER.md, TOOLS.md, IDENTITY.md) → memoria → skills → ultimas 50 mensagens do historico → mensagem atual. Bootstrap files maiores que 500KB sao ignorados. get_history: 50 msgs | MAX_BOOTSTRAP: 500KB
Envia para o LLM provider O provider.chat() envia todo o contexto montado para a API do modelo (ex: OpenRouter → Qwen). Junto vai a lista de tools disponiveis no formato JSON Schema. O LLM processa e retorna uma resposta.
LLM pede tool call (se necessario) Se o LLM decidir usar uma ferramenta (ex: web_fetch("wttr.in/SP")), o agent verifica o rate limit da tool. Se aprovado, executa. O resultado (ex: "28C, parcialmente nublado") e adicionado ao contexto e o loop volta ao passo 7. Isso pode repetir ate 20 vezes numa unica mensagem. tool_exec: 5/min | web_fetch: 20/min | file_write: 30/min | Max 20 iteracoes
LLM responde com texto final Quando o LLM responde com texto (sem tool calls), o loop sai. A resposta e encapsulada em um OutboundMessage e colocada na fila de saida do MessageBus. Mesmas regras: max 1000, timeout 5s. MAX_QUEUE_SIZE: 1000 | Timeout: 5s
Dispatcher envia ao channel correto O dispatcher retira da fila de saida e envia para o TelegramChannel. O channel converte para o formato do Telegram (HTML) e envia via API. A mensagem do channel tem limite de tamanho (50KB base). Channel message size: 50KB
Sessao salva no disco A mensagem do usuario + resposta do agente sao salvos no arquivo .jsonl da sessao. Se a sessao exceder 1000 mensagens, as mais antigas sao descartadas. A escrita e atomica (temp + rename) para evitar corrupcao. MAX_MESSAGES_PER_SESSION: 1000 | MAX_MESSAGE_SIZE: 100KB

Resumo dos limites no caminho

EtapaLimiteSe exceder
Channel recebeallowFromMensagem ignorada
Channel rate limit10/minMensagem descartada
Fila de entrada1000 (5s timeout)Mensagem perdida
Cache de sessoes100 LRUSessao carregada do disco (mais lento)
Expiracao da sessao30 diasSessao deletada, nova criada
Historico para LLM50 mensagensMensagens antigas cortadas
Bootstrap files500KB cadaArquivo ignorado
Tool rate limitsVaria (5-30/min)Tool retorna erro ao LLM
Iteracoes do loop20Responde com o que tem
Fila de saida1000 (5s timeout)Resposta perdida
Mensagem do channel50KBMensagem truncada
Sessao no disco1000 mensagensMais antigas removidas

5. Fila Unica e Concorrencia

Duas perguntas frequentes sobre a arquitetura do nanobot: como funciona quando os dados chegam por mais de um canal ao mesmo tempo? E o agente pode processar varias mensagens em paralelo?

A fila e unica para todos os canais

Nao importa se a mensagem veio do Telegram, Discord, WhatsApp ou Feishu — todas vao para a mesma fila (inbound). O MessageBus tem exatamente duas filas:

FilaDirecaoQuem colocaQuem retira
inboundCanal → AgenteTodos os canais (Telegram, Discord, etc.)AgentLoop (1 consumidor)
outboundAgente → CanalAgentLoopDispatcher (envia para o canal correto)

Funciona como um funil: varios canais despejam mensagens na mesma entrada, e o agente puxa uma por uma.


  Telegram ──┐
             │
  Discord ───┤       ┌─────────────────────┐        ┌───────────┐
             ├──────►│  FILA INBOUND (1000) │───────►│ AgentLoop │
  WhatsApp ──┤       └─────────────────────┘        │ (1 por vez)│
             │                                       └─────┬─────┘
  Feishu ────┘                                             │
                                                           ▼
  Telegram ◄──┐       ┌──────────────────────┐      ┌─────┴─────┐
              ├──────│  FILA OUTBOUND (1000) │◄─────│ Resposta  │
  Discord ◄───┘       └──────────────────────┘      └───────────┘
  (dispatcher envia para o canal certo)

O processamento e sequencial (uma mensagem por vez)

O AgentLoop funciona com um loop simples:

while self._running:
    msg = await bus.consume_inbound()       # 1. Pega UMA mensagem da fila
    response = await _process_message(msg)  # 2. Processa ATE O FIM (inclui tools)
    await bus.publish_outbound(response)    # 3. Coloca resposta na fila de saida
    # so agora volta ao passo 1

Enquanto o agente esta processando a mensagem A (chamando LLM, executando tools, esperando APIs), as mensagens B, C e D ficam esperando na fila. Nao ha processamento paralelo de mensagens.

Consequencias praticas

SituacaoO que acontece
Voce manda 3 mensagens rapidas pelo Telegram A 1a e processada imediatamente. A 2a e 3a ficam na fila. Quando a 1a terminar, o agente pega a 2a, e depois a 3a. Voce recebe 3 respostas, em ordem.
Voce manda pelo Telegram e outra pessoa manda pelo Discord ao mesmo tempo Ambas entram na mesma fila. A que chegar primeiro e processada primeiro. A outra espera. Nao ha prioridade por canal.
O agente esta usando tools (web_fetch, exec) que demoram Tudo faz parte do processamento da mensagem. As tools rodam dentro do _process_message(). Novas mensagens continuam esperando na fila.
O agente usa spawn para criar subagente O subagente roda em background (asyncio task separada). O agente pode terminar a mensagem atual e pegar a proxima. Quando o subagente termina, envia o resultado como mensagem "system" na fila.
100 pessoas mandam mensagens ao mesmo tempo Todas entram na fila (cabe 1000). Sao processadas uma por uma, na ordem de chegada. As ultimas vao esperar bastante. Se a fila encher (1000), novas mensagens sao descartadas.

Por que nao e paralelo?

O nanobot e um assistente pessoal, projetado para poucos usuarios (normalmente so voce). Processamento paralelo traria complexidade desnecessaria:

  • Cada sessao compartilha o mesmo workspace — tools paralelos poderiam conflitar (dois escrevendo no mesmo arquivo)
  • O contexto do LLM depende do historico — processar em paralelo poderia misturar conversas
  • O rate limiter e por sessao — concorrencia exigiria locks extras
  • Para 1-5 usuarios, a fila sequencial funciona bem. Cada mensagem leva segundos, nao minutos

A unica forma de paralelismo real e o subagente (spawn), que roda em background com seu proprio loop de tools (max 5 simultaneos).

6. Seguranca em Camadas

A seguranca do nanobot segue o principio de defesa em profundidade (defense in depth): multiplas camadas independentes, cada uma capaz de bloquear ameacas por conta propria. Se uma camada falhar, as proximas ainda protegem.

+-------------------------------------------------------+
|                    CAMADA 1: allowFrom                 |
|              (whitelist de usuarios)                   |
+-------------------------------------------------------+
                          |
+-------------------------------------------------------+
|                  CAMADA 2: Rate Limiting               |
|           (token bucket por sessao)                    |
+-------------------------------------------------------+
                          |
+-------------------------------------------------------+
|              CAMADA 3: Restricao de Workspace          |
|       (tools so acessam ~/.nanobot/workspace/)         |
+-------------------------------------------------------+
                          |
+-------------------------------------------------------+
|              CAMADA 4: Validacao de Input               |
|        (paths, filenames, tamanhos, comandos)          |
+-------------------------------------------------------+
                          |
+-------------------------------------------------------+
|              CAMADA 5: Sanitizacao de Output            |
|        (erros, resultados de tools, stack traces)      |
+-------------------------------------------------------+
                          |
+-------------------------------------------------------+
|              CAMADA 6: Limites de Tamanho              |
|     (mensagens, arquivos, filas, sessoes)              |
+-------------------------------------------------------+
                          |
+-------------------------------------------------------+
|              CAMADA 7: Isolamento (Docker)             |
|         (non-root, rede restrita, volumes)             |
+-------------------------------------------------------+

Camada 1: allowFrom (Whitelist de Usuarios)

O que faz

Cada channel tem uma lista allowFrom que define quais user IDs podem interagir com o bot. Se a lista estiver vazia, ninguem e aceito (deny all por padrao).

{
  "channels": {
    "telegram": {
      "token": "...",
      "allowFrom": ["123456789", "987654321"]
    }
  }
}

Mensagens de IDs fora da lista sao descartadas silenciosamente — sem resposta, sem log detalhado, sem confirmacao.

Camada 2: Rate Limiting

O que faz

Token bucket por sessao, impedindo abuso mesmo de usuarios autorizados. Cada operacao tem um balde independente. Veja a secao 3 para detalhes completos.

  • channel_message: 10/min
  • tool_exec: 5/min
  • web_fetch: 20/min
  • file_write: 30/min
  • subagent_spawn: 3/5min
  • cron_job: 10/hora

Camada 3: Restricao de Workspace

O que faz

Quando restrictToWorkspace esta ativo (padrao), todos os tools de filesystem (read_file, write_file, edit_file, list_dir) so podem acessar arquivos dentro de ~/.nanobot/workspace/. Tentativas de acessar /etc/passwd, ~/.ssh/, ou qualquer coisa fora do workspace sao bloqueadas.

O tool exec tambem roda com o diretorio de trabalho setado para o workspace.

Camada 4: Validacao de Input

O que faz

O modulo security/validators.py valida:

  • Paths: bloqueia path traversal (../../etc/passwd), symlinks maliciosos, paths absolutos fora do workspace
  • Filenames: bloqueia caracteres especiais, nomes reservados do sistema, nomes muito longos
  • Comandos shell: bloqueia padroes perigosos (rm -rf /, dd if=, fork bombs :(){ :|:, mkfs, > /dev/) e detecta injecao de comandos (;, &&, ||, |, backticks, $())
  • Tamanhos: verifica limites de mensagens (100KB), arquivos (10MB), imagens (5MB)

Camada 5: Sanitizacao de Output

O que faz

O modulo security/sanitize.py limpa resultados antes de enviá-los de volta:

  • Erros: stack traces completos sao removidos e substituidos por mensagens genericas (nao vaza paths internos, nomes de modulos, etc.)
  • Resultados de tools: saida de shell truncada em 10.000 caracteres
  • Informacoes sensiveis: tokens, keys e passwords sao filtrados dos resultados

Camada 6: Limites de Tamanho

O que faz

Limites em todas as "bordas" do sistema para evitar abuso de recursos:

  • Fila: 1000 mensagens max (RAM)
  • Sessao: 1000 mensagens max (disco)
  • Mensagem individual: 100KB
  • Mensagem do channel: 50KB
  • Arquivo read/write: 10MB
  • Bootstrap file: 500KB
  • Imagem: 5MB, max 5 por mensagem
  • Anexo Discord: 20MB
  • Saida shell: 10.000 chars

Camada 7: Isolamento Docker

O que faz

Quando rodando em Docker (recomendado para producao):

  • Non-root: o container roda com um usuario sem privilegios
  • Volumes restritos: apenas ~/.nanobot/ e montado
  • Sem capabilities extras: nao tem acesso ao host
  • Rede controlada: so precisa de acesso HTTPS de saida (APIs de LLM e channels)

Escrita atomica de arquivos

Como funciona a escrita atomica

Para evitar corrupcao de dados (ex: queda de energia no meio de uma escrita), o nanobot usa o padrao temp + rename:

1. Escreve o conteudo em um arquivo temporario (.tmp)
2. Faz fsync() para garantir que esta no disco
3. Renomeia o .tmp para o arquivo final (operacao atomica no filesystem)

Se der erro no passo 1 ou 2, o arquivo original nao e afetado.
O rename (passo 3) e atomico no Linux/macOS — ou o arquivo
antigo existe ou o novo, nunca um estado intermediario.

Isso e usado em: sessoes (.jsonl), config, cron jobs, e qualquer escrita do tool write_file.

7. Armazenamento

Todo o estado do nanobot vive dentro de ~/.nanobot/. Nao usa banco de dados, nao usa Redis, nao usa nada externo. Apenas arquivos no disco.

Estrutura de diretorios

~/.nanobot/
+-- config.json                    ← Configuracao principal
+-- gateway.log                    ← Log do gateway
+-- sessions/                      ← Historico de conversas
|   +-- telegram_123456789.jsonl
|   +-- discord_987654321.jsonl
|   +-- whatsapp_5511999998888.jsonl
+-- workspace/                     ← Area de trabalho do agente
|   +-- SOUL.md                    ← Personalidade do agente
|   +-- AGENTS.md                  ← Instrucoes para agentes
|   +-- USER.md                    ← Informacoes sobre o usuario
|   +-- TOOLS.md                   ← Config de ferramentas
|   +-- IDENTITY.md                ← Identidade do agente
|   +-- memory/
|   |   +-- MEMORY.md              ← Memoria permanente
|   |   +-- 2026-02-10.md          ← Notas diarias
|   +-- ... (outros arquivos)
+-- cron/                          ← Tarefas agendadas
|   +-- jobs.json
+-- skills/                        ← Skills instalados
    +-- remotion/
    |   +-- SKILL.md
    +-- github/
        +-- SKILL.md

Detalhes de cada tipo de armazenamento

Sessoes (~/.nanobot/sessions/*.jsonl)

Formato JSONL

Cada arquivo de sessao usa o formato JSONL (JSON Lines) — uma linha JSON por mensagem. Isso permite append eficiente (adicionar sem reescrever o arquivo todo) e leitura linha por linha (sem carregar tudo na memoria).

{"_type":"metadata","created_at":"2026-02-10T05:30:00","session_key":"telegram:123456789"}
{"role":"user","content":"Oi!","timestamp":"2026-02-10T05:31:00"}
{"role":"assistant","content":"Ola! Como posso ajudar?","timestamp":"2026-02-10T05:31:05"}
{"role":"user","content":"Qual o clima em SP?","timestamp":"2026-02-10T05:32:00"}
{"role":"assistant","content":"28C, parcialmente nublado","timestamp":"2026-02-10T05:32:15"}
  • Max 1000 mensagens por arquivo
  • Max 100KB por mensagem individual
  • Escrita atomica (temp + rename)
  • Auto-deletado apos 30 dias sem acesso
  • 100 sessoes em cache LRU na memoria

Configuracao (~/.nanobot/config.json)

Schema Pydantic

O arquivo de configuracao e validado pelo Pydantic v2 (config/schema.py). Se tiver campo errado, tipo errado, ou valor invalido, o nanobot recusa iniciar e mostra o erro exato.

{
  "model": "openrouter/qwen/qwen3-coder-next",
  "channels": {
    "telegram": {
      "token": "BOT_TOKEN_AQUI",
      "allowFrom": ["123456789"]
    }
  },
  "restrictToWorkspace": true
}

Workspace (~/.nanobot/workspace/)

Arquivos de bootstrap

Os arquivos na raiz do workspace sao os "bootstrap files" — carregados automaticamente no contexto do agente a cada mensagem. Sao eles que definem a personalidade, conhecimento e comportamento:

ArquivoProposito
SOUL.mdPersonalidade principal, tom de voz, regras de comportamento
AGENTS.mdInstrucoes especificas para agentes de IA
USER.mdInformacoes sobre o usuario (nome, preferencias, contexto)
TOOLS.mdConfiguracao e restricoes de ferramentas
IDENTITY.mdNome e identidade visual do agente

Limite: cada arquivo de bootstrap tem max 500KB. Arquivos maiores sao ignorados.

Cron (~/.nanobot/cron/)

Tarefas agendadas

Os jobs do cron sao salvos em jobs.json e persistidos no disco. O servico de cron (cron/service.py, 346 linhas) verifica a cada 10-30 segundos se ha tarefas para executar. Suporta tres tipos: every (intervalo), cron (expressao cron), e at (data/hora unica).

Log do gateway (~/.nanobot/gateway.log)

Log de operacao

Registra eventos do gateway: startup, shutdown, erros de channels, erros de provider, rate limits ativados, etc. Util para debug. Nao tem rotacao automatica — o usuario precisa gerenciar o tamanho.

Memoria (~/.nanobot/workspace/memory/)

Memoria persistente do agente

O agente pode gravar informacoes que quer lembrar entre sessoes:

  • MEMORY.md — Memoria permanente. O agente pode ler e editar livremente. Ideal para informacoes que nunca mudam (nome do usuario, preferencias, etc.)
  • YYYY-MM-DD.md — Notas diarias. O contexto builder inclui automaticamente as notas dos ultimos 7 dias. Ideal para anotacoes temporarias, TODOs, lembretes.

O agente acessa a memoria via tools (read_file, write_file). Na proxima conversa, o context builder carrega a memoria automaticamente no prompt do sistema.

Nada de banco de dados

Por que so arquivos?

O nanobot e um assistente pessoal, nao um servidor de producao com milhares de usuarios. Para um unico usuario (ou um punhado), arquivos no disco sao:

  • Simples: nao precisa instalar/configurar nada
  • Transparentes: voce pode abrir e ler qualquer arquivo num editor de texto
  • Portaveis: copiar ~/.nanobot/ para outra maquina e voce tem tudo
  • Confiaveis: escrita atomica previne corrupcao
  • Leves: sem dependencia externa, sem processo separado