Put a Microscope on Hermes: Full Visibility into Agent Execution

Alibaba Cloud’s OpenTelemetry-based observability plugin brings full visibility to Hermes AI agent execution, enabling traceable costs, performance, and security auditing.

Hermes is an autonomous AI agent runtime frame developed by Nous Research. Rather than a one-shot Q&A pair-style model encapsulation, it is an agent runtime that continuously runs, invokes tools, accumulates experience, and grows throughout the usage procedure.

When an AI agent truly starts solving a problem — whether it completes correctly or exhibits bias — the real challenge is often not whether the result is right, but what exactly it did.

A single run of Hermes is not an ordinary model invocation. A seemingly simple interaction may involve multiple rounds of inference, tool calling, result reinjection, context expansion, and new inference loops. The model decides whether a tool is needed for the next step, and tool results in turn affect the subsequent inference path. Cost, latency, and faults often occur in the middle of this procedure.

If the system can only provide a final reply, a few scattered logs, or a usage summary for a single invocation, Hermes remains a black box. You know it completed the job, but you can hardly tell how. You know the request consumed a lot of tokens, but you can hardly tell which step drove up the cost. You know the user experience has slowed down, but you can hardly determine whether model generation slowed, tool execution went abnormal, or ReAct (Reasoning + Acting) loops spiraled out of control.

This is exactly our starting point for building observability into Hermes.

This article introduces a set of observability plugin solutions provided by Alibaba Cloud for Hermes. It can revert the real execution procedure of Hermes into a structured invocation chain: where a session starts, how many rounds of inference it goes through, which tools are invoked, how many tokens are spent, which step is the most time-consuming, and at which edge zone a fault occurs. Which operations are malicious, and how much sensitive data has been leaked.

If you are using Hermes for real-world jobs, you will almost certainly encounter these problems:

● Why is it so expensive this time?

● Why is it so slow this time?

● Did it actually invoke that tool?

● Did the tool it used leak data?

What these problems have in common is that they are not “results” but “procedures”. So, if we can only see the last reply, then from an observational point of view, Hermes is still not interpretable.

What Exactly Are We Trying to Solve
The Alibaba Cloud Hermes observability plugin focuses on solving the following four types of problems.

The first is that the procedure is invisible.

After integrating an LLM, many systems still only show user input, final output, and a usage summary. But the real run of Hermes is far more than that. Behind a single response, there may be multiple rounds of inference, multiple tool executions, continuous context expansion, and new inference loops. Without a call chain, the intermediate procedure is essentially empty. The first thing we did was fill in that gap.

The second is that costs are not attributable.

The token bill itself isn’t the hardest problem — the hardest part is not knowing where the money actually goes. A Hermes run can be expensive because the context suddenly explodes in a certain round, a tool returns an oversized result, the final round produces overly long output, or a certain class of jobs naturally triggers more steps. Without visibility into the tokens for each round of model invocation, cost analysis is nothing more than guesswork.

The third category is that performance cannot be broken down.

Users will only tell you “it’s getting slower,” but “slow” by itself carries no useful info. What you really need to distinguish is: is the first token slow, or is overall generation slow? Is tool execution slow, or is multi-round ReAct inference itself running too long? Only by separating these stages can a “slowdown” become a problem you can actually pinpoint.

The fourth category is that results cannot be reviewed.

Often the hardest issues to deal with are not clear-cut faults, but cases where “it looks like it succeeded, but the result is wrong.” This is very common in agent systems: Hermes invokes the wrong tool, the tool returns incomplete results, Hermes continues to infer based on partial info, and ultimately produces an answer that seems reasonable on the surface but has already gone off track. Without traces, post-mortem review is nearly impossible. With traces, the problem shifts from “guessing the cause” to “examining the path.”

What We Did
What we built for Hermes is a set of OpenTelemetry (open telemetry frame)-based Tracing Analysis capabilities.

The core idea is straightforward: install runtime instrumentation in the Python environment where Hermes runs, establish spans around the key execution borders of Hermes, and then report traces and indicators to the observability backend through OTLP (OpenTelemetry Protocol), a standard protocol.

Our focus is not on “what the last row of reply looks like”, but on the running procedure of Hermes itself.

This Solution Has Several Advantages Worth Highlighting
It is worth mentioning that this set of plugins is not a temporary instrumentation script thrown together, but is designed along the OpenTelemetry system.

First, it follows the GenAI standard specification as closely as possible at the semantics layer. The currently reported trace data preferentially snaps to the OpenTelemetry GenAI semantic conventions. For structures in the Agent runtime that are closer to the execution procedure, extensions are made in combination with LoongSuite Semantic Conventions. Instead of defining a batch of field names that can only be understood internally, we try to use a set of standard, reusable, and portable semantic expressions. In other words, this is not a makeshift approach, but a well-structured observability design that follows industry best practices.

Second, it provides not only traces but also basic metrics signals. In addition to the call chain of a single request, you can also view trends such as the number of invocations, number of faults, invocation duration, and token usage. This way, you can replay a single request along a trace, or observe cost fluctuations, performance changes, and abnormal trends from a global perspective.

Third, it records time to first token (TTFT) separately for streaming scenarios. In many cases, when users perceive something as “slow”, it is not necessarily that the entire generation is slow, but rather that the first token takes too long to return. With TTFT, performance issues can be further broken down from “feels slow” into “slow first token” or “slow overall generation”.

Fourth, it is not attached to a single Alibaba Cloud service on the backend. The current solution can be directly connected to Alibaba Cloud ARMS, but it uses the OTLP standard protocol underneath and is not designed to be locked into a private data structure. Connecting to ARMS works today, and if you need to connect to other OTLP-compatible backends in the future, migration space is preserved.

Fifth, it supports security audits of important behaviors in Hermes. By collecting full operation logs, access records, and user behavioral data from the Hermes system, and combining outlier detection algorithms to build a dynamic audit model, it can accurately detect suspicious behaviors such as unauthorized access, abnormal data exporting, and malicious prompt injection.

What Can Already Be Seen
The observability capability of the current version of Hermes can revert a real agent run into a ReAct structured trace.

The core pipeline is as follows:

invoke_agent Hermes  
└── react step  
    ├── chat   
   └── execute_tool <tool_name>

If a job contains multiple rounds of inference and multiple tool calls, the pipeline naturally expands:

The significance of this pipeline is not that there are more spans, but that the actual execution of Hermes becomes visible for the first time.

How many rounds an execution ran, which round triggered the tool, and how the tool affected subsequent inference — all of this can now be viewed in the same trace.

Call a Model
Each chat span can currently record:

● gen_ai.request.model

● gen_ai.usage.input_tokens

● gen_ai.usage.output_tokens

● gen_ai.usage.total_tokens

● gen_ai.response.time_to_first_token

This means we can finally view tokens and latency per “actual model invocation” instead of only looking at the aggregate of an entire session. Especially in streaming scenarios, TTFT (time to first token,first-token latency) can help us further distinguish whether the first token is slow to return or the overall generation procedure is slow.

Tool Calling
Each execute_tool span can currently record:

● gen_ai.tool.name

● gen_ai.tool.call.arguments

● gen_ai.tool.call.result

Tools are no longer empty edge zones in the procedure. We can see when Hermes decided to invoke a tool, which tool was invoked, what parameters were passed, and what results were returned.

Agent-Level Summary
The root vertex invoke_agent Hermes span can now record the aggregation results of the entire run, including:

● Cumulative Token

● Final output message

● Total time consumption info

Important Behavior Audit
Records agent behavior across the full chain, intelligently generates audit views, and exposes high-risk operations.

Quick Observability Integration: Deployment in a Few Steps
The integration path for Hermes observability is streamlined into a straightforward flow: get the command from the console, copy it to the terminal and execute it, enable the plugin, start Hermes, and begin reporting.

Tracing Integration
Go to the console to obtain the installation command
Log on to the CMS 2.0 (Cloud Monitor Service 2.0) console, go to the corresponding application monitoring workspace, choose Integration Center > AI Application Observability, and click Hermes.

In the sidebar, enter the application name and click Get to immediately generate the integration command. Click the icon in the upper-right corner to copy it with one click.

One-line command to start installation
Open the terminal on the machine where Hermes is located, paste the copied command, and execute it:

curl -fsSL https://arms-apm-cn-hangzhou-pre.oss-cn-hangzhou.aliyuncs.com/hermes-agent-cms-plugin/hermes-cms.sh | bash -s -- install   
  --x-arms-license-key "auto"   
  --x-arms-project "Your project"   
  --x-cms-workspace "Your Workspace"   
  --serviceName "hermes"   
  --endpoint "https://Your ARMS-OTLP address/apm/trace/opentelemetry"

When you execute the installation command for the first time, in addition to installing the plugin itself, the system also registers the hermes-cms command on the local machine for subsequent operations such as enable, disable, and uninstall.

If the following message appears in the terminal, the plugin has been installed successfully:

════════════════════════════════════════════════════

✅ hermes-agent-cms-plugin installed successfully!

════════════════════════════════════════════════════

Throughout the procedure, you do not need to manually edit the configuration file. The script will first match the current environment. Only when the current environment does not meet the requirements will it resume trying the official default installation position.

Turn on observability, and then start Hermes
After the installation is complete, don’t rush to check the console.

The first step is to turn on the observability switch:

hermes-cms enable

Then start Hermes.

To run in the foreground, execute directly:

hermes

Run executable in background:

hermes gateway install

hermes gateway start

How to confirm that instrumentation is actually working
If the following tooltip appears in the terminal after startup, the observability instrumentation has taken effect:

loongsuite-site-bootstrap: started successfully (OpenTelemetry auto-instrumentation initialized).

After confirming that the instrumentation has taken effect, send a few test requests to Hermes to run a real job that triggers multiple rounds of inference and tool calling. After a minute or two, return to the CMS 2.0 console, and you will see your Hermes application in AI Application Observability.

At this point, Hermes is no longer just a black box responder — it becomes a running system that can be expanded, tracked, and analyzed.

Enter our observability application to view not only the number of Hermes model invocations, token consumption trends, request fluctuations, and the average number of LLM invocation rounds per request, but also the latency and invocation distribution across AGENT, LLM, and TOOL phases. You can also trace a complete Trace to revert the actual execution procedure of Hermes, clearly seeing how many rounds of inference a job went through, which tools were invoked, which step took the longest, and which round consumed the most tokens.

View the demo examples and the hermes_agentloop_support example at https://sls.aliyun.com/doc/en/playground/cmsdemo.html

Want to shut down or uninstall? It’s straightforward.
To temporarily shut down observability, execute:

hermes-cms disable

To completely uninstall the plugin, execute:

hermes-cms uninstall

Log Ingestion
Configure application info on the access Card
Next, click the “Log Access” page, set a custom application name, click Initialize Resources, enter the previously configured Project name, and configure the machine group as prompted to complete the Hermes Audit Feature with one click.

Auto-generated Audit dashboard
After the access is complete, in the left sidebar, choose Audit > Hermes Insight > Hermes Audit to view the audit dashboard of your Hermes agent.

Summary and Outlook
This solution can reliably address Tracing Analysis, token attribution, and basic performance breakdown, while also providing basic metrics signals for trend analysis. However, this does not mean that all observability work for Hermes is complete.

Next, we will continue to push forward in several directions.

● On the data plane, continue to expand from traces, span properties, and basic indicators to more complete log audit and runtime diagnostics capabilities.

● On the link plane, continue to refine Hermes-specific execution phases beyond agent, step, llm, and tool, such as memory lifecycle, delegation orchestration, and runtime recovery.

● On the governance plane, continue to strengthen content collection control, finer-grained data governance capabilities, and unified desensitization and security policy development.

Today, we already have an active runtime observability infrastructure, and the next goal is to further evolve it into a more complete, more detailed Agent observability system that is better suited for real production environments.

Stop Teaching Your AI the Same Rules Every Session — mirrorai Mirrors Your Codebase Automatically

You spent six months building a Vue admin panel. You have a wrapped request layer, a shared component library, a consistent page structure. Every file follows the same pattern.

Then you opened Cursor and asked it to add a new list page.

It imported axios directly. It wrote inline fetch logic. It named the component wrong. It ignored your entire architecture.

Sound familiar?

The Real Problem with AI Coding Assistants

The problem isn’t that AI tools are bad — it’s that every session starts from zero. The AI doesn’t know your conventions. It doesn’t know your wrappers. It invents its own style on the spot.

You end up spending more time correcting AI output than you saved generating it.

The fix isn’t to write better prompts. The fix is to give the AI a permanent memory of how your project works.

Introducing mirrorai

mirrorai is a codebase-aware AI rules generator. It analyzes your existing code and produces rule files for the AI tools you actually use:

Output file Tool
CLAUDE.md + .claude/commands/ Claude Code
.cursorrules Cursor
.windsurfrules Windsurf
.github/copilot-instructions.md GitHub Copilot
.clinerules Cline

One command to install. One /mirror-init to run. Your AI assistant finally understands your project.

How It Works

npx mirrorai install

This drops a single mirror-init.md into .claude/commands/. Then you run /mirror-init in your AI tool.

The AI does the real work — analyzing your project, not filling in some generic template:

Your project
   ↓
Detect language, framework, and stack from manifest files
   ↓
Scan business code and cluster files by unit type
   ↓
Score patterns: appears ≥ 3 times, ≥ 50 lines per file, ≥ 80% similarity
   ↓
Generate rule files + slash commands + scaffolding templates

The entire analysis runs inside your AI tool on your existing subscription. No API key. No cloud service. No data leaves your machine.

What Gets Generated

Rule files built from your real code

Not a generic “write clean code” checklist. The generated CLAUDE.md contains:

  • Your actual stack — exact frameworks, libraries, versions
  • Your directory layout and what each folder is for
  • Your core abstractions, and which layers must never be bypassed
  • Your infrastructure wrappers (HTTP client, auth, logging) with real usage examples
  • Your naming conventions, import order, and typing requirements
  • A reference file per pattern: “Before writing a new CRUD endpoint, read internal/handler/user.go“

Auto-execute rules that fire without a prompt

The real magic is the Auto-Execute Rules section in each generated file:

## Auto-Execute Rules

When the current task matches any of the patterns below,
immediately apply the corresponding rule without asking.

### New list page
- Trigger: "add a list page", "create a list view", "build a table page"
- Rule: use src/views/UserList.vue as reference; wrap requests with useRequest()
- Side effects: register the route in src/router/index.ts
- Scaffold first: npx mirrorai new new-list <name>

You describe the requirement in plain English. The AI recognizes the pattern and applies the right conventions — automatically, every single time, without you repeating yourself.

Zero-token scaffolding (opt-in)

If you opt in, mirrorai also generates a plopfile.js and Handlebars templates extracted from your actual code. Scaffold locally without spending a single token, then let the AI fill in only the business logic:

npx mirrorai new new-list LeaveApproval
# → generates the skeleton from your real project pattern
# → AI writes only the business logic on top

Works With Any Stack

mirrorai is fully language-agnostic. It adapts to whatever it finds:

  • Vue / React / Svelte / Angular frontends
  • Go / Rust / Python / Node.js backends
  • FastAPI, Django, NestJS, Spring Boot, Rails
  • Flutter mobile apps
  • CLI tools, data pipelines, GraphQL services

Re-running Is Safe

Run /mirror-init again after a major refactor. It detects existing files and asks what to do:

  • Regenerate everything
  • Refresh only existing files
  • Choose file by file

Files you wrote yourself — detected by the absence of a <!-- mirrorai:generated --> marker — get a per-file prompt: merge, overwrite, or skip. Your content is never silently clobbered.

The Before / After

Before mirrorai:

You: Add a leave-of-absence approval flow.
AI:  *imports axios directly*
     *creates a new custom fetch utility*
     *names the component inconsistently*
     *forgets to register the route*

After mirrorai:

You: Add a leave-of-absence approval flow.
AI:  (recognizes the approval pattern from CLAUDE.md)
     → reads src/views/ApprovalList.vue as reference
     → uses useRequest() for all data fetching
     → names and structures the component correctly
     → registers the route in src/router/index.ts
     → suggests: npx mirrorai new new-approval LeaveApproval

Get Started in 30 Seconds

# Step 1: install the command into your project
npx mirrorai install

# Step 2: run this inside Claude Code, Cursor, Windsurf, or Cline
/mirror-init

Answer two questions — which AI tools to target, and whether to generate scaffolding — and mirrorai does the rest.

GitHub → github.com/joygqz/mirrorai

npm → npmjs.com/package/mirrorai

If this saves you from re-explaining your architecture one more time, a ⭐ on GitHub goes a long way. Issues and PRs are very welcome.

Como Usar Gemini 3.5 Flash Grátis?

Gemini 3.5 Flash

O Gemini 3.5 Flash foi lançado em 19 de maio de 2026, e o Google manteve a camada de acesso gratuito ativa. Hoje, você pode usar o Flash com uma chave de API gratuita; o Pro chegará em junho. Se a meta é testar, criar protótipos ou automatizar chamadas sem pagar, estes são os caminhos que funcionam.

Experimente o Apidog hoje

Este guia mostra como configurar o acesso ao Flash na prática: escolher o caminho certo, obter uma chave gratuita, validar chamadas com código e entender os limites antes de colocar algo em produção.

Gemini 3.5 Flash

Resumo rápido

Caminho O que você obtém Limite
Aplicativo Gemini Chat completo + entrada de imagem no Flash Limite diário de mensagens no nível gratuito
Playground do Google AI Studio Interface web para testar o Flash com controle de parâmetros Sem quota rígida na interface
Chave de API do AI Studio Acesso REST/SDK a gemini-3.5-flash ~1.500 requisições/dia
Créditos de nova conta Vertex AI Acesso Flash de nível de produção $300 de crédito, 90 dias
CLI do Gemini Acesso via terminal ao Flash com login do Google 1.000 requisições diárias

Escolha o caminho com base no seu caso de uso:

  • Quer só conversar? Use o Aplicativo Gemini.
  • Quer ajustar prompts? Use o AI Studio.
  • Quer integrar em código? Use a chave de API.
  • Quer controles de cloud e mais margem de uso? Use Vertex AI.
  • Quer usar no terminal? Use a CLI do Gemini.

Caminho 1: Aplicativo Gemini, sem configuração

Use este caminho se você só precisa interagir com o Gemini 3.5 Flash via interface web.

Passo a passo

  1. Acesse gemini.google.com
  2. Faça login com uma conta Google
  3. No seletor de modelo, escolha 3.5 Flash
  4. Envie seu prompt

Aplicativo Gemini

O aplicativo suporta texto, entrada de imagem, upload de arquivos e edição de documentos no estilo Canvas. Usuários gratuitos recebem um limite diário de mensagens, redefinido diariamente.

Use este caminho para:

Evite este caminho para:

  • Integrações em software
  • Processamento em lote
  • Qualquer fluxo que precise de resposta programável

Caminho 2: Google AI Studio, playground no navegador

O Google AI Studio é o melhor lugar para testar prompts antes de escrever código. Ele permite controlar parâmetros do modelo e exportar chamadas para SDK/API.

Passo a passo

  1. Acesse aistudio.google.com
  2. Faça login
  3. Clique em Criar novo prompt ou escolha um template inicial
  4. No seletor de modelo, escolha gemini-3.5-flash
  5. Escreva o prompt
  6. Clique em Executar

Google AI Studio

No AI Studio, você consegue:

  • Ajustar temperature, top-K e top-P
  • Definir instruções de sistema
  • Testar saída estruturada em JSON
  • Executar prompts multimodais com várias imagens
  • Gerar código equivalente em Python ou Node.js
  • Exportar o prompt para uma chamada de API Flash

O AI Studio não cobra pelo uso via navegador. Use-o para validar o comportamento do prompt antes de consumir sua cota de API.

Caminho 3: Chave de API gratuita do Gemini 3.5 Flash

Este é o caminho mais útil para desenvolvedores. A mesma conta usada no AI Studio pode gerar uma chave de API gratuita compatível com gemini-3.5-flash.

1. Gere a chave

  1. Abra o AI Studio
  2. Clique em Obter chave de API no menu lateral
  3. Escolha um projeto existente ou crie um novo
  4. Clique em Criar chave de API
  5. Copie a chave e salve em um local seguro

Chave de API Gemini

A chave funciona imediatamente com o Flash. Não é necessário cartão de crédito nem verificação adicional. Para um passo a passo mais detalhado, veja o guia de chave de API Gemini gratuita.

2. Entenda as cotas gratuitas

Em maio de 2026, o nível gratuito para gemini-3.5-flash oferece aproximadamente:

  • 1.500 requisições por dia
  • 1M tokens por minuto
  • 15 requisições por minuto

Esses valores podem mudar. Antes de criar lógica de produção em cima desses limites, confira a página de preços do Google. Para estimar custos após ultrapassar o gratuito, veja o detalhamento de preços do Flash.

3. Configure a variável de ambiente

Use uma variável de ambiente para evitar expor a chave no código.

export GEMINI_API_KEY="sua-chave-aqui"

Em projetos reais, use .env, secrets do CI/CD ou o gerenciador de segredos da sua cloud.

4. Teste com Python

import os
from google import genai

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

response = client.models.generate_content(
    model="gemini-3.5-flash",
    contents="Give me three startup ideas for API tooling in 2026."
)

print(response.text)

5. Teste com Node.js

import { GoogleGenAI } from "@google/genai";

const ai = new GoogleGenAI({
  apiKey: process.env.GEMINI_API_KEY,
});

const response = await ai.models.generateContent({
  model: "gemini-3.5-flash",
  contents: "Give me three startup ideas for API tooling in 2026.",
});

console.log(response.text);

6. Teste com curl

curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent" 
  -H "x-goog-api-key: $GEMINI_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{
    "contents": [
      {
        "parts": [
          {
            "text": "Hello Gemini 3.5 Flash"
          }
        ]
      }
    ]
  }'

Para uma configuração mais completa com streaming e chamadas de ferramenta, consulte o guia da API Gemini 3.5 Flash.

Teste requisições Flash gratuitas no Apidog

Se você tem 1.500 requisições por dia, cada chamada precisa ser útil. O Apidog ajuda a salvar o endpoint do Flash, guardar sua chave como variável de ambiente e repetir testes sem reescrever o curl.

Testando Gemini no Apidog

Fluxo recomendado

  1. Baixe o Apidog
  2. Crie uma nova requisição
  3. Cole o curl da API Gemini
  4. Mova a chave para uma variável de ambiente
  5. Salve a requisição
  6. Adicione asserções para validar a resposta
  7. Rode o teste sempre que alterar o prompt

Exemplo de asserções úteis:

  • Status HTTP é 200
  • A resposta contém candidates
  • O texto gerado não está vazio
  • O tempo de resposta está abaixo de um limite aceitável

Isso evita gastar cota depurando chamadas malformadas repetidamente. O histórico de respostas também ajuda a comparar versões de prompt.

Caminho 4: Créditos de nova conta do Vertex AI

Se você criar uma conta Google Cloud nova, pode receber $300 em créditos válidos por 90 dias. Esses créditos podem ser usados com o Gemini 3.5 Flash no Vertex AI, que oferece o mesmo modelo com controles de nível organizacional.

Passo a passo

  1. Cadastre-se em cloud.google.com
  2. Confirme o crédito de $300
  3. Ative a API do Vertex AI
  4. Crie uma conta de serviço
  5. Conceda a permissão aiplatform.user
  6. Baixe o arquivo JSON de credenciais
  7. Chame o Flash via SDK do Vertex AI

Exemplo em Python

import vertexai
from vertexai.generative_models import GenerativeModel

vertexai.init(
    project="seu-id-de-projeto",
    location="us-central1"
)

model = GenerativeModel("gemini-3.5-flash")

response = model.generate_content(
    "Explique o teorema CAP."
)

print(response.text)

O Vertex AI não usa o mesmo limite diário da chave gratuita do AI Studio. O limite prático passa a ser o crédito disponível. Com o Flash custando cerca de $1.50/$9 por 1M de tokens, $300 podem durar bastante em uso moderado.

Antes de usar, observe:

  • O crédito expira em 90 dias
  • Crédito não utilizado é perdido
  • A conversão para pago pode ocorrer no vencimento, então desative se não quiser cobranças
  • O Vertex exige mais configuração do que o AI Studio

Use Vertex AI se você precisa de uma configuração mais próxima de produção, com IAM, contas de serviço e controles de cloud.

Caminho 5: CLI do Gemini

A CLI do Gemini é o cliente de terminal de código aberto do Google. Ela autentica com sua conta Google e oferece uma cota diária de 1.000 requisições no nível gratuito, sem precisar de chave de API.

Instalação

npm install -g @google/gemini-cli

Execução

gemini

Dentro da CLI, escolha gemini-3.5-flash.

Use a CLI para:

  • Scripts rápidos no terminal
  • Resumos de arquivos e diretórios
  • Exploração de codebase
  • Tarefas ad-hoc que você normalmente faria em uma interface de chat

Ela também pode funcionar como uma alternativa gratuita ao Claude Code, já que consegue ler arquivos e executar ferramentas.

Evite a CLI para:

  • Integrações de produção
  • Cargas que precisam de controle HTTP bruto
  • Pipelines que exigem observabilidade detalhada de API

Caminho 6: OpenRouter e gateways de terceiros

Alguns gateways agregam acesso a modelos de IA e roteiam chamadas para o Flash de forma gratuita ou quase gratuita. Esse padrão é abordado no post Obtenha API Gemini gratuita e ilimitada.

Use como fallback, não como base principal.

Pontos de atenção:

  • A qualidade e a estabilidade variam
  • Alguns gateways aplicam limites agressivos
  • Seus prompts passam por terceiros
  • Não envie dados sensíveis
  • Alguns usam métodos de pagamento ou acesso alternativos

Para builds reais, prefira AI Studio ou Vertex AI.

Qual caminho gratuito escolher?

Use esta árvore de decisão:

  • Apenas quer conversar? Aplicativo Gemini
  • Quer ajustar prompts? Playground do AI Studio
  • Está construindo software? Chave de API do AI Studio
  • Precisa de mais margem e tem nova conta GCP? Créditos do Vertex AI
  • Quer rodar pelo terminal? CLI do Gemini
  • Quer testar gateways externos? OpenRouter e similares

Na prática, muitos desenvolvedores combinam dois caminhos:

  • AI Studio para protótipo e chave de API
  • Vertex AI para testes com maior volume ou ambiente mais próximo de produção

Gratuito vs. pago: quando fazer upgrade

O nível gratuito é suficiente para muitos projetos paralelos. Considere migrar para pago quando algum destes sinais aparecer:

  1. Você atinge 1.500 requisições/dia por vários dias seguidos.

    Nesse ponto, gastar tempo contornando cotas pode custar mais do que pagar pelo uso.

  2. Você precisa de mais throughput por minuto.

    O nível gratuito limita a cerca de 15 RPM. Níveis pagos oferecem limites maiores.

  3. Você precisa de residência de dados ou logs de auditoria.

    Nesse caso, use Vertex AI em uma conta faturada.

Para estimar custos, consulte o guia de preços do Gemini 3.5 Flash.

Também vale comparar com modelos vizinhos. O GPT-5.5 e o Claude Opus 4.7 têm caminhos gratuitos com limites próprios. Em alguns casos, a estratégia mais barata é misturar provedores por tipo de workload. A comparação entre os três detalha onde cada um se destaca.

Dicas para estender o nível gratuito

Cinco práticas simples ajudam a fazer 1.500 requisições/dia renderem mais.

1. Cacheie respostas idempotentes

Se a mesma entrada sempre gera o mesmo tipo de resposta, grave o resultado.

Exemplo simples em Python:

cache = {}

def cached_generate(prompt):
    if prompt in cache:
        return cache[prompt]

    response = client.models.generate_content(
        model="gemini-3.5-flash",
        contents=prompt
    )

    cache[prompt] = response.text
    return response.text

Em produção, substitua o dicionário por Redis, banco de dados ou cache da sua plataforma.

2. Use modo em lote para trabalho massivo

Para tarefas não interativas, o modo em lote da API Gemini oferece desconto de 50% e usa uma cota separada.

Use para:

  • Classificação de muitos documentos
  • Geração de resumos em massa
  • Enriquecimento de dados offline
  • Processamento sem resposta em tempo real

3. Peça saída estruturada

Prefira JSON quando a resposta será processada por código.

Exemplo de prompt:

Responda apenas em JSON válido com este formato:

{
  "summary": "string",
  "tags": ["string"],
  "risk_level": "low | medium | high"
}

Texto:
...

Isso reduz chamadas extras para corrigir ou interpretar texto livre.

4. Pré-valide entradas

Antes de chamar o modelo, valide:

  • Campos obrigatórios
  • Tamanho máximo da entrada
  • Formato de URL
  • Tipo de arquivo
  • Idioma esperado
  • Payload JSON válido

As asserções do Apidog ajudam a capturar requisições malformadas antes que elas consumam cota.

5. Use Flash por padrão

Quando o Pro estiver disponível, ele provavelmente terá cotas mais apertadas. Use o Flash para tarefas rotineiras e reserve modelos maiores para casos que realmente precisam de mais capacidade.

Projeto inicial: API para resumir uma URL

Um bom teste ponta a ponta é criar uma pequena função que:

  1. Recebe uma URL
  2. Baixa o HTML
  3. Envia parte do conteúdo para o Flash
  4. Retorna um resumo em tópicos

Exemplo em Python

import os
import requests
from google import genai

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

def summarize_url(url):
    html = requests.get(url, timeout=10).text

    response = client.models.generate_content(
        model="gemini-3.5-flash",
        contents=f"Summarize this webpage in 3 bullets:nn{html[:50000]}"
    )

    return response.text

print(summarize_url("https://blog.google/"))

Próximos passos

  • Transforme a função em um endpoint HTTP
  • Valide a URL antes de buscar o conteúdo
  • Adicione timeout e tratamento de erro
  • Salve respostas em cache
  • Teste a requisição no Apidog
  • Implante no Cloud Run ou na sua hospedagem preferida

Se você conseguir implementar esse fluxo, já terá entendido o caminho gratuito do Flash do prompt até uma API funcional.

FAQ

O Gemini 3.5 Flash é realmente gratuito?

Sim, com limites. O aplicativo Gemini, o AI Studio e a chave de API do AI Studio oferecem acesso gratuito ao Flash com cotas diárias. Níveis pagos existem para maior throughput.

Preciso de cartão de crédito para obter uma chave Flash gratuita?

Não. O nível gratuito do AI Studio não exige cartão. O Vertex AI exige cartão para ativar o crédito de $300, mas você não será cobrado até fazer upgrade.

Posso usar a chave Flash gratuita em produção?

Tecnicamente, sim. Mas o limite de 1.500 requisições/dia pode ser atingido rapidamente. Para produção, use uma conta faturada ou Vertex AI.

O Gemini 3.5 Pro também será gratuito?

Historicamente, o Google estendeu acesso gratuito a modelos Pro na disponibilidade geral. Espere o Pro no AI Studio gratuitamente com cotas mais apertadas no próximo mês.

Posso usar o Flash gratuito no Cursor ou VS Code?

Sim. Ambos suportam chaves de API personalizadas. O padrão é semelhante ao tutorial Gemini 3.0 Pro com Cursor: troque a string do modelo para gemini-3.5-flash.

Existem logs de uso no nível gratuito?

Sim. Abra o AI Studio, clique no projeto e visualize a guia Atividade.

Qual é a pegadinha?

Duas principais:

  1. O nível gratuito pode usar seus prompts para melhorar os modelos do Google. Verifique as opções de opt-out nas configurações do AI Studio.
  2. Os limites de taxa do nível gratuito podem mudar sem aviso. Não baseie um lançamento crítico nesses limites.

Is Writing a tech blog still worth it ???

Hi DEV Community! 👋

I’m an undergraduate who is currently stepping into the tech industry. Writing has always been one of my favorite hobbies, and for a long time, I’ve wanted to start and maintain a tutoring blog focused on breaking down the specific concepts that were incredibly hard for me to learn.

However, with the massive AI boom, the internet is now flooded with generated articles on almost every topic imaginable. Even if a lot of it has a slightly robotic, “mech” vibe, there’s no denying that this content is actually useful and fast to access. On top of that, reading habits seem to have shifted, and traditional blog reading feels less common now in 2026.

Given how much the landscape has changed, I’m stuck with a question for the community: Is it still worth it to start and maintain a tech blog today?

I’d love to hear your thoughts, especially from anyone still writing or running niche educational blogs!

My Take: Is it worth it?
As a helpful peer looking at the landscape with you, absolutely, it is still worth it. Here is why your specific idea still has a competitive edge over a raw AI prompt:

The “Empathy of the Learner”: AI can explain a concept, but it doesn’t know what it feels like to be confused by it. When you write about things that were hard for you, you capture the exact pain points, misconceptions, and “aha!” moments that generic documentation and AI tools often gloss over.

Proof of Competence: For an undergraduate stepping into the industry, a blog is a dynamic portfolio. When a hiring manager reads your explanation of a complex topic, they aren’t just seeing code—they are seeing your communication skills, your deep understanding, and your passion for teaching.

The Combat Against “AI Fatigue”: People are starting to crave a human voice. The “mechanical vibe” you mentioned is real, and readers appreciate when an article feels like a conversation with a real peer who actually built something and got their hands dirty.

Instead of fighting the shift in how people consume content, you can adapt your approach:

I’m going to start my own blog and my idea is to keep it simple and clean. Break down complex topics into highly visual, bite-sized tutorials.

I’m going to share the struggles: I will not just show the working code; show the error messages that broke my brain for few hours before I fixed them. That’s the human element AI can’t fake.

What are your thoughts on this ?

Git Basics

Formal Definition

Git is a free, open-source distributed version control system (VCS) designed to track changes in source code during software development.

In English

  • A tool that can save the state of our project,from adding a semicolon to a file, to writing hundreds of lines of code.
  • Whenever we think this should be saved, we can!
  • And whenever we need to see that version, we can!
  • Whenever we need to use that version or do changes to it and save again, we can!
  • We can save n number of versions of our project and each version can be just as simple as a space char inclusion in a file!
  • All of this without taking lot of space in hard disk

Getting and Setting-Up Git

Check the official website: https://git-scm.com/

How It Works

  1. First you initialize a directory as git repo (repository):
    • You do git init
    • Git creates a .git hidden folder in current directory
    • This .git folder is what makes your folder a repo and gives those magical powers to see the history and versions of your project.
    • If, by any chance, you delete that .git folder, you will loose all the project history, unless you pushed your changes to github!
  2. You ask git, what is the status of the project:
    • You do git status
    • Git says on branch main, and then shows some filenames in red and says “Untracked files” (if you have files in your repo)
    • That simply means git is working and its asking to save the changes if you are done working on them
    • The branch thing is explained in the Core Concepts section
  3. You ask git to take a screenshot of the changed files:
    • You do git add filename.something or git add .
    • Git takes a screenshot of the specified files
    • Keep in mind,the screenshot is not saved yet
    • Its like print screen on pc, we get a window that asks to save it or discard!
  4. You ask git to save the screenshot:
    • You do git commit -m "Some message about the changes!"
    • Git saves the screenshot into the project history
    • Each screenshot saved is called a commit
    • Each commit has a hash
    • That hash is like a name, that we can refer, to ask git to get that version or commit to us
  5. You can view all the commits or versions:
    • You do git log
    • Git will show all the commits list with details like: Who,when, commit hash, commit message, etc!

This is the basic working!

Core Concepts

User

  • Each commit will include information about the person who made the commit
  • That is the username and email address of the person
  • This helps others or yourself in future, that who made the commit
  • The User is simply a person with username and email set in the git configuration
  • This is done by setting the username and password in git like this:
git config --global user.name "Your Name"
git config --global user.email "[email protected]"

Branches

  • All your commits from first one to the last one are like a rope
  • You can see the timeline of the commits straight
  • This timeline rope is a branch, just like the branch of a tree
  • It starts at the root, first commit, then grows!
  • By default, the root or main branch is called main
  • Now, if you have an experimental feature that you don’t want to save to the current timeline or branch, but still want to save its changes and later when it works fine, want to join it to the current timeline of commits or branch
  • For this we have branches and merging
  • You can create a branch, do changes to the repo and save them into its own rope of commits
  • Later when you want to, you can add it to the main branch, that is to merge the branch with the main branch
  • You can make as many branches as you want
  • But merging branches with lot of changes and commits is an advanced topic!

GitHub (Pushing/Pulling)

  • Although git is helping save our projects’s history, the history of our project is still living on our hard disk
  • If we loose it then bang!
  • And if someone else wants to collaborate with us on the project,
    they don’t have access to our repo on our pc
  • To solve these issues, we have GitHub, a repo hosting platform, where we can save our repo history
  • When you make new commits you send them to github that is to push changes up!
  • Anyone having access to your github repo can send their changes (commits) to the repo on github and we can get or pull those changes back to our local pc repo

The Usual Flow of Commands

1) Initialize a git repo

git init

2) Check status

git status

3) Add changes to the staging area (just a fancy term, you know what it does)

git add .

or

git add specific_file.something specific_file2.something ...

4) Save changes

git commit -m "My first commit with changes"

5) Checking and pulling new changes from GitHub

git pull

6) Pushing changes to GitHub

git push origin main

or

git push

7) See the history

git log

Caution For Beginners

  • While using the command: git add ., git tracks everything in you repo folder including any sensitive information
  • And when you push it to GitHub, if its a public repo, then the whole world can get access to that information
  • So, do not put any sensitive information like passwords, tokens, api keys etc in your repo!
  • And mostly use git add with explicit filenames rather than “.”

Note: I skipped the topics like: how to setup github account and make repos and add local repo’s pushing destination to github repo, etc
Those, I think, you can learn on your own, once you get the basics!
(AI is also there for your help!)

Where to go from here?

I found this video very helpful while learning:
https://www.youtube.com/watch?v=apGV9Kg7ics
And the official git book:
https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control

remove-ai-watermarks: una CLI borra SynthID, C2PA y el sparkle de Gemini

Un repositorio publicado en GitHub bajo el nombre remove-ai-watermarks ofrece una CLI y librería en Python que elimina, en un solo comando, las marcas de agua visibles e invisibles que las grandes plataformas de IA generativa estampan sobre las imágenes que producen. Cubre el sparkle visible de Google Gemini (Nano Banana), SynthID en sus dos versiones, C2PA Content Credentials, EXIF, XMP y los PNG text chunks que disparan los sellos Made with AI en Instagram, Facebook y X.

El proyecto, mantenido por el usuario wiltodelta, acumula 221 estrellas y 20 forks al momento de redactar este artículo y se distribuye con un demo online gratuito en raiw.cc. La discusión es relevante porque pone sobre la mesa, con código funcional, hasta qué punto la trazabilidad criptográfica y perceptual que defienden Google, Adobe, Meta y OpenAI resiste un ataque técnico bien informado.

TL;DR

  • remove-ai-watermarks es una CLI en Python que elimina marcas visibles e invisibles de imágenes generadas por Gemini, DALL-E, SD, Firefly y Midjourney.- Borra el sparkle de Nano Banana en ~0.05s por imagen sin GPU, usando reverse alpha blending con un mapa alfa conocido.- Para SynthID y otras marcas invisibles usa regeneración por difusión con SDXL a 1024 px, ~50 pasos a strength 0.05.- Strippea metadatos C2PA, EXIF, XMP y PNG text chunks que disparan el sello Made with AI en Instagram, Facebook y X.- Incluye Smart Face Protection con YOLO y un Analog Humanizer con grano de película y aberración cromática para esquivar clasificadores.- El repo wiltodelta/remove-ai-watermarks tiene 221 estrellas y un servicio web gratuito en raiw.cc que usa la misma librería.- Migró de SD-1.5 a SDXL en mayo de 2026 tras verificar empíricamente que SynthID v2 de Gemini 3 Pro resistía el pipeline anterior.

Qué hace exactamente la herramienta

La promesa de remove-ai-watermarks es directa: pasarle una imagen generada por un modelo de IA y obtener una versión que ningún sistema actual de detección automatizada pueda identificar como sintética. Eso implica atacar tres capas distintas al mismo tiempo.

La primera capa es la marca de agua visible. Hoy solo Google la aplica de forma sistemática: el sparkle multicolor que Nano Banana y Gemini 3 Pro estampan en una esquina de cada imagen generada. La segunda es la invisible en el dominio del píxel o de la frecuencia: SynthID de DeepMind, StableSignature de Meta, TreeRing en el espacio latente de los modelos de difusión. La tercera es la metadata: campos EXIF, XMP, fragmentos de texto en PNG y, sobre todo, el manifiesto criptográfico C2PA que cargan ChatGPT Images, Adobe Firefly y los modelos compatibles con Content Credentials.

La CLI las desactiva con un único comando, encadenando tres pipelines independientes que se pueden activar o desactivar por separado. El proyecto declara compatibilidad explícita con Gemini, DALL-E 3, ChatGPT Images 2.0, Stable Diffusion (AUTOMATIC1111 y ComfyUI), Adobe Firefly, Midjourney, StableSignature y TreeRing.
Las tres capas que la herramienta ataca en paralelo sobre cada imagen.

Cómo derrota el sparkle de Gemini sin GPU

El sparkle de Gemini se aplica con una operación trivial: alpha blending. La imagen final que ves es una combinación lineal entre el logo y la imagen original, ponderada por un canal alfa que decide cuánta opacidad tiene la marca en cada píxel.

watermarked = α × logo + (1 − α) × original

Si conocés el mapa alfa exacto y la apariencia del logo, la operación se invierte en una sola línea de álgebra. remove-ai-watermarks incluye un mapa alfa que el autor extrajo generando una imagen de Nano Banana sobre fondo negro puro, lo que aísla matemáticamente la contribución del logo.

original = (watermarked − α × logo) / (1 − α)

El detalle no trivial es la ubicación. El sparkle puede aparecer escalado o desplazado si la imagen pasó por un crop o un resize. Para eso, el repo implementa un detector de tres etapas basado en Normalized Cross-Correlation que busca el logo en distintas posiciones y escalas, devuelve una puntuación de confianza y solo aplica la inversión si supera un umbral. Tras el reverse blend, un paso de gradient-masked inpainting limpia los bordes residuales que el algoritmo deja en la frontera del logo.

El resultado declarado es de 0.05 segundos por imagen sin GPU. Es la parte más rápida y determinista del sistema, y la única que no introduce ruido perceptual en el resto de la imagen.

💭 Clave: el sparkle solo es invertible porque Google decidió usar una operación cerrada con un logo público. Si en lugar de un blend lineal hubieran aplicado un inpainting generativo con perturbaciones aleatorias por imagen, la herramienta no podría revertirlo. Es una decisión de diseño que prioriza la velocidad de cómputo en el lado de Google sobre la robustez de la marca.

El ataque a SynthID y las marcas invisibles

Las marcas invisibles son una historia distinta. SynthID, presentado públicamente por Google DeepMind en 2023 y desplegado por defecto en todas las imágenes de Gemini, no es un overlay: es un patrón disperso en el dominio de la frecuencia que resiste compresión JPEG, cropping y resizing. La promesa formal es que un detector autorizado por Google puede leerlo aun después de transformaciones agresivas.

El ataque del repo es indirecto pero efectivo: si no se puede borrar el patrón a mano, se regenera la imagen entera con un modelo de difusión que aplica suficiente ruido para destruir la firma de frecuencia sin alterar visiblemente el contenido. El pipeline por defecto, vigente desde mayo de 2026, usa Stable Diffusion XL:

image → resize a ~1024px (resolución nativa de SDXL)
      → encode a espacio latente con el VAE
      → ruido controlado (forward diffusion)
      → denoise (~50 pasos, strength 0.05)
      → decode al dominio de píxeles con el VAE
      → upscale a la resolución original

Los números importan: con un strength de 0.05 el modelo perturba lo justo para reescribir cada píxel en el espacio latente, pero no lo suficiente para distorsionar el contenido reconocible. El autor documenta que el pipeline anterior basado en SD-1.5 a 768 px no derrotaba a SynthID v2 en los outputs de Gemini 3 Pro, y que la migración a SDXL fue empírica: probó hasta que el detector de referencia dejó de marcar las imágenes regeneradas.

Smart Face Protection con YOLO

Cualquier paso de difusión, por suave que sea, tiende a deformar caras humanas. Los modelos generativos tienen un prior fuerte sobre rostros y arrastran la geometría facial hacia la media estadística de su set de entrenamiento. La herramienta resuelve eso con un paso previo: corre YOLO para detectar personas en la imagen, extrae las regiones faciales antes de la difusión y, después de regenerar el resto, blendea de vuelta los rostros originales con una máscara elíptica suave.

Analog Humanizer

Una vez removida la marca, queda el problema de los clasificadores que aprenden a distinguir imágenes IA por características estadísticas globales, no por una marca explícita. El módulo opcional Analog Humanizer inyecta grano de película y aberración cromática calibrados para que el resultado sea estadísticamente indistinguible de una foto tomada de la pantalla de un monitor. Es el equivalente, en términos digitales, a fotocopiar y escanear un documento para borrar metadatos.
El Analog Humanizer añade ruido analógico calibrado para esquivar clasificadores.

Cómo se eliminan los metadatos y el sello Made with AI

La parte menos glamorosa, pero probablemente la más importante para el uso cotidiano, es el stripping de metadatos. Instagram, Facebook y X no detectan IA mirando los píxeles: leen el manifiesto C2PA embebido en el archivo, el campo XMP DigitalSourceType o un puñado de etiquetas EXIF, y si alguno indica generación por IA, agregan el sello Made with AI. C2PA, una iniciativa empujada por Adobe, Microsoft y The New York Times, firma criptográficamente el manifiesto, lo que garantiza autenticidad pero no impide que el manifiesto se borre por completo.

El repo limpia los siguientes contenedores:

  • PNG text chunks, donde Stable Diffusion guarda prompt, modelo, sampler y seed.- EXIF y XMP, usados por Midjourney y Firefly para metadata de generación.- Manifiestos C2PA en PNG, JPEG, AVIF, HEIF y JPEG-XL, que son las cinco contenedores donde la coalición decidió incrustar el bloque firmado.

Sin manifiesto y sin XMP, las plataformas pierden la señal explícita que dispara el sello. Las marcas perceptuales (SynthID, StableSignature) ya fueron eliminadas en el paso anterior por la regeneración por difusión.

flowchart LR
  A["Imagen IA con marcas"] --> B["Detector NCC del sparkle"]
  B -->|si lo encuentra| C["Alpha reversal + inpainting"]
  B -->|si no| D["Saltea visible"]
  C --> E["Difusion SDXL 1024px"]
  D --> E
  E --> F["YOLO: face protection"]
  F --> G["Strip C2PA, EXIF, XMP"]
  G --> H["Analog Humanizer opcional"]
  H --> I["Imagen limpia"]

Instalación y uso desde la CLI

El proyecto usa uv como gestor de dependencias de Python, lo que simplifica la instalación y reduce los problemas habituales de versiones en torch y diffusers. Los comandos son idénticos en las tres plataformas si tenés uv instalado:

# Windows (PowerShell), macOS y Linux
git clone https://github.com/wiltodelta/remove-ai-watermarks
cd remove-ai-watermarks
uv sync

# Limpieza basica de una imagen
uv run remove-ai-watermarks clean entrada.png salida.png

# Procesamiento por lotes con perfil SDXL completo
uv run remove-ai-watermarks batch ./carpeta_entrada ./carpeta_salida --profile sdxl

# Solo strip de metadatos (sin difusion, instantaneo)
uv run remove-ai-watermarks strip imagen.jpg salida.jpg --metadata-only

El flag --metadata-only es el modo más liviano y honesto si tu objetivo es solo evitar los sellos automáticos en redes: deja la imagen pixel-idéntica al original y descarta los manifiestos C2PA. El modo completo, con difusión, está pensado para casos donde se quiere derrotar también detectores estadísticos.

⚠️ Ojo: el proyecto no se posiciona como herramienta para creación de desinformación. La discusión técnica es legítima, pero el uso responsable —usos académicos, investigación de robustez, recuperación de imágenes propias con metadata corrupta— es muy distinto del uso para fabricar evidencia falsa o desinformación. El autor incluye la licencia y no asume responsabilidad legal por el uso.

Por qué importa: la trazabilidad como infraestructura frágil

La existencia pública y funcional de remove-ai-watermarks reabre una discusión que la industria intentó cerrar a fuerza de comunicados: ¿es la marca de agua una solución técnica viable para la trazabilidad de contenido generado por IA?

La Casa Blanca firmó en 2023 compromisos voluntarios con OpenAI, Google, Meta, Microsoft, Anthropic y otros para implementar marcas de agua en contenido generado. La Unión Europea, en el AI Act de 2024, exige etiquetado de contenido sintético. Adobe lanzó Content Credentials como infraestructura abierta para resolver el problema con firma criptográfica. C2PA es ahora la coalición de referencia, y publicaciones como The New York Times o la BBC ya integran lectura de manifiestos en sus pipelines editoriales.

La hipótesis subyacente es que una imagen generada por IA va a llegar a los lectores con un sello legible. La herramienta demuestra, con código auditable, que tres ataques bien conocidos en seguridad y procesamiento de imágenes —reverse alpha blending, regeneración por difusión y stripping de metadatos— bastan para neutralizar las tres capas que componen la marca. Y lo hace en segundos, sin GPU para el caso visible, sin entrenar modelos nuevos, sobre commodity hardware.

La asimetría es estructural: defender requiere que la marca sobreviva a cualquier transformación, mientras que atacar solo requiere una transformación que la rompa. Los autores académicos de SynthID lo reconocen en sus papers: la marca está diseñada para resistir ediciones casuales, no ataques adversariales informados con acceso al código.

Implicaciones para periodismo, redes y verificación

El sello Made with AI de Meta ya cubre, según la propia empresa, decenas de millones de imágenes mensuales. Para una redacción periodística que verifica contenido viral, ese sello es una señal débil pero útil. Si el atacante motivado puede borrarla con una línea de comando, la verificación tiene que apoyarse en otras señales:

  • Análisis forense estadístico, no basado en marcas embebidas sino en correlaciones de ruido, frecuencias o artefactos de modelos específicos.- Procedencia firmada de origen, no solo de generación: certificar al fotógrafo, dispositivo y momento de captura, como propone el estándar C2PA Capture.- Análisis contextual y de fuentes: la verificación tradicional, que en última instancia no depende de píxeles.

Para las grandes plataformas, la lección es que la única defensa robusta posible es marcar lo auténtico en origen, no marcar lo sintético en destino. Es la misma discusión que en seguridad llevó del blacklisting al allowlisting: enumerar lo malo no escala, enumerar lo bueno sí.

Qué sigue

El propio CHANGELOG del repo es transparente sobre la dinámica de gato y ratón: el pipeline SD-1.5 funcionó contra SynthID v1 pero fue obsoleto por v2, y por eso migraron a SDXL. Es razonable esperar una SynthID v3 diseñada para sobrevivir regeneraciones por difusión, probablemente embebida más profundamente en el modelo generativo (no como post-proceso) y verificada por un detector que sea robusto a downscaling y reencoding.

Paralelamente, las plataformas pueden pasar de leer un manifiesto a requerir un manifiesto firmado válido para no aplicar restricciones. El cambio de criterio mueve la carga: en vez de marcar lo IA, dejarían sin contexto lo no firmado, lo que penaliza también a quien no usa IA pero no tiene Content Credentials configurado. Es una decisión política, no técnica.

Por ahora, lo concreto es que la herramienta existe, es pública, funciona, y se mantiene. Y eso obliga a periodistas, plataformas y arquitectos de sistemas de verificación a aceptar que la marca de agua, en su forma actual, es una capa entre muchas, no la solución del problema.

Preguntas frecuentes

¿remove-ai-watermarks es legal?

El proyecto está bajo una licencia open source explícita y procesa archivos locales. La legalidad de eliminar metadatos o marcas de tus propias imágenes depende de la jurisdicción y del propósito: la mayoría de marcos legales no prohíben modificar archivos personales, pero sí prohíben fabricar evidencia falsa, suplantar identidad o violar términos de servicio de plataformas. El uso responsable —investigación, recuperación de metadata corrupta, pruebas de robustez— es muy distinto del uso para desinformación.

¿Funciona contra todas las marcas de agua actuales?

El propio CHANGELOG documenta que el pipeline anterior (SD-1.5 a 768 px) no derrotaba SynthID v2 de Gemini 3 Pro. La migración a SDXL a 1024 px lo resuelve en la fecha actual, pero los esquemas evolucionan. Para ChatGPT Images 2.0 el repo admite que existe una marca imperceptible cuyo detector no es público todavía, por lo que el resultado contra ese modelo no es verificable.

¿Por qué SDXL y no un modelo más nuevo?

SDXL tiene VAE y arquitectura abiertos, corre razonablemente en GPU de consumo y su resolución nativa de 1024 px se alinea con la resolución típica de las imágenes generadas por Gemini. Modelos más nuevos como Flux o SD3 son alternativas viables, pero introducen sus propios artefactos y requieren recalibrar el strength óptimo para no destruir contenido.

¿Qué hace exactamente el Analog Humanizer?

Aplica grano de película sintético con distribución calibrada de luminancia y un patrón de aberración cromática suave en los bordes de alto contraste. La idea es que clasificadores entrenados para distinguir IA por características estadísticas globales —patrones de ruido demasiado limpios, ausencia de aberraciones ópticas— vean una imagen con la firma visual de un sensor analógico o de una pantalla fotografiada.

¿Cuál es el costo computacional real?

El stripping de metadatos y el reverse alpha blending son operaciones triviales: corren en CPU en milisegundos. El pipeline de difusión con SDXL a 1024 px y 50 pasos requiere GPU para tiempos razonables: alrededor de 10-15 segundos por imagen en una RTX 4090, varios minutos en CPU. El procesamiento por lotes en una carpeta es directo y soporta resume si se interrumpe.

¿Cómo verifico que la imagen ya no tiene SynthID después de procesarla?

El detector oficial de SynthID no es público. La verificación práctica en el repo se hace con detectores de terceros y con re-ejecución de los analizadores estadísticos que las plataformas usan internamente, lo que da una señal indirecta pero no una garantía formal. Para auditorías rigurosas hay que confiar en que el pipeline de difusión destruyó la información de frecuencia, sin un oráculo externo que lo confirme.

📖 Resumen en Telegram: Ver resumen

Referencias

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.

“How I discovered a hidden 146W power draw on NVIDIA A100 GPUs (and built an open‑source fix)”

How I discovered a hidden 146W power draw on NVIDIA A100 GPUs (and built an open‑source fix)

TL;DR: nvidia-smi reported 0% utilization, but the GPU was drawing 146W. Standard telemetry lies. I built an open‑source detector and a new efficiency benchmark (CEI).

The moment I knew something was wrong

I was running a matrix multiplication benchmark on an NVIDIA A100 SXM (RunPod, my own money). After the kernel finished, nvidia-smi said:

  • GPU utilization: 0%
  • Power draw: 146.66 W

Not a spike. It stayed there for 11+ minutes. The GPU was locked in P0 state, memory clock stuck at 1593 MHz, burning electricity while reporting “idle”.

I tested sampling rates of 1 second, 100 milliseconds, and even 10 ms – the blind spot persisted.

This is a GHOST anomaly: physically impossible telemetry that leads to over‑provisioned clusters, wasted energy, and wrong scaling decisions.

What I did about it

I ran 35 hardware tests (24 A100, 11 H100) and validated:

  • A100 idle floor is ~67 W, but ghost power can reach 146 W at 0% utilization.
  • H100 shows no ghost power – the issue is A100‑specific (likely fixed in Hopper).
  • NVIDIA’s own MIG documentation admits: “Profiling of shared GPU resources is not supported.” My tool fills that gap.

I defined Compute Energy Intensity (CEI) = FLOPs / joule.

Reference: A100 sustained FP32 → 5.68 B FLOPs/J (Test 24, 900 s).

Then I built the AI GPU Energy Optimizer – an open‑source platform that:

  • Detects DESYNC/GHOST anomalies in real time.
  • Provides CEI benchmarking across 17+ cloud providers (AWS, GCP, Azure, RunPod, etc.).
  • Integrates with Kubernetes / Run:ai for auto‑eviction.
  • Deploys with a single docker-compose up.

✅ All 40 platform tests pass. Live API: ai-gpu-brain-v3.onrender.com/docs

Why this matters

Cloud providers and AI teams are paying for electricity they can’t see. At 500 GPUs, ghost waste can exceed $150/day in hidden energy + cooling.

The tool is open source, but I need sponsored compute (100‑500 GPUs on MIG partitions) to scale validation and prove the ROI. I’m an independent researcher in BC, Canada – all tests so far were at my own expense.

If you run GPU fleets or work at a cloud provider, let’s talk.

Resources

Tags: gpu ai opensource observability energyefficiency

– Mike Bains ([email protected])

How to use feature flags in JavaScript and TypeScript

A practical guide to implementing feature flags in JS/TS: what they are, when to use them, and how to avoid the traps that make them painful to manage.

Feature flags are a conditional: if (flagEnabled) { show new thing } else { show old thing }. That’s the entire concept. The value comes from where the condition is evaluated and who controls it — not the developer at deploy time, but a dashboard at runtime.

This guide covers the core patterns, when each one applies, and what separates a well-managed flag from one that quietly rots in your codebase.

The basic pattern

A feature flag SDK gives you a function. You call it with a flag key, optionally a user context, and it returns a boolean (or a variant, for multivariate flags).

import { flaggy } from '@flaggy.io/sdk-js';

const client = flaggy({ apiKey: process.env.FLAGGY_API_KEY });
await client.initialize();

if (client.isEnabled('release-checkout-flow', { key: user.id })) {
  return <NewCheckout />;
}
return <LegacyCheckout />;

The SDK downloads your ruleset on initialization and evaluates locally. No network call happens at the point of evaluation — it’s a dictionary lookup against rules in memory. This is why MAU-based pricing from some vendors doesn’t make technical sense: the vendor’s infrastructure isn’t involved when a flag evaluates.

When to use a feature flag

Release control is the most common case. You merge a half-finished feature to main behind a flag, deploy continuously, and flip the flag when it’s ready. No long-lived feature branches, no merge conflicts.

Percentage rollouts let you ship to 5% of users, watch your error rate, and expand if it looks clean. This is a canary deployment without the infrastructure complexity of actually routing traffic.

User targeting lets you ship to internal users, beta testers, or a specific customer segment first. You define a segment in the dashboard — “users where plan = enterprise” — and enable the flag for that segment.

Kill switches are flags you never intend to remove. A circuit breaker on a third-party integration, a way to disable a feature if it’s causing support volume. Having this in a dashboard is faster than a deploy.

The initialization pattern

Initialize the SDK once at app startup and share the client. Don’t initialize per-request or per-component — it defeats the local evaluation model.

// flaggy.ts — initialize once, export everywhere
import { flaggy } from '@flaggy.io/sdk-js';

export const flags = flaggy({
  apiKey: process.env.FLAGGY_API_KEY!,
});

await flags.initialize();
// any component or module
import { flags } from './flaggy';

const showFeature = flags.isEnabled('my-feature', { key: currentUser.id });

Targeting rules

Most SDKs let you pass a context object — any key/value pairs you want to use for targeting. The dashboard lets you define rules against those attributes without a code change.

Common context attributes:

flags.isEnabled('experiment-dark-mode', {
  key: user.id,
  email: user.email,
  plan: user.plan,           // target by billing tier
  accountAge: daysSinceSignup,
  country: user.countryCode,
});

You define the rule once in the dashboard: “users where plan = team AND accountAge > 30”. No deploy required to change targeting.

The cleanup problem

The real cost of feature flags isn’t evaluating them — it’s the ones you forget to remove. A codebase with 200 permanent flags is harder to reason about than one with 20.

A few habits that help:

Add a ticket at flag creation. When you create a flag, immediately create a ticket to remove it after the rollout is done. The flag has a lifespan of “rollout + one release cycle.”

Name flags by lifecycle prefix. Use a prefix that encodes intent: release-new-dashboard, experiment-checkout-button, kill-switch-payment-provider, ops-cache-ttl. It makes lifespan visible at a glance — you can immediately separate flags that need cleanup from ones that are permanent.

Review old flags quarterly. Pull a list of flags that haven’t changed in 90 days. Most of them are either fully rolled out (safe to remove) or forgotten experiments.

Server-side vs client-side evaluation

Some teams evaluate flags server-side to avoid any flag state being visible in the client bundle. The tradeoff:

Client-side: zero latency at evaluation, no server dependency, flag rules are technically visible in the bundle (usually fine for most use cases)
Server-side: rules stay private, adds one lookup per request (fast if in-memory, latency concern if remote)
For most product flags — rollouts, A/B tests, kill switches — client-side evaluation is fine. For flags that gate access control or pricing, server-side is worth the overhead.

What to look for in a flag platform

You need: a dashboard to manage flags without a code change, targeting rules, audit history (who changed what and when), and an SDK that evaluates locally.

What you don’t need to pay for: MAU metering. Modern SDKs evaluate client-side. The vendor’s servers aren’t involved in evaluations — if you’re paying per-MAU, you’re paying for a proxy metric that doesn’t reflect actual vendor cost.

Flaggy’s Team plan is $99/month flat — unlimited seats, no MAU fees, full flag analytics and audit log included.

Reducing Pager Fatigue: Why Excessive Alerting Systems Fall Short?

While recently discussing operational loads with a colleague, I heard them say, “I see the alerts, but I just don’t feel like checking them anymore.” This sentence perfectly encapsulates a reality I’ve witnessed and experienced myself at different stages of my career: “pager fatigue.” Excessive alerting systems, even when set up with good intentions, can over time become a significant source of inefficiency and demotivation for system administrators and developers.

In this post, I will delve into the roots of this problem, explain why it’s so widespread, and share the approaches I’ve tried over the years to cope with it, and what has worked. My goal is not just to solve a technical problem but also to protect the mental health and productivity of operational teams.

What is Pager Fatigue and Why Does It Matter?

Pager fatigue, in short, is the condition where individuals exposed to too many unnecessary or trivial alerts from a system become desensitized over time and overlook critical alarms. I first experienced this during the intense operational periods of an e-commerce company. Every SMS, every call at midnight, felt like the world was ending, but we would often find out that most were either false positives or already known, insignificant issues.

The human cost of this situation is quite high. Team members, having to be constantly on alert, experience chronic fatigue, stress, and eventually, a lack of engagement with their work. After a while, the risk of responding late to an important alert increases with the thought, “Is this another false alarm?” I recall once, in a manufacturing company’s ERP system, a critical database replication error alert was lost among daily “disk filling up” alerts. Despite a WAL rotation alarm sounding at 03:14 that day, the team couldn’t intervene until 08:00 because they were receiving an average of nearly 300 alerts per day, which was considered “normal.”

⚠️ Important Note

Pager fatigue is not just about “noise.” It also erodes engineers’ trust in their systems and traps them in a reactive cycle instead of enabling them to produce proactive solutions. This negatively impacts system stability and team morale in the long run.

In reality, most of the time, the team stops questioning what each alarm truly means. They act simply with the thought, “something is happening.” This situation also makes root cause analysis difficult, as the sheer volume of symptoms makes finding the real root cause like searching for a needle in a haystack.

The Origins of Excessive Alerting Systems: Well-Intentioned Bad Decisions

So, how does this pager fatigue situation arise? It’s usually a cumulative result of well-intentioned but flawed decisions. Often, the approach is “let’s monitor everything.” When a new service is deployed, default monitoring templates are applied as-is, or a separate alert is defined for every possible error scenario. This situation quickly spirals out of control, especially in large and complex systems.

Another common reason is the lack of ownership for alerting systems. An alert is defined, but over time, the system causing that alert changes or the issue is resolved, yet the alert definition is not updated or removed. A similar situation occurred in the backend of “my own side project.” Initially, I had defined a simple rule to alert if CPU usage exceeded 80%. However, over time, daily data processing batches were introduced, and CPU usage naturally spiked to 95% during these batches. This alert started triggering regularly every night and quickly became a “normal” occurrence.

ℹ️ Sharing Experience

Managing the lifecycle of an alert is as important as defining it. If an alert is no longer valid or is producing false positives, it should be revised or removed. Otherwise, the accumulated “garbage” alarms in the system will hide the truly important ones.

Furthermore, different teams trying to integrate their own monitoring solutions can increase complexity. If three different monitoring tools are used for one system, and each reports the same situation with different thresholds, this invites pager fatigue. I observed this situation “within a bank’s internal platform,” which resulted in three different teams monitoring the same database server, each setting their own “critical” thresholds.

Sifting Through the Noise: The Art of Identifying Real Problems

The first step in reducing pager fatigue is separating the noise from the signal. That is, understanding which alerts truly require intervention and which are merely informational. My approach has always been to focus on the impact, not just the problem. If an alert doesn’t directly affect user experience or stop a critical business process, it’s probably not a high-priority alarm.

At this point, Google’s SRE principles of “Golden Signals” (Latency, Traffic, Errors, Saturation) have been my guide. I applied them not just as a theory but by truly understanding what they mean in a production environment:

  • Latency: A metric that directly impacts user experience. On “an e-commerce site,” we would trigger an alarm when the load time for the cart page exceeded 2 seconds at the 99th percentile.
  • Traffic: The volume of requests coming into the service. Unexpected drops or sharp increases can be signs of a problem or a DDoS attack.
  • Errors: The rate of failed requests, especially HTTP 5xx errors.
  • Saturation: Resource usage approaching limits, such as CPU, memory, disk I/O. However, caution is needed here; 80% CPU usage isn’t always a critical issue.

💡 An Important Distinction

It is essential to design alerts that focus on root causes rather than symptoms. For example, a PostgreSQL WAL bloat alert is much more valuable than a disk full alert. This is because WAL bloat can be a precursor to a problem long before the disk is full, providing more time for intervention. Disk fullness is usually the last step and leaves very little time for intervention.

When sifting through noise, analyzing historical data is also very beneficial. Which alarms are triggered most frequently? How many of them actually required intervention? How many were false positives? These analyses have helped me make alert thresholds more realistic. On “a monitoring system I built for my own site,” I periodically reviewed the top 5 most frequently triggered alarms over the last 30 days and adjusted their thresholds or removed them entirely.

Intelligent Alert Design: What I Did, What I Didn’t

Beyond sifting through noise, I’ve implemented various strategies to make alerts more “intelligent.” This involves much more than just changing thresholds.

1. Dynamic Thresholds and Baselining: Static thresholds quickly become insufficient in situations where system load varies. Instead, I’ve preferred using dynamic thresholds that learn the system’s normal behavior and detect deviations. For time-series data in particular, algorithms that detect deviations from the past 7 or 30-day averages have been effective. In an ERP system, we knew that CPU usage naturally increased during month-end reporting periods. Defining separate thresholds for these periods or allowing the system to learn its own “normal” significantly reduced unnecessary alerts.

2. Alert Correlation and Suppression: It’s common for multiple systems to alarm due to the same root cause. For example, a network outage can cause hundreds of servers to trigger “unreachable” alerts. In such cases, we want to receive a single alert indicating the root cause, the network outage. I’ve set up systems that group similar alarms or suppress others, waiting for the main issue to be resolved. Even for fail2ban patterns, I’ve implemented rate limiting against multiple failed login attempts from a specific IP, preventing repeated alarms from the same IP.

# An example Prometheus alert rule (pseudo-code)
groups:
  - name: my-service-alerts
    rules:
    - alert: HighRequestLatency
      expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket{job="my-service"}[5m])) > 0.5
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "High service request latency"
        description: "99th percentile latency on service '{{ $labels.job }}' has been above 0.5 seconds for 5 minutes."

    - alert: ServiceDown
      expr: up{job="my-service"} == 0
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "Service unreachable"
        description: "Service '{{ $labels.job }}' has been unreachable for 1 minute."

    # Example rule for alert correlation (actual implementation might be more complex)
    - alert: MultipleServiceFailures
      expr: sum(up{job=~"my-service.*"} == 0) > 3
      for: 2m
      labels:
        severity: major
      annotations:
        summary: "Multiple services are down"
        description: "Multiple 'my-service' components have been unreachable for 2 minutes. Potential root cause: infrastructure issue."
      # When this alarm triggers, we might suppress ServiceDown alarms.
      # In reality, this is done with grouping and inhibition rules in Alertmanager.

3. Alert Prioritization and Notification Channels: Not every alarm has the same urgency. Using immediate calls (pager/SMS) for critical system failures, Slack notifications for less critical but attention-requiring situations, and email for informational or daily summaries ensures that the right channel is used for the right alert. On “a client project,” P1 (critical) alarms directly triggered a call to the mobile phone, while P2 (high) alarms only went to a Slack channel, with action expected within 15 minutes. This ensured the team addressed the right alarm with the right priority.

4. Runbook Integration: Having a runbook that clearly outlines what the team should do when an alarm triggers saves time during panic moments and ensures the correct action is taken. In some cases, automating parts of the steps in the runbook can significantly reduce the Mean Time To Recovery (MTTR). For example, for a disk full alarm, I might trigger an automation script that automatically cleans up old log files or deletes temporary files. I touched upon this topic previously in [my related post on system automation].

Operational Discipline: Culture and Process Change

Reducing pager fatigue isn’t just possible with technical solutions; it also requires changes in operational culture and processes.

1. On-Call Rotation and Responsibility: A clear on-call rotation and clearly defined responsibilities for each shift are important. It should be clear who is responsible for which alarm and when. Furthermore, the alarm load for on-call engineers should be kept within a defined “error budget” or “alarm budget.” If a team consistently exceeds this budget, it’s a sign of a serious problem with the system or alert definitions. At “a manufacturing company,” the alarm load from the previous week and how many of those alarms were real issues versus false positives were always discussed during the weekly on-call rotations.

2. Post-Mortem Culture: Conducting post-mortems after every significant incident not only helps find the root cause of the problem but also allows for drawing lessons to prevent similar incidents in the future. In a post-mortem, issues like why the alarm triggered late, why it provided incorrect information, or why it didn’t trigger at all are examined in detail. This also provides an opportunity for continuous improvement of alerting systems. Once, during a data loss incident I experienced during [my related VPS migration], the post-mortem revealed that disk I/O metrics were insufficient.

3. Regular Alert Review Meetings: Periodically reviewing all alarms (weekly or bi-weekly) is critical for identifying dead or unnecessary alarms and adjusting thresholds. In these meetings, questions like “How many times did this alarm trigger in the last month?”, “What was done when this alarm triggered?”, “Did this alarm truly require intervention?” are asked. On “my own side project’s” monitoring system, I would generate the previous month’s alarm report on the first Monday of each month and discuss it with the team. This allowed us to weed out unnecessary alarms in a timely manner.

🔥 A Trap to Avoid

Simply “silencing” or “disabling” an alarm is a temporary solution that doesn’t address the root cause and can lead to bigger problems in the long run. It’s important to remember that every alarm either points to a problem that needs to be solved or an alert definition that needs to be reconfigured.

4. Automation and Self-Healing Systems: Wherever possible, developing automated solutions for common and known issues significantly reduces pager load. For example, automatically restarting a specific service if it crashes or automatically deleting old logs when disk space exceeds a certain threshold. This allows engineers to focus on more complex issues that require human intervention.

Looking Ahead: The Role of AI and Automation

Today’s technologies, and especially artificial intelligence, are opening new horizons in reducing pager fatigue. My own experiences in AI application architecture and prompt engineering have helped me develop approaches in this area that excite me.

1. Intelligent Anomaly Detection: Instead of traditional threshold-based alerts, AI models can detect much more complex and insidious anomalies by learning the system’s normal behavior. This allows us to identify a potential problem at an early stage, even before a threshold has been breached. For example, in a user’s financial calculator, if an unexpected delay pattern occurs in a specific transaction, an AI model can flag it as an anomaly.

2. Automatic Event Correlation and Root Cause Analysis: AI can analyze thousands of log and metric data points from different systems, automatically correlate events, and suggest potential root causes, simplifying the work of engineers. This aligns with the experience I gained while doing AI-driven production planning in “a manufacturing company’s ERP”; AI can uncover hidden relationships in complex datasets.

3. Intelligent Notification and Escalation: AI can dynamically adjust notification channels and escalation chains based on the severity of an event and the number of users affected. In some cases, it can even suggest or directly apply automated remediation steps based on predefined runbooks. In “an AI-assisted system I built for my own site,” I experimented with automatically restarting certain services based on specific log patterns, and this eliminated the need for human intervention in minor issues.

ℹ️ AI’s Limitations

While AI is a powerful assistant in reducing operational load, it does not replace human intelligence and experience. Especially for rare and complex problems, AI’s predictive capabilities can be limited. Therefore, the goal should be to position AI as an “assistant” and to make operations more efficient, rather than completely eliminating human intervention.

I am trying to increase the reliability of these AI-assisted operational tools by using multi-provider fallback strategies (like Gemini Flash + Groq + Cerebras + OpenRouter). Providing uninterrupted service by switching to another provider when one has an issue is vital for these critical automations.

Conclusion

Pager fatigue is one of the most insidious and destructive problems facing modern operational teams. It’s not just a technical problem but also a cultural issue that affects engineers’ morale, productivity, and ultimately, companies’ operational efficiency. Through years of experience, I’ve seen that there is no single “silver bullet” to combat this issue.

The solution is possible through an integrated approach of intelligent alert design, strong operational discipline, and the use of next-generation AI-assisted tools. I have always operated with the philosophy of “eliminating the cause of the problem rather than solving the problem.” This is a continuous struggle and learning process. It’s important to remember that the best alerting system is one that never triggers an alert but still ensures you are confident that the system is operating healthily. My clear position is: fewer, more meaningful, and more actionable alerts always lead to better operational outcomes.

Zero to Kubernetes Part 5: Two Frontends + Complete Stack

K8s Terraform ArgoCD — Header

K8s Terraform ArgoCD — Header

Series: Zero to Kubernetes — Part 1 · Part 2 · Part 3 · Part 4 · Part 5

You’ve made it to the final part. You now have a real Kubernetes cluster, automatic TLS, GitOps with ArgoCD, PostgreSQL, and a FastAPI backend running with 2 replicas. Only what your users actually see is missing: the frontends.

We’ll deploy two independent applications that consume the same API. One is an admin dashboard (React + Vite). The other is a public app (Vue 3). Both packaged with Docker, served with nginx for production, and exposed with ingress + TLS. At the end, we’ll run a full integration test.

Final Architecture

┌────────────────────────────────────────────────────────────┐
│                      INTERNET                               │
│                         │                                   │
│    ┌────────────────────┼────────────────────┐              │
│    │                    │                    │              │
│    ▼                    ▼                    ▼              │
│ ┌──────────┐    ┌──────────────┐    ┌──────────────┐       │
│ │ admin.   │    │ api.         │    │ app.         │       │
│ │yourdomain│    │yourdomain.com│    │yourdomain.com│       │
│ └────┬─────┘    └──────┬───────┘    └──────┬───────┘       │
│      │                 │                   │               │
│      ▼                 ▼                   ▼               │
│ ┌──────────┐    ┌──────────────┐    ┌──────────────┐       │
│ │ React    │    │ FastAPI      │    │ Vue 3        │       │
│ │ Dashboard│───▶│ Backend      │◀───│ Landing Page │       │
│ │ (Vite)   │    │ (2 replicas) │    │ (Vite)       │       │
│ └──────────┘    └──────┬───────┘    └──────────────┘       │
│                        │                                   │
│                        ▼                                   │
│               ┌────────────────┐                           │
│               │ PostgreSQL 16  │                           │
│               │ (CloudNativePG)│                           │
│               └────────────────┘                           │
│                                                            │
│ ☸️ Kubernetes (kubeadm + containerd + Calico)               │
│ 🌐 nginx ingress + cert-manager (Let's Encrypt TLS)        │
│ 🔄 ArgoCD — everything synced from GitHub                  │
└────────────────────────────────────────────────────────────┘

Part A: Frontend #1 — Admin Dashboard (React + Vite)

npm create vite@latest frontend-admin -- --template react-ts
cd frontend-admin && npm install

src/App.tsx (simplified):

import { useEffect, useState } from 'react'

const API_URL = import.meta.env.VITE_API_URL || 'https://api.yourdomain.com:30443'

interface Item {
  id: number; name: string; description: string; created_at: string
}

export default function App() {
  const [items, setItems] = useState<Item[]>([])
  const [name, setName] = useState('')
  const [desc, setDesc] = useState('')
  const [loading, setLoading] = useState(true)
  const [dbStatus, setDbStatus] = useState('')

  useEffect(() => {
    fetch(`${API_URL}/health`).then(r => r.json()).then(d => setDbStatus(d.db))
    loadItems()
  }, [])

  const loadItems = () => {
    fetch(`${API_URL}/items`).then(r => r.json()).then(setItems).finally(() => setLoading(false))
  }

  const createItem = async () => {
    await fetch(`${API_URL}/items`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, description: desc })
    })
    setName(''); setDesc(''); loadItems()
  }

  const deleteItem = async (id: number) => {
    await fetch(`${API_URL}/items/${id}`, { method: 'DELETE' })
    loadItems()
  }

  return (
    <div className="dashboard">
      <header>
        <h1>📊 Admin Dashboard</h1>
        <span className={`status ${dbStatus === 'connected' ? 'ok' : 'err'}`}>
          DB: {dbStatus}
        </span>
      </header>
      <section className="create-form">
        <input value={name} onChange={e => setName(e.target.value)} placeholder="Item name" />
        <input value={desc} onChange={e => setDesc(e.target.value)} placeholder="Description" />
        <button onClick={createItem}>Create Item</button>
      </section>
      <section className="items-table">
        <h2>Items ({items.length})</h2>
        {loading ? <p>Loading...</p> : (
          <table>
            <thead><tr><th>ID</th><th>Name</th><th>Description</th><th>Created</th><th></th></tr></thead>
            <tbody>
              {items.map(item => (
                <tr key={item.id}>
                  <td>{item.id}</td><td>{item.name}</td><td>{item.description}</td>
                  <td>{new Date(item.created_at).toLocaleString()}</td>
                  <td><button onClick={() => deleteItem(item.id)}>🗑️</button></td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </section>
    </div>
  )
}

Part B: Frontend #2 — Public Landing (Vue 3)

npm create vite@latest frontend-app -- --template vue-ts
cd frontend-app && npm install

src/App.vue (simplified):

<script setup lang="ts">
import { ref, onMounted } from 'vue'

const API_URL = import.meta.env.VITE_API_URL || 'https://api.yourdomain.com:30443'
const items = ref<any[]>([])
const loading = ref(true)
const form = ref({ name: '', description: '' })

const loadItems = async () => {
  const r = await fetch(`${API_URL}/items`)
  items.value = await r.json()
  loading.value = false
}

const createItem = async () => {
  await fetch(`${API_URL}/items`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(form.value)
  })
  form.value = { name: '', description: '' }
  loadItems()
}

onMounted(loadItems)
</script>

<template>
  <main class="landing">
    <h1>🚀 Guayoyo Items</h1>
    <p class="subtitle">Create and share items instantly</p>
    <form @submit.prevent="createItem" class="create">
      <input v-model="form.name" placeholder="What do you want to create?" required />
      <textarea v-model="form.description" placeholder="Describe your item..." rows="2" />
      <button type="submit">Create</button>
    </form>
    <div class="grid">
      <div v-for="item in items" :key="item.id" class="card">
        <h3>{{ item.name }}</h3>
        <p>{{ item.description }}</p>
        <small>{{ new Date(item.created_at).toLocaleDateString() }}</small>
      </div>
    </div>
    <p v-if="loading">Loading...</p>
  </main>
</template>

Part C: Docker + nginx for Production

Both frontends use the exact same multi-stage Docker pattern:

Dockerfile:

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

nginx.conf:

server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location ~* .(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location /health {
        return 200 "OK";
        add_header Content-Type text/plain;
    }
}

Build and push for both:

docker build -t ghcr.io/your-org/frontend-admin:v1.0.0 ./frontend-admin
docker push ghcr.io/your-org/frontend-admin:v1.0.0

docker build -t ghcr.io/your-org/frontend-app:v1.0.0 ./frontend-app
docker push ghcr.io/your-org/frontend-app:v1.0.0

Part D: Kubernetes Manifests for Both Frontends

Both use the same structure. Only the image, ingress host, and API variable change.

gitops/apps/frontend-app1/deployment.yaml (Admin Dashboard):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-admin
  namespace: production
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend-admin
  template:
    metadata:
      labels:
        app: frontend-admin
    spec:
      containers:
        - name: nginx
          image: ghcr.io/your-org/frontend-admin:v1.0.0
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: "64Mi"
              cpu: "50m"
            limits:
              memory: "128Mi"
              cpu: "200m"
          livenessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 15
          readinessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 3
            periodSeconds: 10

Ingress (admin):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: frontend-admin
  namespace: production
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-production
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - admin.yourdomain.com
      secretName: admin-tls
  rules:
    - host: admin.yourdomain.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-admin
                port:
                  number: 80

Repeat the same pattern for frontend-app (Vue), changing admin to app everywhere.

Part E: ArgoCD Syncs Everything

On git push with both frontend files, ArgoCD auto-detects the new directories in gitops/apps/ (thanks to directory.recurse: true in app-of-apps). The UI shows:

🌳 app-of-apps
  ├── 🟢 database        (Healthy · Synced)
  ├── 🟢 backend         (Healthy · Synced)  2 pods
  ├── 🟢 frontend-app1   (Healthy · Synced)  2 pods
  └── 🟢 frontend-app2   (Healthy · Synced)  2 pods

Four applications, two replicas each (except DB), TLS on all ingresses. Everything from a git push.

Part F: Full Integration Test

1. Create an item from the public app

Open https://app.yourdomain.com, fill in “Coffee time” and create. The backend responds 201 Created.

2. See it in the admin dashboard

Open https://admin.yourdomain.com. The table shows:

ID Name Description Created
1 Coffee time Best coffee in town 5/20/2026

The dashboard also shows DB: connected in green.

3. Create another item from the dashboard

“Create Item” → “Morning meeting notes” → Enter.

4. Verify the public app sees it

Refresh https://app.yourdomain.com. Now there are 2 cards. Both apps share data through the same API.

5. Delete from dashboard

🗑️ on “Coffee time” → disappears from both apps.

Rollback in Action

# 1. Revert the change in git
cd gitops
git revert HEAD --no-edit
git push

# 2. ArgoCD detects the change and reverts
argocd app diff backend  # shows image going back to v1.0.0

# 3. Verify
curl https://api.yourdomain.com/health
# {"status":"ok","db":"connected"}

Total rollback time: however long git revert && git push takes. Under 30 seconds.

What You Built Across These 5 Parts

Part Deliverable
Part 1 3 Hetzner VMs, kubeadm, containerd, Calico — real cluster
Part 2 Namespaces, nginx ingress, cert-manager (auto TLS), storage, RBAC
Part 3 ArgoCD, GitOps, app-of-apps, auto sync with selfHeal + prune
Part 4 PostgreSQL with CloudNativePG, FastAPI backend with health checks and migrations
Part 5 Two frontends (React + Vue, each with its own ingress + TLS), backend ↔ frontend communication

Complete tech stack: Hetzner Cloud → Terraform → kubeadm → containerd → Calico → nginx ingress → cert-manager → ArgoCD → CloudNativePG → FastAPI → React → Vue → Docker → GitHub

Next Steps (On Your Own)

What you built is the foundation. On top of this you can add:

  • CI with GitHub Actions: Automatic Docker build on push, push to registry, update tag in GitOps repo
  • Monitoring: Prometheus + Grafana (kube-prometheus-stack installs in 5 minutes)
  • Centralized Logs: Loki + Grafana
  • HA: Second PostgreSQL instance with replication, multiple control-planes
  • Service Mesh: Linkerd for automatic mTLS between services
  • Image Updater: ArgoCD Image Updater for auto-updating images on new tags
  • Real Secrets: Migrate from plain text Secret to SealedSecrets or ExternalSecrets + Vault

At Guayoyo Tech, we build complete architectures like this for companies that need serious infrastructure. From the Terraform that provisions the VMs to the last button on the React dashboard. Kubernetes, GitOps, CI/CD, monitoring — all integrated, documented, with knowledge transfer included. Talk to the team free for 15 minutes and we’ll show you how to take your infrastructure to the next level.