Início rápido: do cadastro à URL publicada

Público: um agente de código trabalhando para uma pessoa, mais os poucos minutos dessa pessoa que o caminho não consegue evitar. A conta é de uma pessoa, então uma pessoa a cria; tudo depois disso é seu e pode ser verificado pelo terminal.

Tudo abaixo foi conferido em dev em 24/09/2026. Os tempos são o que os passos levam lá, não promessas.

0. O que a pessoa faz uma vez (alguns minutos, no navegador)

  1. Abra https://dev.infra.doublex.ai.
  2. Clique em Começar (Get started em inglês) e crie a conta com e-mail e senha, ou Google. A organização nasce no primeiro acesso, no plano Hobby, gratuito (1 GiB de lake, 3 usuários finais, 1 ambiente).
  3. Na /welcome, copie o comando que aparece. Ele tem esta cara:
claude mcp add --scope user --transport http cortex https://mcp.dev.infra.doublex.ai/mcp

O --scope user importa: registra o servidor uma vez para todas as pastas. Sem ele o Claude Code cria uma entrada de escopo local para a pasta atual, e a entrada local tem prioridade sobre a de escopo user.

Peça esse comando, ou a tela em que ele está. Não peça senha, e não peça para a pessoa colar um token no chat: o fluxo pelo navegador abaixo nunca precisa disso.

1. Conectar o servidor MCP

Rode o comando. No primeiro uso o Claude Code abre o navegador, a pessoa entra com a mesma conta do site, e o cliente guarda a sessão.

Confira: chame whoami. Ele responde com a organização, o plano e os seus escopos.

Se der errado:

SintomaO que fazer
o cortex aparece mas não tem ferramentas, ou pede loginno Claude Code digite /mcp, escolha cortex e depois Authenticate, com a mesma conta do site
aponta para localhost ou outra URL erradaclaude mcp remove cortex -s local (e -s user se for o caso), depois adicione de novo com o comando da /welcome
o navegador diz Authentication failed … invalid_targetera configuração do servidor, corrigida em 24/09/2026; se voltar, mande a URL completa da barra de endereço para a equipe do Cortex

2. Instalar o CLI e entrar

O deploy roda no terminal, não no servidor MCP, então o terminal precisa do CLI companion.

O comando cortex. Ele não está no npm, e npx cortex instala outro pacote com o mesmo nome: nunca rode. Construa a partir do checkout da plataforma com make cli, que deixa o cortex em ~/.local/bin (coloque no PATH). cortex --version prova que é o nosso. Ele fala com dev por padrão.

make cli                # no checkout da plataforma
cortex login            # abre o navegador
cortex whoami --json    # subject, org (slug, name, plan), scopes, api

O cortex login descobre o servidor de autorização sozinho, registra um cliente público, grava a credencial em ~/.cortex/credentials.json com modo 0600 e a renova antes de expirar. Se o navegador não abrir, crie um token na página Tokens do console e rode cortex login --pat <token>; em CI, coloque o token em CORTEX_TOKEN.

Confira: cortex whoami --json sai com 0. Código de saída 3 quer dizer que não há credencial válida: rode cortex login de novo.

3. Criar o projeto

Combine o slug com a pessoa antes: ele é global.

Pergunte a região à pessoa; nunca escolha por ela. region é obrigatória e permanente: o lake e o app rodam nela (southamerica-east1 para o Brasil, us-central1 para os Estados Unidos). Em dev, hoje, o runtime existe em us-central1; uma região sem runtime é recusada pelo create_project com um erro que diz quais regiões funcionam. Conte isso à pessoa e pergunte de novo; nunca tente outra região por conta própria.

create_project        { slug: "acme-bi", name: "Acme BI", region: "us-central1" }
get_project           { project_id }        # repita até o status ser "active"
apply_project_config  { project_id, yaml }  # um cortex.yaml mínimo: version e project

O projeto nasce provisioning e passa a active em poucos minutos. Aplique uma configuração logo depois, mesmo mínima: hoje o catálogo sincroniza no primeiro apply, e um run_sql antes dele responde que o catálogo ainda não foi sincronizado.

version: 1
project: { slug: acme-bi, name: Acme BI, region: us-central1 }

4. Carregar dados

Uma planilha ou um CSV:

upload_file      { project_id, filename: "vendas.xlsx" }  # devolve upload_id e uma url assinada
# PUT dos bytes nessa url (por exemplo com curl -X PUT --upload-file)
finalize_upload  { upload_id }                            # devolve um run_id
get_run          { run_id }                               # até o status ser "succeeded"

Uma pasta de trabalho vira uma tabela por aba, com o nome raw.<arquivo>_<aba>; o get_run lista os nomes reais em tables_touched.

Os arquivos estão no disco da pessoa? cortex upload vendas.xlsx dados/ --json faz essas quatro chamadas para cada arquivo e espera as cargas. Ou deixe para o primeiro deploy (passo 6, cortex deploy --with-data), que os sobe antes do app.

Um banco acessível pela internet (Postgres, MySQL, SQL Server):

set_secret    { project_id, name: "ERP_DSN", value }   # o valor nunca vai para arquivo
add_source    { project_id, name: "erp", type: "database", connector: "postgres", secret: "ERP_DSN" }
test_source   { project_id, source: "erp" }

Depois declare um pipeline para essa fonte em pipelines no cortex.yaml, aplique com apply_project_config e rode com run_pipeline { project_id, pipeline: "<nome>" } e get_run. Um banco dentro da rede da pessoa não é alcançável pela nuvem hoje.

Confira: list_tables { project_id } devolve as tabelas, e sample_table { project_id, table } mostra linhas que a pessoa reconhece. Se os números estiverem errados aqui, pare: nada depois vai consertá-los.

5. Modelar e definir as métricas

propose_model       { project_id }         # devolve um change set; não aplica nada
get_change_set      { change_set_id }      # leia e explique para a pessoa
approve_change_set  { change_set_id }      # pergunta à pessoa, com o diff
run_models          { project_id }

As métricas ficam em semantic.metrics do cortex.yaml: acrescente e aplique com apply_project_config. Depois:

query_metrics  { project_id, query: { metrics: ["revenue"], dimensions: ["orders.channel"] } }

Confira: o query_metrics devolve números e o SQL que os produziu. Mostre os dois para a pessoa. Número sem SQL é defeito, não resposta.

6. Publicar o app

O projeto precisa já existir e estar active (passo 3), e o --slug precisa ser o slug dele.

cortex init bi-nextjs --dir acme-bi --slug acme-bi --region us-central1
cd acme-bi
cortex deploy --json     # empacota, envia, Cloud Build, Cloud Run; imprime a URL

O deploy respeita .gitignore e .cortexignore e recusa .env*. Se a resposta for approval.required, o ambiente é protegido: mostre o change set para a pessoa, aprove com approve_change_set e rode cortex deploy de novo.

A primeira publicação de um slug leva uns minutos a mais depois do live: o certificado do novo endereço está sendo emitido. O deploy espera a URL responder por HTTPS (até 6 minutos) antes de imprimi-la. Se ele imprimir um warning, a publicação está boa: espere um minuto e tente de novo.

Primeiro deploy e dados locais. No primeiro deploy de um projeto (nenhum deployment em nenhum ambiente ainda), a pessoa decide se os arquivos de dados do disco dela sobem junto com o app. Pergunte antes de rodar o comando, ou chame create_deployment { project_id }: ele pergunta e devolve o comando a rodar, cortex deploy --with-data ou cortex deploy.

  • cortex deploy --with-data --json sobe antes todos os arquivos de dados locais (CSV, TSV, Excel .xlsx, Parquet), espera cada um carregar e só então publica o app. Os arquivos são as fontes type: files do cortex.yaml cujo path existe no disco; sem elas, todo arquivo de dados da pasta, com .gitignore e .cortexignore aplicados. O que subiu como dado fica fora do pacote do app (--keep-data-in-app manda também).
  • --data <caminho|glob>[,...] escolhe os arquivos (o .gitignore não se aplica a ele); --no-data publica só o app. --env pr<N> manda os dados para essa prévia.
  • Sem flag e sem terminal (você, um agente), o comando nunca pergunta: publica só o app e imprime em stderr uma dica de --with-data. O --json traz data.decision, data.uploads e data.run_ids.
  • Falha de dados para antes de publicar o app: sai com 7 quando um upload é recusado, com 1 e run.failed quando uma carga falha. Leia get_run_logs { run_id }, corrija o arquivo e rode de novo.
  • .xls não é lido: salve como .xlsx.
  • Nunca sobem como dado, com qualquer flag ou glob: .env*; chaves e arquivos de credencial (*service-account*.json, *credentials*.json, *.pem, *.key, *.p12, *.pfx, id_rsa*, id_ed25519*, .npmrc, .pypirc); arquivos de texto cujos primeiros 4 KiB têm uma chave privada ou um JSON de service account; links simbólicos que resolvem para fora da pasta (glob e varredura de pasta nunca seguem link). Cada um imprime uma linha warning: em stderr e entra em refused (data.refused no deploy) com o reason. Nomeado sozinho, o comando sai com 7. Globs pulam package.json, tsconfig*.json e outros JSON de ferramenta.

Hoje o dashboard do template mostra dados de exemplo: o login de usuário final funciona, mas os números ainda não vêm do lake. Diga isso para a pessoa.

Confira: abra https://acme-bi.apps.dev.infra.doublex.ai; /healthz responde {"status":"ok"}. Se o build falhar, leia get_build_logs { deployment_id }; para voltar, rollback_deployment { project_id }.

7. Conte para a pessoa o que existe agora

  • A URL, e quem pode entrar nela (convide com invite_end_user { project_id, email, roles }).
  • As métricas que você definiu, nas palavras dela, com o SQL de uma delas.
  • O que o dashboard mostra hoje (dados de exemplo) e o que ainda falta ligar.
  • Se o app está do tamanho certo: get_app_metrics { project_id, env: "production" } depois de algum tráfego (veja "O meu app está do tamanho certo?" em Criar um projeto).

Quando algo falha

SintomaO que quer dizerO que fazer
cortex sai com 3sem credencial, ou expiroucortex login, ou cortex login --pat <token>
cortex: command not found, ou npx cortex mostra outra ferramentao companion não está instalado; o nome no npm é de outra pessoamake cli no checkout da plataforma; nunca npx cortex
init sai com 7 dizendo que os pacotes não foram construídoso CLI foi construído sem o bundlemake cli e init de novo (nada foi escrito)
deploy diz que não achou o projetoo project.slug do cortex.yaml não é um projeto existentecreate_project antes, ou corrija o slug, ou passe --project <id>
deploy sai com approval.requiredo ambiente é protegidoapprove_change_set com a pessoa, depois deploy de novo
a URL falha no handshake TLS logo depois do deployo certificado do novo endereço ainda está sendo emitidoespere; o deploy já esperou até 6 min e avisou
deploy --with-data sai com 7 dizendo "found no local data file"os dados estão no .gitignore, ou são .xls--data <pasta>, ou uma fonte type: files com path local; salve .xls como .xlsx
upload ou deploy --data sai com 7 dizendo que o arquivo "looks like a credential" ou "is a symbolic link"o arquivo é chave, credencial ou link para fora da pasta; o companion nunca sobe isso como dadosuba o arquivo de dados de verdade; copie o arquivo linkado para dentro da pasta; não contorne
deploy --with-data sai com 1 e run.failedum arquivo não carregou; o app não foi publicadoget_run_logs { run_id }, corrija o arquivo, rode de novo (--no-data publica só o app)
cortex sai com 9o comando existe mas ainda não foi implementado (ingest, dev, config)use as ferramentas do MCP acima
query_metrics recusa uma métricaela não está no modelo semânticoget_semantic_model, depois defina; nunca invente SQL para um número

Todo comando aceita --json e todo erro traz code, message e hint. Decida pelo código de saída, não pelo texto.