DOCUMENTAÇÃO PARA DESENVOLVEDORES / EM BREVE

Desenvolva com clareza.

Uma API compatível com a OpenAI, com rotas de modelos publicadas e tratamento de dados claro.

Onde estamos

A API está pronta e testada. Até a primeira rota de modelo abrir, as solicitações de chat retornam 503 inference_not_configured. O estado em tempo real está sempre em https://undergrove.vercel.app/api/status.

Ela segue o formato da API da OpenAI, então os SDKs existentes funcionam trocando a URL base: chat com imagens, chamada de funções e saídas estruturadas, além de embeddings, transcrição e fala. Para que as solicitações fiquem ilegíveis até para o nosso gateway, use o modo criptografado, como o espaço de trabalho faz.

Início rápido

URL base: https://undergrove.vercel.app/v1. Liste os modelos com GET /v1/models e depois transmita uma resposta em streaming:

request.shShell
curl https://undergrove.vercel.app/v1/chat/completions \
  -H "Authorization: Bearer $UNDERGROVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model-id>",
    "stream": true,
    "messages": [{"role": "user", "content": "Help me explore an idea."}]
  }'
Exemplo de respostaUm exemplo; valores encurtados

HTTP/2 200

9 cabeçalhos
content-type
text/event-stream; charset=utf-8
cache-control
no-store
x-undergrove-route
tinfoil
x-undergrove-attestation
verified-by-gateway
x-undergrove-enclave-host
inference.tinfoil.sh
x-undergrove-enclave-measurement
c0ffee12…9a7d
x-undergrove-enclave-key
4f2b81e0…d3c6
x-undergrove-attestation-sha256
8e41c7aa…01fb
x-undergrove-attestation-url
/v1/attestation?sha256=8e41c7aa…01fb

    A resposta até agora

    Migre da OpenAI com uma linha

    O Undergrove fala a API da OpenAI, então os SDKs oficiais funcionam como estão. Passe ao cliente a URL base do Undergrove e uma chave do Undergrove, na linha que o cria, e depois indique um dos modelos abertos oferecidos aqui.

    import os
    from openai import OpenAI
     
    client = OpenAI(base_url="https://undergrove.vercel.app/v1", api_key=os.environ["UNDERGROVE_API_KEY"])
     
    response = client.chat.completions.create(
        model="<model-id>",
        messages=[{"role": "user", "content": "Summarise this contract in three points."}],
    )
    print(response.choices[0].message.content)
    

    Mudam 2 linhas: a URL base e a chave do cliente, e o modelo.

    Ou não mude código nenhum: os dois SDKs leem OPENAI_BASE_URL e OPENAI_API_KEY quando o cliente é criado sem eles, então definir essas variáveis como https://undergrove.vercel.app/v1 e a sua chave muda um app sem mexer nele. Streaming, imagens, chamada de funções e saídas estruturadas funcionam como descrito abaixo, nos modelos que oferecem suporte.

    Ferramentas para desenvolvedores

    Clientes que verificam o enclave e criptografam na sua própria máquina. Cada um verifica primeiro a atestação do enclave da Tinfoil e depois criptografa cada solicitação para aquele enclave, de modo que o retransmissor do Undergrove a repassa sem conseguir lê-la. Eles ficam na pasta packages/ do repositório, são testados com um substituto local e ainda não foram publicados em nenhum registro.

    Experimente a CLI

    Este terminal reproduz o comando real undergrove. A saída foi gravada com o substituto local usado nos testes dos pacotes: verify é uma verificação real de uma atestação gravada da Tinfoil, models lista o catálogo atual e as respostas a ask são roteirizadas. Não há nenhum modelo por trás desta página, e nada do que você digita sai dela.

    O terminal carrega quando você chega até ele.

    Apps de exemplo

    Três pequenos apps web em packages/examples, cada um com uma página e um servidor Node que guarda a chave. Sem chave, eles rodam com o substituto local e respostas roteirizadas, e nada sai do seu computador; com UNDERGROVE_API_KEY definida, o mesmo código verifica o enclave e criptografa cada solicitação para ele.

    Um chat que responde a partir de um arquivo da central de ajuda, transmitido para a página como server-sent events.

    npm run support-botem packages/examples; o código-fonte está em packages/examples/support-bot

    const SYSTEM = `You are the support assistant for Acme Bikes.
    Answer only from the help centre below. Quote prices and times exactly.
    When it doesn't cover a question, say so and offer the contact options.
    Keep answers short: two to four sentences, or a short list.
    
    ${knowledge}`;
    
    // Streams one answer. `history` is the conversation so far, as
    // [{ role: "user" | "assistant", content }].
    export async function answer(undergrove, history, send) {
      const stream = await undergrove.chat.completions.create({
        model,
        stream: true,
        max_tokens: 600,
        messages: [{ role: "system", content: SYSTEM }, ...history.slice(-12)],
      });
      for await (const chunk of stream) {
        const text = chunk.choices[0]?.delta?.content;
        if (text) send({ text });
      }
    }

    No seu editor e na CI

    • SDK para Node@undergrove/sdk: chat no estilo da OpenAI, streaming, embeddings, transcrição e fala, criptografado por padrão.packages/sdk-js
    • SDK para Pythonundergrove, sobre o SDK para Python da Tinfoil, com a mesma criptografia por padrão.packages/sdk-python
    • CLIundergrove ask, models e verify, com a chave no chaveiro do sistema. Para editores: --events transmite linhas JSON, acp o executa como agente e mcp como ferramentas.packages/cli
    • VS CodeUm painel de chat, e Explain selection, Fix this (com Apply fix) e Write tests sobre o código que está na sua frente.packages/vscode
    • JetBrains IDEsUm chat em uma janela de ferramentas e as mesmas três ações no IntelliJ IDEA, no PyCharm e nos demais, com a chave no cofre de senhas da IDE.packages/jetbrains
    • ZedO Undergrove como agente no Agent Panel, com /explain, /fix, /tests e /review, ou como ferramentas para o agente próprio do Zed. Os dois executam a CLI, que criptografa na sua máquina.packages/zed
    • Neovim:UndergroveAsk, e :UndergroveExplain sobre uma seleção visual, com a resposta chegando em tempo real em uma janela flutuante.packages/neovim
    • RaycastAsk Undergrove e Ask About Selected Text em qualquer app.packages/raycast
    • GitHub ActionUndergrove Review: uma revisão por pull request, com comentários nas linhas e sugestões, e a atestação no rodapé.packages/github-action

    Playground

    Envie uma solicitação de chat desta página com a sua própria chave e veja a resposta, o uso e a resposta bruta, com a mesma solicitação em curl. Ela vai do seu navegador para o /v1 deste site, como qualquer outra chamada de API, e gasta a sua cota ou o seu crédito como de costume.

    Escolha Criptografado para enviá-la do jeito que o modo criptografado dos SDKs faz: o SDK da Tinfoil verifica o enclave nesta página e depois sela a solicitação para ele. O playground mostra os bytes que de fato saíram da página, o que voltou e o comprovante do enclave para o qual ela foi selada.

    Fica apenas na memória desta página e é enviada apenas para https://undergrove.vercel.app/v1. Recarregar ou sair da página apaga a chave. Uma chave com limite diário limita quanto um erro pode custar.

    Caminho

    O modo criptografado precisa de um modelo que rode em um enclave; este não roda.

    A API ainda não está respondendo: até a primeira rota de modelo abrir, as solicitações retornam 503 inference_not_configured. Mesmo assim, você pode montar uma solicitação e copiá-la como curl.

    Mensagens

    Ctrl ou ⌘ e Enter em uma mensagem também a enviam.

    A mesma solicitação com curl

    curl --no-buffer https://undergrove.vercel.app/v1/chat/completions \
      -H "Authorization: Bearer $UNDERGROVE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "<model-id>",
        "messages": [
          {
            "role": "user",
            "content": "Me ajuda a explorar uma ideia."
          }
        ],
        "stream": true
      }'

    Autenticação

    Envie uma chave de API do Undergrove como token Bearer. As chaves começam com ugk_ e são emitidas por convite durante o alfa. Onde as chaves de autoatendimento estiverem ativadas, os membros do alfa criam até cinco nas configurações do espaço de trabalho, e cada uma gasta a cota e o crédito da conta dela. Guardamos apenas um hash unidirecional de cada chave, então uma chave perdida não pode ser recuperada, apenas substituída.

    Cada chave de autoatendimento pode ter os próprios limites, definidos ao criá-la ou depois nas configurações: um limite de gasto diário, depois do qual as solicitações dela são recusadas até a meia-noite UTC, e uma lista dos modelos que ela pode usar. Uma chave com limite continua contando para a cota da conta. As chaves criadas antes de os limites existirem não têm nenhum e continuam funcionando como antes.

    models.shShell
    curl https://undergrove.vercel.app/v1/models \
      -H "Authorization: Bearer $UNDERGROVE_API_KEY"

    Endpoints

    EndpointFinalidade
    GET /v1/modelsOs modelos disponíveis agora, cada um com o tipo (chat, embedding, transcrição ou fala), a licença, o tamanho de contexto, os preços, os níveis de raciocínio, o suporte a imagens, ferramentas e pesquisa na web, o ritmo (rápido ou ponderado) e onde roda.
    POST /v1/chat/completionsChat, streamed or as a single response, with images, function calling and structured outputs where the model supports them.
    POST /v1/embeddingsVetores para busca e similaridade do Nomic Embed Text v1.5 (768 dimensões). Até 256 entradas. US$ 0,002 a cada 48 entradas ou 67.200 caracteres, o que for maior.
    POST /v1/audio/transcriptionsTranscrições do Whisper Large v3 Turbo. Envio multipart de até 4,4 MB, com idioma e prompt opcionais. US$ 0,01 por megabyte iniciado.
    POST /v1/audio/speechÁudio WAV do Qwen3-TTS em nove vozes: aiden, dylan, eric, ono_anna, ryan, serena, sohee, uncle_fu e vivian. Até 4.096 caracteres. US$ 0,005 a cada 600 caracteres iniciados.
    GET /api/statusEstado do serviço, limites de uso e o registro completo de modelos.

    As respostas usam o formato padrão chat.completion. Os streams enviam eventos chat.completion.chunk e terminam com data: [DONE]. Os modelos de raciocínio transmitem o raciocínio separadamente em delta.reasoning_content, e o último chunk informa o uso de tokens. Se um stream for interrompido, o evento final é um objeto de erro em vez de [DONE], então uma resposta parcial nunca é confundida com uma completa.

    embeddings.shShell
    curl https://undergrove.vercel.app/v1/embeddings \
      -H "Authorization: Bearer $UNDERGROVE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "undergrove/nomic-embed-text",
        "input": ["Where is the signed contract?", "Payment terms"]
      }'

    Parâmetros

    CampoTipoPadrãoSignificado
    modelstringObrigatórioUm id de /v1/models.
    messagesarrayObrigatórioAté 200 mensagens system, user, assistant e tool. Nos modelos com visão, as mensagens user podem incluir até 16 partes image_url como data URLs em base64 (PNG, JPEG, GIF ou WebP); links não são buscados. Cada imagem conta como 3.000 tokens de entrada, a menos que o modelo informe outro valor.
    tools, tool_choice, parallel_tool_callsarray, string | object, boolean—Chamada de funções para os modelos com tools em /v1/models: até 128 funções. As chamadas em streaming chegam como deltas tool_calls, e a resposta termina com finish_reason tool_calls. Devolva os resultados como mensagens tool com o tool_call_id de cada uma.
    response_formatobject—Saídas estruturadas: {"type": "json_object"}, ou json_schema com um nome e um schema.
    streambooleanfalseEnvie true para transmitir a resposta como server-sent events.
    max_tokensintegerO limite do modeloTambém aceito como max_completion_tokens. Limitado ao limite de saída do modelo.
    temperature, top_pnumberDefinido pelo modeloControles de amostragem: 0–2 e 0–1.
    reasoning_effortstringDefinido pelo modeloQuanto tempo um modelo de raciocínio pensa antes de responder. Os valores aceitos variam conforme o modelo e estão listados em /v1/models; Rápido e Minucioso, no espaço de trabalho, usam os valores fast e thorough listados.
    web_search_optionsobject—Envie {} para deixar o modelo pesquisar na web. As consultas são filtradas para remover dados pessoais, e os resultados, para detectar injeção de prompt. As fontes chegam como anotações url_citation. Cada solicitação com pesquisa custa US$ 0,05 da sua cota.
    stopstring | string[]—Uma string ou até quatro strings.
    presence_penalty, frequency_penalty, seednumber, integer—Repassados quando a rota do modelo oferece suporte.
    tools.shShell
    curl https://undergrove.vercel.app/v1/chat/completions \
      -H "Authorization: Bearer $UNDERGROVE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "<model-id>",
        "max_tokens": 500,
        "messages": [{"role": "user", "content": "Is it raining in Lisbon?"}],
        "tools": [{
          "type": "function",
          "function": {
            "name": "get_weather",
            "description": "Current weather for a city",
            "parameters": {
              "type": "object",
              "properties": {"city": {"type": "string"}},
              "required": ["city"]
            }
          }
        }]
      }'

    Modo criptografado

    A API padrão é lida pelo nosso gateway na memória para verificar cada solicitação. Para que as solicitações fiquem ilegíveis também para o Undergrove, use o SDK da Tinfoil com a sua chave. Ele verifica a atestação de hardware do enclave e depois criptografa cada solicitação para que só aquele enclave possa lê-la. O nosso retransmissor verifica a sua chave e a sua cota e repassa o texto cifrado.

    1. Seu códigoO SDK da Tinfoil verifica o enclave e depois criptografa cada solicitação para ele.Pode ler a solicitação
    2. Selado para o enclaveRetransmissor do UndergroveVerifica a chave e a cota e repassa o texto cifrado. Não consegue lê-lo.Vê apenas texto cifrado
    3. Ainda seladoEnclave do modeloO único lugar onde a solicitação é descriptografada e o modelo roda.Pode ler a solicitação
    Modo criptografado: o seu código verifica o próprio enclave, e a solicitação continua selada até chegar lá dentro.
    encrypted.mjsJavaScript
    import { SecureClient } from "tinfoil"; // npm install tinfoil
    
    const RELAY = "https://undergrove.vercel.app/api/enclave";
    const client = new SecureClient({
      baseURL: `${RELAY}/v1/`,
      attestationBundleURL: RELAY,
    });
    await client.ready(); // verifies the enclave; throws if it can't
    
    const response = await client.fetch("chat/completions", {
      method: "POST",
      headers: {
        authorization: `Bearer ${process.env.UNDERGROVE_API_KEY}`,
        "content-type": "application/json",
      },
      body: JSON.stringify({
        model: "gemma4-31b", // enclave_model in /v1/models
        max_tokens: 1000,
        messages: [{ role: "user", content: "Summarise this contract." }],
      }),
    });
    console.log((await response.json()).choices[0].message.content);

    O mesmo cliente acessa embeddings, audio/transcriptions e audio/speech. Como o retransmissor não consegue ler essas solicitações, os modelos são indicados pelo id enclave_model de /v1/models, as respostas voltam no formato da Tinfoil e o uso é estimado a partir do tamanho dos dados criptografados. As cotas e o crédito se aplicam como de costume.

    Servidor MCP

    O Undergrove também é um servidor Model Context Protocol, em https://undergrove.vercel.app/mcp. Cada modelo real é uma ferramenta, como ask_<model>, que clientes MCP como Claude Code, Cursor e Claude Desktop podem chamar com a sua chave de API. Uma ferramenta recebe um prompt e, opcionalmente, system, max_tokens e, para modelos que pesquisam, web_search, e retorna a resposta do modelo como texto.

    Uma chamada de ferramenta é uma solicitação de API como qualquer outra: o gateway a lê na memória, envia pela rota publicada do modelo, não guarda cópia e a desconta dos limites, da cota e do crédito da sua chave. O servidor usa HTTP com streaming, responde em JSON e não mantém sessões. Ele fala as versões de protocolo 2025-11-25, 2025-06-18 e 2025-03-26.

    Claude Code

    Execute uma vez em um terminal:

    terminalShell
    claude mcp add --transport http undergrove https://undergrove.vercel.app/mcp \
      --header "Authorization: Bearer ugk_your_key"

    Cursor

    Em ~/.cursor/mcp.json, ou em .cursor/mcp.json dentro de um projeto:

    mcp.jsonJSON
    {
      "mcpServers": {
        "undergrove": {
          "url": "https://undergrove.vercel.app/mcp",
          "headers": {
            "Authorization": "Bearer ugk_your_key"
          }
        }
      }
    }

    Claude Desktop

    Em claude_desktop_config.json (Settings › Developer › Edit Config). O Claude Desktop acessa servidores remotos pelo mcp-remote, que precisa do Node.js:

    claude_desktop_config.jsonJSON
    {
      "mcpServers": {
        "undergrove": {
          "command": "npx",
          "args": [
            "-y",
            "mcp-remote",
            "https://undergrove.vercel.app/mcp",
            "--header",
            "Authorization:${AUTH_HEADER}"
          ],
          "env": {
            "AUTH_HEADER": "Bearer ugk_your_key"
          }
        }
      }
    }

    Qualquer cliente

    Ou chame-o diretamente. Ele fala JSON-RPC por HTTP com streaming:

    tools-list.shShell
    curl https://undergrove.vercel.app/mcp \
      -H "Authorization: Bearer ugk_your_key" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'

    Depois, chame a ferramenta de um modelo:

    tools-call.shShell
    curl https://undergrove.vercel.app/mcp \
      -H "Authorization: Bearer $UNDERGROVE_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{
        "jsonrpc": "2.0",
        "id": 2,
        "method": "tools/call",
        "params": {
          "name": "ask_<model>",
          "arguments": {"prompt": "Name three uses for a sealed envelope."}
        }
      }'

    No sentido inverso, o espaço de trabalho pode usar ferramentas de servidores MCP que você conecta nas Configurações. Essas chamadas passam pelo servidor do Undergrove até o servidor que você conectou, em texto simples, então use servidores em que você confia com o que as ferramentas recebem.

    Erros e limites

    Os erros usam o formato da OpenAI: {"error": {"message", "type", "code"}}.

    StatusCódigos
    400invalid_request, invalid_json, unsupported_parameter, unsupported_content, context_length_exceeded, model_rejected_request, unsupported_api_version, async_needs_webhook, async_stream, invalid_webhook_url
    401authentication_required, invalid_api_key, invalid_session, sandbox_key (uma chave ugk_sandbox_, que só o substituto local responde)
    403alpha_access_required, model_not_allowed_for_key (a chave está limitada a outros modelos)
    404model_not_found, webhook_not_found, thread_not_found, assistant_not_found, prompt_not_found, version_not_found
    409idempotency_in_progress (a primeira solicitação com este Idempotency-Key ainda está em execução), version_conflict (um item selado mudou desde que foi lido), id_taken
    413request_too_large (chat e transcrição acima de 4,4 MB, embeddings acima de 1 MB, fala acima de 100 KB)
    422idempotency_key_reused (o mesmo Idempotency-Key com outra solicitação)
    429rate_limited, budget_exhausted, capacity_exhausted, key_budget_exceeded (a chave atingiu o próprio limite diário; a mensagem informa o horário de reinício). Aguarde o tempo do cabeçalho Retry-After, adicione crédito nas configurações do espaço de trabalho ou aumente lá o limite da chave.
    502model_unavailable, stream_interrupted
    503capacity_exhausted (com Retry-After), inference_not_configured, realtime_not_available (uma rota de áudio não tem atestação)
    504model_timeout

    Durante o alfa, cada chave ou conta tem uma cota por minuto, de solicitações diárias e de gasto diário. Os valores atuais são publicados em /api/status.

    429.jsonJSON
    HTTP/2 429
    retry-after: 41
    
    {
      "error": {
        "message": "You’re sending messages quickly. Please wait a moment.",
        "type": "rate_limit_error",
        "code": "rate_limited"
      }
    }

    Saiba para onde vão os seus dados

    1. Seu servidorEnvia a solicitação com a sua chave.Pode ler a solicitação
    2. HTTPSGateway do UndergroveVerifica a chave e os limites, lê a solicitação na memória e não guarda cópia.Pode ler a solicitação
    3. Criptografada para o enclave verificadoEnclave do modeloO enclave de hardware da Tinfoil roda o modelo na rota publicada.Pode ler a solicitação
    A API padrão: o gateway lê cada solicitação na memória para verificá-la e depois a envia criptografada para o enclave que ele verificou.

    O gateway verifica a sua chave, aplica os limites e encaminha à rota do modelo apenas os campos acima. Ele não guarda cópia de prompts ou respostas e não os registra. Os registros dele contêm apenas nomes de rotas, códigos de status e tempos. Os contadores de limite de taxa usam um hash unidirecional da sua chave ou conta e expiram em até dois dias.

    O operador, a localização e a retenção de cada rota de modelo são publicados no registro de modelos em /api/status e na página de privacidade. As solicitações nunca passam para outra rota como alternativa. A Vercel, nossa hospedagem, guarda os metadados de solicitação habituais, como endereços IP.

    Nas rotas de computação confidencial, o gateway da API verifica a atestação de hardware do enclave antes de se conectar e criptografa cada solicitação para que só aquele enclave possa lê-la. O próprio gateway ainda lê as solicitações de API na memória para verificá-las.

    Dados do espaço de trabalho

    A sincronização, os links compartilhados e os projetos compartilhados guardam apenas texto cifrado criado pelo navegador. Cada diagrama mostra, passo a passo, quem tem a chave.

    O que a verificação comprova

    Antes de enviar qualquer coisa, o espaço de trabalho executa no seu navegador o verificador de código aberto da Tinfoil. Ele confere o relatório de atestação de hardware do enclave com as chaves de assinatura da AMD ou da Intel, confere se o código medido no enclave corresponde a uma versão que a Tinfoil publicou e assinou no GitHub e confere se a chave de criptografia que ele usa pertence àquele enclave. Selecione Verificado no espaço de trabalho para ver cada verificação, a versão e a medição.

    Isso comprova que as suas mensagens só podem ser descriptografadas por aquele hardware rodando aquele código publicado. Não comprova que o código publicado esteja livre de falhas, não protege contra fraquezas dos próprios processadores e não cobre o que você faz com as respostas depois que elas chegam ao seu navegador. O código do espaço de trabalho é servido pelo Undergrove, então você está confiando que servimos o verificador tal como foi publicado; Verificar esta página compara o código que o seu navegador carregou com o manifesto da build, e npm run verify:site compara o site com a sua própria build do mesmo commit. Está prevista uma avaliação independente.

    Cada resposta de um enclave tem um botão Comprovante. Ele mostra o enclave, a versão e as medições verificadas antes do envio daquela solicitação e a chave para a qual ela foi criptografada, e os baixa como JSON. Até a aba ser fechada, o download inclui o relatório de atestação, para que você possa rodar o verificador nele de novo depois; a partir daí, ele contém o hash do relatório e onde buscar o relatório atual do enclave. O comprovante é o registro do seu navegador, não uma declaração assinada, e o enclave que ele indica é o roteador de modelos da Tinfoil; o roteador repassa as solicitações ao enclave do próprio modelo. O comprovante indica também esse enclave, e o seu navegador tenta verificá-lo do mesmo jeito. A maioria dos enclaves de modelos roda em Intel TDX, que o verificador do navegador ainda não consegue verificar; nesses casos, o comprovante informa isso, e a verificação fica a cargo do roteador, cujo código publicado verifica cada enclave de modelo antes de usá-lo.

    attestation.shShell
    # The attestation bundle the gateway verified, what it checked
    # in it, and the steps to check it again yourself
    curl https://undergrove.vercel.app/v1/attestation

    Respostas verificáveis

    Nos modelos que rodam em um enclave, cada resposta da API informa qual enclave o gateway verificou antes de enviar a sua solicitação, e GET /v1/attestation retorna o pacote de atestação correspondente, para que você possa rodar o verificador de código aberto da Tinfoil sobre a mesma evidência.

    CabeçalhoValor
    x-undergrove-attestationverified-by-gateway
    x-undergrove-enclave-hostO enclave atestado: o roteador de modelos da Tinfoil
    x-undergrove-enclave-measurementA medição do código, conferida com uma versão assinada
    x-undergrove-enclave-releaseA tag da versão (e -release-digest, o digest publicado dela)
    x-undergrove-enclave-keyA chave pública do enclave para a qual a solicitação foi criptografada
    x-undergrove-attestation-sha256O SHA-256 do relatório de atestação
    x-undergrove-attestation-urlOnde buscar o pacote: /v1/attestation?sha256=…

    Leia-os em qualquer cliente. Com o SDK para Python da OpenAI, peça a resposta bruta:

    headers.shShell
    curl -si https://undergrove.vercel.app/v1/chat/completions \
      -H "Authorization: Bearer $UNDERGROVE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"model": "<model-id>", "messages": [{"role": "user", "content": "Say hello."}]}' \
      | grep -i '^x-undergrove-'

    Para verificar uma resposta, este script em Node envia uma solicitação, busca o pacote indicado pelos cabeçalhos dela, verifica-o com @tinfoilsh/verifier e confere se a chave, a medição, o enclave e o hash do relatório correspondem. O playground acima faz o mesmo no seu navegador com Verificar neste navegador.

    verify-response.mjs
    verify-response.mjsJavaScript
    // Checks the attestation evidence on an Undergrove API response yourself.
    //
    //   npm install @tinfoilsh/verifier
    //   UNDERGROVE_API_KEY=ugk_… node verify-response.mjs
    import { createHash } from "node:crypto";
    import { Verifier } from "@tinfoilsh/verifier";
    
    const base = process.env.UNDERGROVE_BASE_URL || "https://undergrove.vercel.app/v1";
    const model = process.env.UNDERGROVE_MODEL || "undergrove/gpt-oss-120b";
    
    // 1. Each response from an attested route names the enclave the gateway
    //    verified and encrypted the request to.
    const response = await fetch(`${base}/chat/completions`, {
      method: "POST",
      headers: {
        authorization: `Bearer ${process.env.UNDERGROVE_API_KEY}`,
        "content-type": "application/json",
      },
      body: JSON.stringify({
        model,
        max_tokens: 50,
        messages: [{ role: "user", content: "Say hello." }],
      }),
    });
    if (!response.ok) throw new Error(`The API answered ${response.status}.`);
    const header = (name) => response.headers.get(`x-undergrove-${name}`);
    const claim = {
      host: header("enclave-host"),
      key: header("enclave-key"),
      measurement: header("enclave-measurement"),
      report: header("attestation-sha256"),
    };
    if (!claim.key) throw new Error("This response carries no attestation evidence.");
    
    // 2. Fetch the attestation bundle it names. Without a report hash, the
    //    route's current bundle (its key must still match).
    const query = claim.report ? `?sha256=${claim.report}` : "";
    const { bundle } = await (await fetch(`${base}/attestation${query}`)).json();
    
    // 3. Verify the bundle with Tinfoil's open-source verifier: the chip
    //    maker's signature on the report, the release's signed build record
    //    from GitHub, and the enclave's certificate. It throws if any fails.
    const verifier = new Verifier({ configRepo: "tinfoilsh/confidential-model-router" });
    const attested = await verifier.verifyBundle(bundle);
    const verified = verifier.getVerificationDocument();
    
    // 4. The verified bundle must attest exactly what the response claimed.
    const { format, body } = bundle.enclaveAttestationReport;
    const hash = createHash("sha256").update(format + body).digest("hex");
    const checks = [
      ["report hash", !claim.report || hash === claim.report],
      ["encryption key", attested.hpkePublicKey === claim.key],
      ["code measurement", verified.codeFingerprint === claim.measurement],
      ["enclave host", verified.enclaveHost === claim.host],
    ];
    for (const [name, ok] of checks) if (!ok) throw new Error(`The ${name} doesn’t match the response.`);
    
    console.log(
      `Verified: ${verified.enclaveHost} runs ${verified.configRepo} ${verified.releaseTag || ""},` +
        ` and its attested key is the one this response names (${claim.key.slice(0, 16)}…).`,
    );
    

    O que uma verificação aprovada mostra

    Que existia um enclave com aquela chave e aquele código medido: hardware genuíno de computação confidencial da AMD ou da Intel, rodando a versão que a Tinfoil publicou e assinou no GitHub. E que o gateway afirma ter verificado aquele enclave e criptografado a sua solicitação para a chave dele antes de encaminhá-la.

    O que ela não mostra

    Que o gateway de fato criptografou a sua solicitação para aquela chave. Os cabeçalhos são o relatório do gateway, não uma declaração assinada pelo enclave, e o gateway lê as solicitações de API na memória para verificá-las. Para depender da sua própria verificação, use o modo criptografado, em que o seu cliente verifica o enclave e criptografa cada solicitação por conta própria. O enclave indicado é o roteador de modelos da Tinfoil, que repassa as solicitações ao enclave do próprio modelo.

    Assistente para sites

    Coloque um assistente no seu próprio site com uma única tag script. Ela adiciona um botão que abre um painel de chat, respondido pelo modelo aberto que você escolher, dentro de um limite de gasto diário que você define. O painel é um frame servido pelo Undergrove, então a sua página não consegue ler o que os visitantes digitam.

    1. No espaço de trabalho, abra Configurações e crie uma chave publicável em Assistente para sites: o nome, o modelo, os sites que podem usá-la e um limite diário.
    2. Copie o código fornecido e cole-o antes de </body> em cada página.
    3. Experimente primeiro na página de demonstração, uma loja fictícia.
    index.htmlHTML
    <script
      src="https://undergrove.vercel.app/embed.js"
      data-key="ugp_…"
      data-model="&lt;model-id&gt;"
      data-instructions="Somos um ateliê de cerâmica. Ajude com aulas, horários de funcionamento e envios."
      async
    ></script>
    AtributoTipoPadrãoSignificado
    data-keystringObrigatórioA sua chave publicável, ugp_…
    data-modelstringObrigatórioUm dos modelos que a chave lista.
    data-titlestring“Ask a question”O título do painel.
    data-greetingstring—A primeira mensagem que os visitantes veem.
    data-instructionsstring—Do que trata o seu site, com o que ajudar e o tom. É enviado com cada pergunta; os visitantes podem lê-lo na sua página.
    data-labelstring“Ask”O texto do botão.
    data-positionstringrightright ou left.
    data-themestringautoauto (o dispositivo do visitante), light ou dark.
    data-openbooleanfalsetrue abre o painel quando a página carrega.

    Por que uma chave publicável pode ficar em uma página pública

    • Ela funciona apenas para chat, e apenas a partir do frame do próprio assistente do Undergrove. A API a recusa em qualquer outro lugar.
    • Ela funciona apenas enquanto o frame estiver incorporado em um dos sites que você listar. O navegador informa qual página incorpora o frame, e o servidor verifica isso.
    • Ela sempre tem um limite de gasto diário e uma lista de modelos. Um programa fora de um navegador pode se passar por qualquer site, então trate a chave como pública e defina o limite no valor que você aceita gastar em um dia.
    • Cada visitante tem um limite de perguntas por minuto e por dia, publicado em /api/status. Os visitantes são contados sob um hash unidirecional com chave do endereço IP deles, nunca pelo endereço, e as contagens expiram em até dois dias.

    As respostas gastam a cota e o crédito da sua conta, como qualquer chave. Nos modelos que rodam em um enclave, o frame criptografa cada mensagem no navegador do visitante para o enclave verificado, como o espaço de trabalho faz; os outros modelos são acessados pelo gateway, que lê as solicitações na memória e não guarda cópia. O seu site não vê nem uma coisa nem outra.

    Selo “Verified by Undergrove”

    Um site com assistente pode exibir um pequeno selo com link para a página do Undergrove para a chave dele. Essa página lê do servidor do Undergrove os sites e os modelos da chave, verifica no navegador do visitante o enclave por trás deles e explica o que o selo afirma e o que não afirma. Coloque a tag onde o selo deve aparecer.

    Ou, sem script, uma imagem e um link (badge-dark.svg para páginas escuras):

    badge.htmlHTML
    <script src="https://undergrove.vercel.app/badge.js" data-key="ugp_…" async></script>
    • O selo é um link. Ele não faz solicitações a partir da sua página nem verifica nada por conta própria; quem faz a verificação é a página que ele abre.
    • Ele indica a sua chave publicável, que já está na sua página por causa do assistente. A página do selo mostra os sites e os modelos da chave, e nunca a conta, o limite ou quanto já foi gasto.
    • O link pede ao navegador que envie a origem do seu site, para que a página possa informar aos visitantes se eles vieram de um dos sites da chave. Um selo copiado para outro site informa isso lá.
    • Ele atesta apenas o assistente, não o restante do seu site.

    Integrações

    A maioria dos clientes do próprio Undergrove verifica a atestação do enclave com o verificador da Tinfoil e depois criptografa cada solicitação para aquele enclave, de modo que o retransmissor a repassa sem conseguir lê-la. Cada um usa a sua chave de API e os ids de modelo do Undergrove. Eles ainda não estão em registros de pacotes; cada um é compilado a partir de packages/ no repositório do site.

    Os clientes em Rust, Java e Kotlin e o app do Make usam a API para desenvolvedores, já que não há SDK da Tinfoil para eles se basearem: o gateway lê cada solicitação na memória, verifica ele mesmo o enclave e informa o que verificou nos cabeçalhos da resposta.

    PacoteParaO que faz
    @undergrove/sdkNodeUm cliente no estilo da OpenAI com streaming, ferramentas, embeddings, transcrição e fala.
    undergrovePythonO cliente para Python, com LangChain (ChatUndergrove) e LlamaIndex (UndergroveLLM) como extras.
    GoGoUm cliente sobre o verificador em Go da Tinfoil, com streaming e chamadas de ferramentas.
    UndergroveSwiftUm pacote Swift para macOS 14 e iOS 17, sobre o verificador em Swift da Tinfoil.
    @undergrove/ai-sdk-providerVercel AI SDKgenerateText, streamText e ferramentas, com o que foi verificado nos metadados do provedor de cada resposta.
    @undergrove/langchainLangChain.jsUm modelo de chat e embeddings.
    @undergrove/reactReactComponentes e hooks de chat, e uma rota de servidor que mantém a chave de API fora do navegador.
    create-undergrove-appModelo inicialnpm create undergrove-app: um app de chat privado com deploy na Vercel em dois comandos.
    @undergrove/agentsAgentesUm loop de chamadas de ferramentas que registra qual enclave verificado respondeu a cada etapa, em uma trilha que qualquer pessoa pode verificar em busca de alterações, com enclaves fixados.
    undergroveRustUm cliente para a API para desenvolvedores, com streaming e chamadas de ferramentas, e a evidência de atestação do gateway em cada resposta. Ele próprio não criptografa as solicitações para o enclave.
    dev.undergrove:undergroveJava e KotlinUm cliente para a API para desenvolvedores, com streaming, ferramentas e embeddings, além de corrotinas e um Flow para Kotlin. Não é o modo criptografado: o gateway lê as solicitações na memória.
    undergrove[jupyter]JupyterA cell magic %%undergrove, com variáveis do notebook e DataFrames como contexto, e um widget de atestação.
    @undergrove/officeExcel e Word=UNDERGROVE.ASK() em qualquer célula e, no Word, resumir, reescrever ou fazer perguntas sobre a seleção.
    @undergrove/officeOutlookUm resumo da mensagem aberta e um rascunho de resposta que você revisa antes de enviar.
    undergrove-obsidianObsidianPergunte sobre a nota aberta, resuma, reescreva e continue escrevendo, com o enclave verificado abaixo de cada resposta. Apenas no desktop.
    Undergrove for AlfredAlfredDigite ug e uma pergunta: a resposta em Large Type e na área de transferência, criptografada pela CLI undergrove.
    n8n-nodes-undergroven8nUm nó da comunidade: perguntar a um modelo, criar embeddings e listar modelos, com o que foi verificado em cada item.
    Undergrove for ZapierZapierAções Ask a Model e Create Embeddings, com cada solicitação criptografada para o enclave a partir do processo do Zap.
    Undergrove for MakeMakeUm app personalizado sobre a API para desenvolvedores: o gateway verifica o enclave e informa o que verificou na saída de cada módulo.
    Undergrove Enclave PinGitHub ActionsUma etapa que verifica o enclave no runner com o verificador da Tinfoil e faz o job falhar quando ele não é o que você fixou.

    Com o Vercel AI SDK:

    summarise.mjsJavaScript
    import { streamText } from "ai";
    import { undergrove } from "@undergrove/ai-sdk-provider";
    
    const result = streamText({
      model: undergrove("undergrove/gemma-4-31b"),
      prompt: "Summarise this contract.",
    });
    for await (const text of result.textStream) process.stdout.write(text);

    Com os componentes React e o template inicial, as mensagens vão da página para o seu próprio servidor, que as sela; quem opera esse servidor pode lê-las, como em qualquer integração do lado do servidor.

    Testes na CI

    O substituto com que os pacotes são testados responde a chaves sandbox: qualquer chave que comece com ugk_sandbox_. Os seus testes podem usar uma sem segredo nem cobranças, e o próprio Undergrove as recusa com sandbox_key. As respostas dele são roteirizadas, não de um modelo. No GitHub Actions, uma etapa o inicia e define UNDERGROVE_BASE_URL e UNDERGROVE_API_KEY:

    .github/workflows/test.ymlYAML
    - uses: ekempinski/undergrove/packages/github-action/sandbox@main
    - run: npm test   # UNDERGROVE_API_KEY=ugk_sandbox_ci
    
    # And before a deploy: fail if the enclave isn’t the one you pinned.
    - uses: ekempinski/undergrove/packages/github-action/pin@main
      with:
        measurement: 9134838da8aa4e1792566b2988820340356517c4db0c94261eb8154843e0357554ba72fcf185f950d9301fbede574c90

    Próximos passos

    DisponívelWorkspace with encryption from your browser to the enclave, streaming gateway, OpenAI-compatible API with images, function calling, structured outputs, embeddings, transcription and speech, encrypted API mode, web search, documents, folders and codebases, images, audio transcripts, read-aloud, search across long documents, voice input, model register, saved conversations and export on your device.
    PróximoPrimeira rota de modelo, login do alfa privado, limites de uso compartilhados.
    Pronto, ainda não ativadoChaves de autoatendimento, planos pagos e crédito pré-pago para uso além da cota diária.
    PlanejadoAcesso para equipes.
    Requer verificaçãoAvaliação de segurança independente do espaço de trabalho, do retransmissor e do gateway.