Rafhael Marsigli Logo

Por Que Estou Construindo uma Plataforma de Marketing Open-Source com Esteroides

18 min de leitura
Por Que Estou Construindo uma Plataforma de Marketing Open-Source com Esteroides

Quase toda agência de marketing com a qual trabalhei usa exatamente a mesma stack caótica: um SaaS para agendamento de redes sociais, o painel do Google Ads aberto em outra aba, ChatGPT em uma terceira janela para ideias de copy, e em algum lugar aquela famosa planilha tentando amarrar tudo isso. Nenhuma dessas ferramentas sabe da existência das outras, nenhuma delas lembra a voz da marca do cliente e ,principalmente, nenhuma delas consegue agir sobre os dados.

Elas apenas os exibem informação e torcem para que você saiba o que fazer em seguida.

Passei anos vendo esse padrão se repetir. O tempo perdido em copiar e colar entre sistemas desconectados é absurdo. Então comecei a fazer uma pergunta:

Por que não existe uma plataforma única que gerencie tudo isso, com IA que tenha contexto real sobre o cliente?

A resposta é desconfortável: é genuinamente difícil construir isso corretamente. O mercado de SaaS tem pouco incentivo para resolver isso quando vender cinco assinaturas separadas para uma agência é mais lucrativo do que uma plataforma integrada.

Então eu comecei.

Observação: Obviamente, esse é um problema que não tecnicamente existe, e estou prestes a resolver, esse é o momento que ou 1. eu faço uma ótima plataforma que ninguém usa, ou 2. eu faço uma ótima plataforma disruptiva. No fim do dia, não tem como sair perdendo, além de muitas horas de vida.

Mas que raios é Meisterfy, de fato?

Meisterfy é uma plataforma self-hosted e open-source de gerenciamento de marketing, construída para agências. Em uma única aplicação, você gerencia:

  • Mídias Sociais: Um calendário visual para planejar, aprovar e publicar posts no Facebook e Instagram. Geração de rascunhos assistida por IA com a identidade da marca do seu cliente incorporada — não apenas um prompt, mas a persona, o tom, as hashtags, o nicho e o idioma-alvo injetados em cada chamada de geração.
  • Google Ads: Monitoramento de campanhas, gerenciamento de grupos de anúncios, análise de termos de pesquisa, critérios de lances e retenção de métricas por 180 dias para uma análise histórica mais rica. Misture dados de API em tempo real com histórico armazenado localmente para relatórios que o próprio console do Google não consegue produzir.
  • Conteúdo e Relatórios de IA: Posts sociais gerados por IA e relatórios de campanha automatizados. Não é uma IA genérica — a identidade real da marca do locatário faz parte do prompt do sistema. A IA sabe para quem está escrevendo.
  • Servidor MCP Nativo: Um servidor Model Context Protocol (MCP) in-process que permite que qualquer agente de IA externo como Claude Desktop, ChatGPT, Gemini, qualquer extensão de IDE compatível com MCP ou automação personalizada — se autentique com uma chave de API com escopo e interaja com seus dados de marketing programaticamente.
  • Multi-Tenant por Design: Cada cliente é um tenant. Cada tenant tem suas próprias conexões, identidade de marca, usuários, funções e permissões.

Não existe vida fácil para o marketeiro

Arquitetura com Go + SvelteKit?

Muita gente deve estar pensando, "S-sv-sv-sve-Svelte???", calma pequeno Padawan! Cada decisão arquitetônica tem uma razão, e as escolhas refletem o estado da arte em 2026.

Go 1.25, chi, pgx/v5, sqlc e goose

Go foi a única escolha razoável para este tipo de plataforma. Os requisitos eram claros: uma linguagem que compila para um único binário, tem uma pegada de memória pequena e previsível, lida com I/O concorrente de forma eficiente e possui excelentes ferramentas de análise estática. Com o lançamento do Go 1.25 e 1.26, o conjunto de ferramentas e o tempo de execução nunca foram tão robustos.

E o segundo motivo - que preciso deixar claro - embora não tenha importância real para este contexto. Eu já uso PHP/Laravel há decadas, estou estudando Go, e estou adorando. Hora de aplicar algumas coisas em prática, não é? E é por esse motivo que eu fugi de ORM e qualquer tipo de mágica, se fosse para manter essa estrutura eu continuaria com o Laravel que já faz isso com os pés nas costas.

  • chi é mínimo e composível: sem mágica! apenas cadeias de middleware explícitas. Você pode rastrear exatamente o que é executado em qualquer rota lendo a definição do roteador.
  • pgx/v5 usa o protocolo nativo do Postgres, suporta pool de conexões via pgxpool e evita a sobrecarga de abstração do database/sql.
  • sqlc gera código Go com segurança de tipo a partir de consultas SQL brutas. Eu escrevo SQL, o sqlc gera as funções Go. Sem ORM, sem interface{} na fronteira do banco de dados, sem surpresas de composição de consulta em tempo de execução. Cada consulta é validada em tempo de construção.
  • goose gerencia migrações de esquema sequencialmente com arquivos SQL explícitos de up/down. Cada alteração de esquema é rastreada, reversível e reproduzível. O binário cmd/migrate executa o goose contra o banco de dados de destino: o mesmo código em desenvolvimento, CI e produção.

SvelteKit 5, Svelte Runes, Tailwind v4, Paraglide.js e Bun

  • SvelteKit 5 com Svelte Runes é a pilha de frontend reativa mais ergonômica disponível atualmente, e que ninguém usa 🫣. Runes substituem o sistema de reatividade implícito $: por primitivas explícitas — $state, $derived, $effect. Esta não é uma mudança cosmética, em uma aplicação complexa com fluxos de trabalho de aprovação em várias etapas, respostas de streaming de IA em tempo real e interfaces de planejamento baseadas em calendário, a reatividade implícita cria cadeias de dependência invisíveis que são impossíveis de raciocinar em escala. Runes explícitas eliminam essa classe de bug inteiramente, e benchmarks mostram que o Svelte 5 supera outras bibliotecas de reatividade por uma margem sólida.
  • Paraglide.js gera funções de mensagem i18n totalmente seguras em tempo de construção. Cada chave de tradução é uma função TypeScript. Se uma chave existe em inglês, mas está faltando em português, a etapa de auditoria i18n do CI falha na construção. Sem traduções ausentes silenciosas em produção.
  • Bun como gerenciador de pacotes e executor de testes substitui npm/yarn e Jest. Instalações mais rápidas, execuções de testes mais rápidas.

Binário Único

Essa é a parte mais bonita do Go, isso quer dizer: zero dependências em tempo de execução. Para produção, o binário Go incorpora o SPA SvelteKit compilado usando o pacote embed do Go:

//go:embed cmd/server/ui/dist
var uiFS embed.FS

Um binário, um só processo, e nenhum tempo de execução do Node.js é necessário no servidor. Você pode executar o Meisterfy em um VPS de $5 com Docker e PostgreSQL. É isso.

Multi-Tenancy antes de tudo

Cada objeto no sistema tem escopo de tenant. O esquema do banco de dados impõe isso no nível da restrição — chaves estrangeiras em cascata, não filtragem no nível da aplicação.

CREATE TABLE posts (
id TEXT PRIMARY KEY, -- ULID, classificável lexicograficamente por tempo
tenant_id TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'draft'
CHECK (status IN ('draft', 'approved', 'scheduled', 'published')),
content TEXT NOT NULL DEFAULT '',
hashtags JSONB NOT NULL DEFAULT '[]',
platforms JSONB NOT NULL DEFAULT '[]',
workflow JSONB,
...
);
CREATE INDEX idx_posts_scheduled ON posts (tenant_id, scheduled_date)
WHERE status = 'scheduled';

As chaves primárias são ULIDs, não UUIDs. ULIDs são classificáveis lexicograficamente por tempo de criação, consultas de intervalo e paginação em ULIDs são eficientes sem a necessidade de um índice created_at separado, a própria chave codifica a ordem temporal. Cada tenant possui um objeto completo de identidade de marca: persona primária, tom de voz, nicho, idioma padrão e hashtags padrão. Essa identidade é injetada em cada chamada de geração de IA como parte do prompt do sistema. A geração de conteúdo segue as diretrizes definidas para a marca, aumentando consideravelmente a qualidade da entrega com menos custo de tokens.

Sobre as permissões, um usuário pode ser um admin no tenant A e um reader no tenant B. As permissões são semeadas a partir de migrações SQL e aplicadas na camada do repositório — não apenas na fronteira do manipulador HTTP. Permissão é algo tão padrão e obrigatório no mercado atual, que preciso extrair para um código reaproveitável em outros projetos.

"Com licença, senhorita, você já ouviu a palavra do Meisterfy?"

O Sistema de Conectores

Essa é a minha parte preferida do projeto, e pra mim, o coração dele todo. Cada serviço externo, como Google Ads, Meta, S3, R2, OpenAI, Claude, Gemini, Kimi, Groq, Resend, Brevo, Sentry e etc. passa por um sistema de conectores unificado. As credenciais residem na tabela integrations, criptografadas em repouso com AES-256-GCM:

// Encrypt criptografa texto simples usando AES-256-GCM com um nonce aleatório.
// Retorna base64(nonce || ciphertext || GCM auth tag).
func Encrypt(key []byte, plaintext string) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
sealed := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
return base64.StdEncoding.EncodeToString(sealed), nil
}

O nonce é gerado aleatoriamente por chamada de criptografia, a tag de autenticação GCM fornece verificação de integridade, um ciphertext adulterado é rejeitado. A chave de criptografia é derivada do seu JWT_SECRET, um valor que você define e que nunca sai do seu servidor. Seu token de atualização do Google Ads, suas credenciais OAuth da Meta, sua chave de API do OpenAI... Nada disso é legível por ninguém além da sua própria instância em execução.

Uma única integração pode ser atribuída a vários tenants: uma conexão do Google Ads pode atender a cinco contas de clientes diferentes sem duplicar credenciais, revogue a integração e ela desaparecerá de todos os tenants simultaneamente.

MCP no day-one

Sim! aqui o negócio fica mais interessante.

Este é o recurso que torna o Meisterfy estruturalmente diferente de tudo o mais no espaço de ferramentas de marketing. Em 2026, o Model Context Protocol tornou-se a "camada de IA ausente", padronizando como as aplicações de IA se comunicam com a infraestrutura de dados. O mercado de MCP está quente e praticamente toda IA tem essa integração, e centenas de centenas de ferramentas usam.

O Que É MCP

Na prática, MCP é uma API, e nada mais!

Mas, na linguagem de robôs, ela tem um impacto adicional: MCP é um padrão aberto que define como os agentes de IA se comunicam com ferramentas externas via HTTP. Qualquer cliente compatível com MCP - seja Claude Desktop, GitHub Copilot, extensões do VS Code, Cursor ou scripts de automação personalizados - pode descobrir ferramentas, ler recursos e chamar ações em um servidor MCP usando um protocolo JSON-RPC bem definido.

Hoje, praticamente nenhum SaaS de marketing expõe um endpoint MCP. Seu agente de IA pode ler uma captura de tela de um painel e preencher um formulário. Esse é o limite, isso cria um gargalo enorme para empresas que tentam passar de prompts de chat simples para fluxos de trabalho automatizados avançados.

O Servidor MCP do Meisterfy

Em vez de usar uma biblioteca de terceiros, a melhor opção para ter 100% de controle e manutenção é construir o servidor MCP do zero dentro da aplicação Go. Isso mantém a superfície de dependência mínima e me dá controle total sobre a camada de transporte HTTP Streamable.

type Server struct {
name string
version string
tools map[string]*toolDef
resources map[string]*resourceDef
}
func (s *Server) RegisterTool(name, description string, schema map[string]any,
handler func(ctx context.Context, args json.RawMessage) ToolResult) {
s.tools[name] = &toolDef{
Name: name,
Description: description,
InputSchema: schema,
handler: handler,
}
}

O endpoint MCP é montado em /mcp, separado da API REST protegida por JWT. A autenticação usa chaves de API com escopo readonly, editor ou admin geradas e revogadas diretamente da UI de configurações do tenant. Cada chave está vinculada a um tenant específico, então o contexto do agente é sempre corretamente escopado, isso se alinha perfeitamente com os requisitos de segurança empresarial para MCP, que enfatizam ambientes isolados e declarações explícitas de contexto para evitar "envenenamento de ferramentas" e acesso não autorizado.

Ferramentas Disponíveis para Agentes

Muitas! Entre elas: diversas métricas ao vivo e de monitoramento, gestão de conteúdo, gestão de campanhas e etc.

s.RegisterTool("get_live_metrics",
"Get live campaign metrics from Google Ads API",
map[string]any{"type": "object", "properties": map[string]any{}},
func(ctx context.Context, _ json.RawMessage) mcp.ToolResult {
tenantID, ok := mcp.TenantIDFromContext(ctx)
if !ok {
return mcp.ErrResult("tenant not authenticated")
}
client, _, err := factory(ctx, tenantID)
if err != nil {
return mcp.ErrResult(err.Error())
}
metrics, err := client.GetLiveMetrics(ctx)
if err != nil {
return mcp.ErrResult(err.Error())
}
return mcp.Ok(metrics)
},
)

O que isso significa na prática? conecte o Claude Desktop ao Meisterfy com uma chave de editor, e você pode ter uma conversa como:

"Verifique todas as campanhas ativas para o cliente X. Quais palavras-chave têm uma taxa de conversão abaixo de 1% e estão gastando mais de 15% do orçamento da campanha?"

O agente puxa métricas em tempo real, executa a análise e apresenta a resposta. Então:

"Adicione essas palavras-chave como negativas e reduza o orçamento da campanha em 10%."

O agente chama get_campaign_criteria, adjust_campaign_budget e confirma a alteração, tudo na mesma conversa, tudo a partir de dados reais. Esta é a mudança de "Integrações como Serviço" para "Contexto como Protocolo". Ainda é possível fazer conversas buscando insight, o céu é o limite aqui.

Boa escolha!

Propostas! Não Palpites

O Motor de Ajuste é um pacote interno que avalia o desempenho da campanha e gera propostas de ajuste de lance ou orçamento. É baseado em propostas, não autônomo, cada proposta é registrada na trilha de auditoria, e o operador decide se a aplica.

O motor aplica proteções rígidas antes de gerar qualquer proposta:

// Proteção 1: idade da campanha — não toque em campanhas com menos de 14 dias
if time.Since(createdDate) < minAge {
return nil, nil // pular silenciosamente
}
// Proteção 2: intervalo de ajuste — não reajuste em menos de 7 dias
if time.Since(lastAdjusted) < interval {
return nil, nil
}
// Proteção 3: dados mínimos — precisa de pelo menos 3 dias com dados nos últimos 7 dias
since := time.Now().AddDate(0, 0, -7)
allMetrics, err := e.metrics.GetHistory(ctx, resource.TenantID, since)
// ...E por aí vai

Nesse contexto, uma campanha com menos de 14 dias é ignorada: ela não acumulou dados suficientes para fazer um ajuste significativo. Uma campanha ajustada há menos de 7 dias é ignorada, mudanças repetidas desestabilizam o período de aprendizado. Uma campanha sem histórico de métricas suficiente não gera proposta.

Essas proteções são configuráveis por tenant: MinCampaignAgeDays, AdjustmentIntervalDays, MaxIncreasePct, MaxIncreaseBRL, MaxDecreasePct, MaxDecreaseBRL. O motor opera dentro de um envelope de orçamento estritamente limitado. Ele não tem autoridade ilimitada.

Esta é a diferença entre automação que é confiável e automação que é perigosa.

Estratégia de Testes: Cobertura Que Significa Algo

Tenho opiniões fortes sobre testes. Um número de cobertura só é significativo se os testes estiverem realmente exercitando os modos de falha corretos. Para um projeto open-source que busca adoção empresarial, a estabilidade é primordial.

Testes de Integração de Backend: 86,5% de Cobertura de Repositório

Os testes de integração são executados contra o PostgreSQL real. Eu uso fergusstrange/embedded-postgres para iniciar uma instância do Postgres 16 in-process para execuções de testes de unidade, e um contêiner postgres:16-alpine real no CI.

Sem mocks. Cada método de repositório — Create, Update, List, GetByID, Delete — é executado contra um banco de dados com migrações reais aplicadas. A tag de construção integration separa estes dos testes de unidade para que possam ser executados em paralelo de forma apropriada:

Terminal window
go test -tags=integration -race -count=1 -p 1 ./...

A flag -race executa o detector de corrida do Go em cada teste. A flag -count=1 desabilita o cache de testes — testes instáveis que passam devido ao estado de cache obsoleto não se escondem.

Testes de Unidade de Backend

Os testes de unidade cobrem o pacote de criptografia (ida e volta de criptografia/descriptografia AES-256-GCM, rejeição de ciphertext adulterado), emissão e validação de JWT, hashing de senha bcrypt e contratos de manipulador HTTP. Tudo com -race.

Testes de Fumaça

7 testes de fumaça verificam se o binário construído e em execução responde corretamente a: GET /health, POST /auth/login com credenciais inválidas (401), GET /admin/* sem um token (401) e POST /mcp com uma chave inválida (401). Estes são executados no CI contra um binário que foi construído a partir do código-fonte na mesma execução do pipeline.

Frontend: Mais de 260 Testes em Três Camadas

  • 159 testes Vitest — cobertura CRUD completa para cada módulo cliente de API. Cada formato de requisição, cada caminho de erro.
  • 99 testes de componente de navegador — Playwright + Vitest executando em um navegador Chromium real. Comportamento do componente, não apenas renderização.
  • 10 testes E2E Playwright — fluxos de autenticação, aplicação de rotas protegidas, caminho dourado completo de criação de posts, incluindo fluxo de aprovação.

A suíte E2E requer uma pilha em execução. No CI, a pilha completa é iniciada via Docker Compose, as migrações são executadas, um usuário de teste é semeado e o Playwright é executado contra uma instância ativa. O relatório HTML é carregado como um artefato do GitHub em caso de falha.

CI/CD

Cada push e pull request para main aciona o pipeline completo. Os jobs são executados em paralelo onde as dependências permitem:

all-checks:
name: All Checks Passed
if: always()
needs:
- go-lint # golangci-lint
- go-build # go build + go vet
- go-test-unit # testes de unidade com detector de corrida
- go-test-integration # camada de repositório contra PostgreSQL real
- go-security # govulncheck — análise de grafo de chamadas executável
- smoke-test # 7 testes de contrato contra binário em execução
- frontend-quality # eslint + tsc + auditoria i18n
- frontend-test # 260+ testes vitest + playwright
- frontend-build # bun run build
steps:
- name: Check all jobs
run: |
results='${{ toJSON(needs) }}'
if echo "$results" | grep -qE '"result":"(failure|cancelled)"'; then
echo "One or more jobs failed or were cancelled"
exit 1
fi

O job de portão agrega todos os resultados. Um único job cancelado ou falho faz com que o portão falhe, e o PR não pode ser mesclado. go-security executa govulncheck, o scanner de vulnerabilidades do Go. Ao contrário do go audit, o govulncheck rastreia o grafo de chamadas real do binário compilado e relata apenas vulnerabilidades em funções que são alcançáveis a partir do seu código. frontend-quality executa ESLint, verificação estrita do TypeScript (svelte-check) e um script de auditoria i18n personalizado que verifica a paridade de localidade — se alguma chave existe em inglês, mas está ausente em português (ou vice-versa), a construção falha. O pipeline e2e (e2e.yml) é separado, ele é executado em push para main e em despacho manual, iniciando a pilha completa do Docker (Postgres, migrações, binário Go, SPA SvelteKit) e executando a suíte Playwright.

A Lacuna de Mercado É Real?

Bom, pelo menos no que eu pesquisei... é! Meus pontos:

  1. Nenhuma plataforma unificada de social + anúncios com contexto real de IA: Ferramentas como Hootsuite e Buffer lidam com agendamento social. O console do Google Ads é um visualizador de dados. Não há uma única plataforma que contenha ambos, com uma identidade de marca compartilhada impulsionando a IA em ambas as superfícies, isso se deve a diversos fatores, mas meu chute é que eles são grandes demais para certos riscos, astronomicamente diferente do nosso caso.
  2. Nenhuma plataforma de marketing com um servidor MCP nativo: Quase nenhum SaaS de marketing expõe um endpoint MCP. Isso significa que os agentes de IA, as ferramentas que as equipes de engenharia estão construindo e integrando ativamente, não podem interagir programaticamente com dados de marketing, ler capturas de tela ou preencher formulários da web. Esse é o limite. O Meisterfy eleva isso: agentes que leem dados em tempo real, geram conteúdo, modificam campanhas e interagem com a identidade da sua marca em uma única sessão autenticada.
  3. Nenhuma alternativa open-source e auto-hospedada para agências: Aqui eu tenho certeza, todas as ferramentas de marketing para agências são SaaS. Preços por assento, bloqueio de fornecedor, sem controle sobre seus dados, sem capacidade de auto-hospedagem ou personalização. Embora existam projetos open-source para automação de marketing (como o ótimo Mautic), o Meisterfy é a única plataforma totalmente open-source neste espaço que unifica social, anúncios e contexto de IA.

O Que Vem Por Aí

Bom, se você teve a paciência de ler esse longform imenso até aqui, muito obrigado! O Meisterfy tem como meta um lançamento alfa no final de 2026. O desenvolvimento é ativo e está em andamento.

O código-fonte está no GitHub. A arquitetura já é pensada em nível de produção, a suíte de testes é abrangente. O pipeline CI/CD executa 11 jobs em cada PR e não permite que nada questionável passe. Se você está construindo automação de IA para fluxos de trabalho de marketing, ou simplesmente quer uma plataforma de monitoramento e gestão robusta, fique de olho neste projeto.

Compartilhe com quem você gosta