MathTrail

Para o público técnico

Como o serviço funciona

O MathTrail fica entre o chat que a família já usa e o Google Drive dela. O modelo do chat escreve cada problema, e o serviço decide se ele chega à criança. É um programa só: não chama nenhum modelo próprio e não guarda nada de uma requisição para outra.

  • Um binário Go no Cloud Run
  • Sem estado
  • Sem banco de dados
  • Sem chamadas próprias a LLM
  • MCP 2026-07-28 · MCP Apps

Cliente de chat1 · cliente

  • Claude
  • ChatGPT
  • outros hosts de MCP Apps

O modelo do chat escreve o problema, e o cartão o mostra à criança.

MCP · chamadas de ferramentas por HTTPS

MathTrail no Google Cloud2 · execução

Cloud Run · um binário Go · sem estado

O núcleo — computação pura

  • A regra
  • Pontuações
  • Verificações
  • Resolvedor
  • Conteúdo

Ao redor: OAuth, o servidor MCP, o cartão, a selagem, os limites.

Drive API v3 · com o próprio token da família

Google Drive3 · armazenamento

MathTrail/mathtrail-profile.json

Um arquivo por criança, no próprio Drive da família — a única coisa guardada a longo prazo.

01

O mapa

Quem fala com quem. Nem a criança nem a pessoa adulta acessam o MathTrail diretamente — falam com o host do chat. Tudo o que está no bloco do meio é parte de um só processo, não um serviço separado. Escolha qualquer parte para ver o que ela faz e com o que se liga.

Partes do serviço

O chat da família

Claude no navegador, no computador e no celular · ChatGPT

Pessoas

Host do chat

MathTrail

Um binário Go · Cloud Run · um domínio próprio

entre as partes, simples chamadas de função

A entrada

Ferramentas — ler o perfil, calcular, gravar de volta

Computação pura — sem rede

Ao redor do núcleo

Google e GitHub

Os únicos serviços fora do binário

A Conta do Google da família

Google Cloud

GitHub

Escolha uma parte do mapa: tudo o que se liga a ela continua em destaque, e o resto esmaece.

O chat da família

Pessoa adulta

Mãe, pai ou docente. É titular tanto da conta do chat quanto da Conta do Google: conecta o MathTrail, faz login uma vez, cria o perfil, conduz a aula no chat e acompanha o progresso. É a única pessoa que faz login e a única que digita.

O chat da família

Criança

Do 1º ao 6º ano. Resolve os problemas no cartão, no chat da pessoa adulta. Não tem login próprio.

Host do chat

O modelo do host

O Claude ou o ChatGPT. Escreve o problema, um resolvedor em Starlark, uma explicação para cada alternativa errada e uma autoverificação; conduz a aula em palavras onde não se desenham cartões. O próprio MathTrail não chama nenhum modelo.

Host do chat

Ambiente de execução de MCP Apps

Desenha o cartão em um frame isolado, passa a ele o idioma, a plataforma e o tamanho, e repassa as chamadas do próprio cartão com o token do host — o cartão em si nunca vê o token.

Host do chat

Documento de metadados do cliente

O host se identifica por um endereço HTTPS. O MathTrail busca o documento nesse endereço, com proteção contra SSRF, em vez de manter um cadastro de clientes.

MathTrail · a entrada

Roteador HTTP

A única entrada: /health, /mcp, os endpoints do OAuth e .well-known. Tempos limite, um tamanho máximo para o corpo da requisição, uma verificação de Origin; o emissor é o próprio domínio do serviço, e qualquer outro Host recebe um 404.

MathTrail · a entrada

Servidor de autorização

O MathTrail é, ele mesmo, um servidor OAuth 2.1 com PKCE: metadados, uma tela de permissão própria, login com o Google, tokens, renovação e revogação. Tudo o que um servidor com estado guardaria vai selado em tokens e cookies.

MathTrail · a entrada

Servidor MCP

O protocolo: Streamable HTTP 2026-07-28, sem estado, sobre o SDK oficial de Go. Lista e chama as ferramentas, serve o cartão como recurso ui:// e entrega ao modelo as instruções, com a versão delas.

MathTrail · a entrada

Limites

Um ritmo por endereço antes do login e por conta depois dele, e um teto geral, tudo em memória — depois, os tetos diários, que o perfil conta. Passado um limite, uma frase simples que o modelo repassa.

MathTrail · ferramentas

Ferramentas

Doze ferramentas com o mesmo formato: ler o perfil, calcular, gravar de volta. O modelo chama a maioria; o cartão chama as suas — para o problema, a resposta, o progresso e o formulário do perfil. Nada secreto jamais entra em um resultado.

MathTrail · computação pura

A regra

Escolhe o briefing de forma determinística: depois de um erro, o mesmo tema, para consolidá-lo; senão, o tema que está há mais tempo sem aparecer; a dificuldade, do corredor em que a chance de acerto é de 70–85%. Um tema em que a criança está atrasada vem com o dobro da frequência. Os temas difíceis demais por enquanto ficam em pausa, e quando o nível geral da criança alcança um deles, cada sexto problema pode trazê-lo de volta como verificação do tema.

MathTrail · computação pura

Pontuações

P = 0,2 + 0,8·σ(θ + δ − β). O Elo se move só pelo acerto ou erro da resposta, com um passo que diminui; uma escala de xadrez e onze etapas; um tema conta como dominado por uma estimativa cautelosa.

MathTrail · computação pura

Verificações

Um problema enviado precisa passar em tudo isto: a estrutura e cinco alternativas diferentes, as letras do idioma da aula, explicações distintas, o resolvedor, a autoverificação, a legibilidade, as quase repetições e o desenho: o formato, os rótulos confrontados com o enunciado e nenhuma resposta escrita nele. Todas as falhas são informadas de uma só vez.

MathTrail · computação pura

Resolvedor

Executa o programa em Starlark do modelo, que testa as alternativas; exatamente uma deve sair correta. Limites de passos e de tempo, sem rede e sem arquivos.

MathTrail · computação pura

Conteúdo

Tudo o que vem embutido no binário e não é código: os catálogos de temas, armadilhas e habilidades, os problemas de referência, os esquemas, os modelos de resolvedor, um exemplo de cada tipo de desenho e as instruções do modelo, com a versão delas.

MathTrail · ao redor do núcleo

Widget

O cartão: Preact compilado em um único arquivo HTML que não carrega nada de fora, com os dicionários dos idiomas dentro. O layout funciona a partir de 320 px, e os botões são fáceis de tocar com o dedo.

MathTrail · ao redor do núcleo

Armazenamento

Encontra o perfil por uma marca oculta, compara antes de gravar, concilia duas abas, restaura o arquivo a partir das revisões fixadas do Drive quando a pessoa adulta pede — e se mantém dentro de um pequeno orçamento de chamadas ao Drive.

MathTrail · ao redor do núcleo

Selagem

Um mecanismo, duas funções: sela o login e os tokens do Google dentro dos tokens que o host guarda, e a resposta de um problema — a do problema atual e a do que foi escrito com antecedência — dentro do perfil. As chaves vêm do Secret Manager e são rotacionadas pelo id da chave.

MathTrail · ao redor do núcleo

Registro

Um resumo estruturado por chamada: a ferramenta, o resultado, a duração, a versão das instruções; as tentativas e os motivos de uma recusa, como eventos do problema. Sem dados pessoais, sem texto dos problemas, sem respostas.

MathTrail · ao redor do núcleo

Contêiner e configuração

Liga as partes entre si e as fecha em ordem inversa, trabalha com tempos limite e um encerramento controlado, lê toda a configuração do ambiente — e se recusa a iniciar no Cloud Run com opções de desenvolvimento ligadas.

A Conta do Google da família

Google OAuth

O login da pessoa adulta, só com o escopo drive.file, a renovação e a revogação. Revogar qualquer token encerra todo o acesso.

A Conta do Google da família

Drive API v3

O único armazenamento de longo prazo: um perfil JSON em uma pasta MathTrail visível no Drive da família, lido e gravado com o próprio token da família.

Google Cloud

Secret Manager

As chaves de selagem e a chave secreta do cliente do Google: versões fixadas, lidas uma vez quando uma instância inicia.

Google Cloud

Artifact Registry

A imagem de cada revisão, por digest; as imagens antigas são excluídas.

Google Cloud

Cloud Logging

Para onde vai o stdout do serviço. As linhas pelas quais as crianças são contadas ficam guardadas por 62 dias em um bucket próprio.

Google Cloud

BigQuery → Data Studio

As contagens de uso ficam guardadas por anos e são recontadas toda noite; nenhuma tabela guarda o nome sob o qual uma criança é contada. Um relatório privado e um público, que não mostra nenhum grupo de menos de dez crianças.

GitHub

Actions

O build, as verificações, a publicação da imagem e a implantação pela Federação de identidade da carga de trabalho — sem chaves de contas de serviço.

GitHub

Pages

Este site: as páginas dos temas, e a política de privacidade e os termos de uso, para onde a tela de permissão aponta.

02

Uma aula, chamada por chamada

O modelo e o cartão chamam as mesmas ferramentas e se apoiam em uma única fonte da verdade — o arquivo do perfil no Drive da família. Para a aula seguir, eles não têm nada a dizer um ao outro.

Cenário
  • uma chamada
  • a resposta dela
  • dentro do host

Problema

  • Pessoa adulta
  • Cartão
  • Modelo do host
  • MathTrail
  • Drive
  1. 1Pessoa adulta → Modelo do host: “um problema novo, por favor”

  2. 2Modelo do host → MathTrail: next_task

  3. O host desenha o cartão assim que a chamada começa: “Preparando o próximo problema…”

  4. limites · a regra escolhe o tema, o nível e a dificuldade

  5. 3MathTrail → Drive: gravar o pedido aberto

  6. 4MathTrail → Modelo do host: o pedido está aberto — busque o pacote

  7. 5Modelo do host → MathTrail: get_package

  8. 6MathTrail → Modelo do host: briefing · 3 problemas de referência · armadilhas · modelos de resolvedor · exemplos de desenhos

  9. O modelo escreve o problema, um resolvedor em Starlark e uma autoverificação

  10. 7Modelo do host → MathTrail: submit_task — até 3 tentativas

  11. verificações · o resolvedor deve encontrar exatamente uma alternativa correta

  12. 8MathTrail → Drive: o problema passa a ser o atual, com a resposta selada

  13. 9Cartão → MathTrail: read_task — o cartão pergunta a cada 4 s

  14. 10MathTrail → Cartão: texto · desenho · cinco alternativas · dica — sem a resposta

Toda chamada também lê o perfil do Drive — essas leituras foram omitidas. O cartão que espera nunca vê o pacote: os problemas de referência do pacote trazem as respostas. Enquanto a criança resolve, o modelo escreve o próximo problema com antecedência, com prepare_task, e quando outro é pedido, next_task o coloca em um cartão novo na hora — sem esta espera.

Resposta

  • Pessoa adulta
  • Criança
  • Cartão
  • Modelo do host
  • MathTrail
  • Drive
  1. 1Criança → Cartão: aperta C

  2. 2Cartão → MathTrail: submit_answer — o chat não gasta nenhum turno

  3. só a primeira resposta · o selo é aberto agora · θ e δ se movem só pelo acerto ou erro

  4. 3MathTrail → Drive: gravar as pontuações e o histórico

  5. 4MathTrail → Cartão: certa ou não · a armadilha por trás de C · o desenho da solução · os passos · a pontuação antes → depois

  6. O cartão vira a análise da resposta, no lugar do problema

  7. 5Cartão → Modelo do host: ui/update-model-context — uma linha, lida com a próxima mensagem da pessoa adulta

  8. 6Pessoa adulta → Modelo do host: “por que um poste a mais?”

  9. 7Modelo do host → Pessoa adulta: responde sobre este mesmo problema, completando o cartão

Apertar a alternativa registra a resposta antes de qualquer explicação, e o cartão vira na hora a análise da resposta. O chat não gasta nenhum turno, e o modelo não desenha nada. Ele lê a linha do cartão com a próxima mensagem da pessoa adulta e responde a uma pergunta sobre o problema sem recontar o que o cartão mostra. Se a linha que o host envia ao modelo se perder, o modelo se atualiza na sua próxima chamada.

Login

  • Navegador da família
  • Host
  • MathTrail
  • Google
  • Drive
  1. 1Host → MathTrail: POST /mcp, sem token

  2. 2MathTrail → Host: 401 · onde estão os metadados

  3. 3Host → Navegador da família: abre /oauth/authorize · PKCE S256

  4. 4Navegador da família → MathTrail: authorize

  5. Uma tela de permissão própria: qual cliente, e para onde ele vai devolver a pessoa adulta

  6. 5Navegador da família → Google: login com o Google · só drive.file

  7. 6Google → Navegador da família: de volta, com o código do Google

  8. 7Navegador da família → MathTrail: /oauth/callback

  9. 8MathTrail → Google: trocar o código

  10. 9MathTrail → Drive: /about — de quem é este Drive?

  11. id de usuário = HMAC da conta do Drive · os tokens do Google selados no nosso código

  12. 10MathTrail → Navegador da família: 302 · code = mt1.c.…

  13. 11Navegador da família → Host: devolve o código

  14. 12Host → MathTrail: /oauth/token · código + verificador

  15. 13MathTrail → Host: acesso 15 min · atualização 30 dias

A pessoa adulta faz login uma vez. O serviço não guarda nada dessa troca: a requisição viaja em um parâmetro state selado, e os tokens do Google, em um código selado.

O problema atual como máquina de estados

Cada regra de “só uma vez” é um campo do perfil, verificado dentro da mesma leitura, cálculo e gravação. Nada mais se lembra de nada.

nonenext_taskbriefing prontorequestedsubmit_task ✓selado, entregueissuedsubmit_answerregistrada uma vezanswered

  • requested ↺recusado, ainda há tentativas — no máximo três por pedido
  • requested → nonetrês recusas, ou 15 minutos desde a abertura do pedido; os problemas do dia não são gastos, e três recusas contam como um pedido malsucedido do dia
  • issued → requested“Outro problema” antes de uma resposta — fica registrado como em branco, e a pontuação não muda
  • answered ↺qualquer resposta posterior — de uma segunda aba ou do modelo — recebe de volta o primeiro resultado e não grava nada
  • answered → requestedo próximo problema, em um cartão novo; o respondido deixa de ser o atual
  • answered → issuedo próximo problema foi escrito com antecedência — next_task o coloca em um cartão novo na hora, pulando requested
  • issued → noneo selo não pode mais ser aberto — a chave passou por duas rotações; nada é registrado

03

Onde fica o estado

Tudo o que um servidor com estado guardaria em um banco de dados fica em outras mãos: em um token selado que o chat guarda, em um cookie no navegador da família ou no próprio Drive da família. Reinicie o serviço ou inicie uma segunda cópia — nada se perde.

O próprio MathTrail

Nada

Nenhuma conta, nenhuma sessão, nenhum cadastro, nenhum token do Google. Cada requisição traz tudo de que precisa.

O armazenamento de tokens do chat

Tokens selados, opacos para o host

Token de acesso
15 min
Token de atualização
30 dias, ≤ 90

Dentro deles vão selados os tokens do Google, um id de usuário opaco para os limites e o país de onde a pessoa adulta fez login.

O navegador da família

Dois cookies, sem sessão

Clientes aprovados
180 dias
Proteção do login (CSRF)
10 min

Perdê-los custa uma tela de permissão a mais, ou um novo login.

O Drive da família

A única coisa guardada a longo prazo

mathtrail-profile.json
até ser excluído

O perfil, as pontuações e o problema atual, com a resposta selada. A pessoa adulta pode abrir o arquivo, e o Drive guarda o histórico dele.

Secret Manager

Leitura única, quando uma instância inicia

Chaves de selagemdepois de uma rotação, a chave anterior ainda é aceita
90 dias
Chave secreta do cliente do Google
fixada

A memória da instância

Só caches: perdidos em um reinício, e nada quebra

Contadores do ritmo de requisições
minutos
Onde está o arquivo do perfil
10 min
Documentos dos clientes
de 5 min a 24 h

O que a v1 deixa de fora de propósito

  • Um banco de dados
  • Filas
  • Um armazenamento de sessões
  • Um cache compartilhado
  • Um banco de problemas prontos
  • Um cliente de API de LLM
  • REST e OpenAPI

04

O arquivo do perfil

Uma criança, um arquivo JSON: não um cache de alguma verdade guardada em um servidor, mas a própria verdade. Cada bloco tem um teto, respeitado a cada gravação, então o arquivo nunca cresce sem limite: um arquivo comum fica dentro da meta, e um que esteja perto dos tetos passa dela. Os problemas passados ficam guardados como impressões digitais, não como texto.

O tamanho do arquivo

≈ 61 KB / ≈ 79 KB

Qual tamanho mostrar
BlocoO que guardaQuem gravaTamanho
Impressões digitaisAté 200 esboços dos problemas entregues, para a verificação de quase repetições — sem texto, sem tema, sem datanext_tasksubmit_task27 KB / 29 KB
Histórico da pontuaçãoOnde estavam θ e o δ do tema antes de cada resposta recente, e θ e cada tema no início de cada um dos últimos 7 dias com respostassubmit_answer14 KB / 15 KB
Pontuações e temasθ e o ponto de partida da criança; para cada tema: δ, as respostas, as armadilhas em que caiu, o domínio e o seu nívelnext_tasksubmit_tasksubmit_answer8 KB / 14 KB
Respostas recentesAs últimas 20 respostas e problemas em branco — e nunca menos que as 5 respostas que a regra lênext_tasksubmit_answer4 KB / 5 KB
Problema atual, seladoA resposta, a armadilha por trás de cada alternativa errada, a solução e o resolvedor — uma única string opacanext_tasksubmit_tasksubmit_answer4 KB / 8 KB
Problema atual, abertoO que o cartão mostra: o texto, o desenho, cinco alternativas, a dica — e, depois da resposta, a própria respostanext_tasksubmit_tasksubmit_answer2 KB / 4 KB
Pedido abertoEnquanto o problema está sendo escrito: o id dele, o briefing, as tentativas usadas e quem escolheu o temanext_taskprepare_tasksubmit_task1,5 KB / 2 KB
Serviço e criançaA versão do esquema, um UUID aleatório, a revisão; o pseudônimo, o ano escolar, os interesses, as anotações; os contadores do diasave_profileedit_profiletoda gravação0,6 KB / 1,5 KB

Um problema escrito com antecedência fica ao lado do atual e pesa mais ou menos o mesmo. O que o arquivo não guarda: os textos dos problemas passados, a resposta às claras até a criança responder, qualquer coisa que nomeie a pessoa adulta e uma etapa — as etapas são calculadas quando são mostradas. O Drive não tem gravação condicional, então o arquivo é lido de novo antes de salvar, e a gravação é recusada se ele tiver mudado: duas abas nunca sobrescrevem uma à outra em silêncio.

05

O que é selado — e por quanto tempo

Um só mecanismo cumpre duas funções: sela o login dentro dos tokens que o chat guarda, e a resposta de um problema — a do problema atual e a do que foi escrito com antecedência — dentro do perfil. Cada selo leva a sua finalidade, então um token de acesso não pode se passar por um token de atualização, e uma resposta selada não pode se passar por mais nada.

Um token, desmontado

mt1.a.kQ7fWx.5rJ0Yb2mQ8vK1nT4pXc9…

mt1
a versão do formato
finalidade
c código · a acesso · r atualização · d cliente · s state · k consentimento · t resposta de um problema
id da chave
tirado da própria chave, então uma rotação passa despercebida
texto cifrado
XChaCha20-Poly1305, um nonce aleatório de 192 bits

Não é um JWT: só o serviço o lê, e nenhum claim pode vazar de uma string opaca para um log.

Validades, em escala logarítmica

  • Código de autorização
    60 s
  • Login em andamento
    10 min
  • Token de acesso
    15 min
  • Token de atualização
    30 dias deslizantes, no máximo 90
  • Chave de selagem
    rotação a cada 90 dias
  • Clientes aprovados
    180 dias

Um servidor sem estado não consegue marcar um código como usado, por isso a janela do código é mantida curta. Depois de uma rotação, a chave anterior ainda é aceita, então nem um token nem uma resposta selada se perdem quando a chave muda.