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)
- Abra
https://dev.infra.doublex.ai. - Clique em Começar (
Get startedem 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). - 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/mcpO --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:
| Sintoma | O que fazer |
|---|---|
o cortex aparece mas não tem ferramentas, ou pede login | no Claude Code digite /mcp, escolha cortex e depois Authenticate, com a mesma conta do site |
aponta para localhost ou outra URL errada | claude 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_target | era 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, enpx cortexinstala outro pacote com o mesmo nome: nunca rode. Construa a partir do checkout da plataforma commake cli, que deixa ocortexem~/.local/bin(coloque noPATH).cortex --versionprova que é o nosso. Ele fala comdevpor padrão.
make cli # no checkout da plataforma
cortex login # abre o navegador
cortex whoami --json # subject, org (slug, name, plan), scopes, apiO 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 projectO 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 URLO 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 --jsonsobe 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 fontestype: filesdocortex.yamlcujopathexiste no disco; sem elas, todo arquivo de dados da pasta, com.gitignoree.cortexignoreaplicados. O que subiu como dado fica fora do pacote do app (--keep-data-in-appmanda também).--data <caminho|glob>[,...]escolhe os arquivos (o.gitignorenão se aplica a ele);--no-datapublica 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--jsontrazdata.decision,data.uploadsedata.run_ids. - Falha de dados para antes de publicar o app: sai com 7 quando um upload é recusado, com 1 e
run.failedquando uma carga falha. Leiaget_run_logs { run_id }, corrija o arquivo e rode de novo. .xlsnã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 linhawarning:em stderr e entra emrefused(data.refusednodeploy) com oreason. Nomeado sozinho, o comando sai com 7. Globs pulampackage.json,tsconfig*.jsone 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
| Sintoma | O que quer dizer | O que fazer |
|---|---|---|
cortex sai com 3 | sem credencial, ou expirou | cortex login, ou cortex login --pat <token> |
cortex: command not found, ou npx cortex mostra outra ferramenta | o companion não está instalado; o nome no npm é de outra pessoa | make cli no checkout da plataforma; nunca npx cortex |
init sai com 7 dizendo que os pacotes não foram construídos | o CLI foi construído sem o bundle | make cli e init de novo (nada foi escrito) |
deploy diz que não achou o projeto | o project.slug do cortex.yaml não é um projeto existente | create_project antes, ou corrija o slug, ou passe --project <id> |
deploy sai com approval.required | o ambiente é protegido | approve_change_set com a pessoa, depois deploy de novo |
| a URL falha no handshake TLS logo depois do deploy | o certificado do novo endereço ainda está sendo emitido | espere; 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 dado | suba 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.failed | um arquivo não carregou; o app não foi publicado | get_run_logs { run_id }, corrija o arquivo, rode de novo (--no-data publica só o app) |
cortex sai com 9 | o comando existe mas ainda não foi implementado (ingest, dev, config) | use as ferramentas do MCP acima |
query_metrics recusa uma métrica | ela não está no modelo semântico | get_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.