DOCUMENTAZIONE PER SVILUPPATORI / IN ARRIVO

Sviluppa con chiarezza.

Un’API compatibile con OpenAI, con percorsi dei modelli pubblicati e un trattamento dei dati chiaro.

A che punto siamo

L’API è pronta e testata. Finché non si apre il primo percorso di modello, le richieste di chat restituiscono 503 inference_not_configured. Lo stato in tempo reale è sempre su https://undergrove.vercel.app/api/status.

Segue il formato dell’API di OpenAI, quindi gli SDK esistenti funzionano cambiando l’URL di base: chat con immagini, function calling e output strutturati, oltre a embedding, trascrizione e sintesi vocale. Per rendere le richieste illeggibili persino per il nostro gateway, usa la modalità cifrata, come fa lo spazio di lavoro.

Guida rapida

URL di base: https://undergrove.vercel.app/v1. Elenca i modelli con GET /v1/models, poi ricevi una risposta in 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."}]
  }'
Risposta di esempioUn esempio; valori abbreviati

HTTP/2 200

9 header
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

    La risposta finora

    Passa da OpenAI con una riga

    Undergrove parla l’API di OpenAI, quindi gli SDK ufficiali funzionano così come sono. Dai al client l’URL di base di Undergrove e una chiave di Undergrove, nella riga che lo crea, poi indica uno dei modelli aperti offerti qui.

    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)
    

    Cambiano 2 righe: l’URL di base e la chiave del client, e il modello.

    Oppure non cambiare affatto il codice: entrambi gli SDK leggono OPENAI_BASE_URL e OPENAI_API_KEY quando il client viene creato senza questi valori, quindi impostarli su https://undergrove.vercel.app/v1 e sulla tua chiave fa passare un’app così com’è. Streaming, immagini, function calling e output strutturati funzionano come descritto più avanti, per i modelli che li supportano.

    Strumenti per sviluppatori

    Client che verificano l’enclave e cifrano sul tuo computer. Ognuno controlla prima l’attestazione dell’enclave di Tinfoil, poi cifra ogni richiesta per quell’enclave, così il relay di Undergrove la inoltra senza poterla leggere. Si trovano nella cartella packages/ del repository, sono testati con un sostituto locale e non sono ancora pubblicati in nessun registro.

    Prova la CLI

    Questo terminale riproduce il vero comando undergrove. Il suo output è stato registrato con il sostituto locale con cui vengono testati i pacchetti: verify è un vero controllo di un’attestazione Tinfoil registrata, models elenca il catalogo attuale e le risposte di ask sono preimpostate. Dietro questa pagina non c’è alcun modello, e niente di ciò che scrivi la lascia.

    Il terminale si carica quando lo raggiungi.

    App di esempio

    Tre piccole app web in packages/examples, ciascuna con una pagina e un server Node che custodisce la chiave. Senza chiave funzionano con il sostituto locale e risposte preimpostate, e niente lascia il tuo computer; con UNDERGROVE_API_KEY impostata, lo stesso codice verifica l’enclave e cifra ogni richiesta per essa.

    Una chat che risponde a partire da un file del centro assistenza, trasmessa alla pagina come server-sent events.

    npm run support-botin packages/examples; il codice sorgente è in 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 });
      }
    }

    Nel tuo editor e nella CI

    • SDK per Node@undergrove/sdk: chat in stile OpenAI, streaming, embedding, trascrizione e sintesi vocale, cifrati per impostazione predefinita.packages/sdk-js
    • SDK per Pythonundergrove, basato sull’SDK Python di Tinfoil, con la stessa cifratura predefinita.packages/sdk-python
    • CLIundergrove ask, models e verify, con la chiave nel portachiavi di sistema. Per gli editor: --events trasmette righe JSON, acp lo esegue come agente e mcp come strumenti.packages/cli
    • VS CodeUn pannello di chat, e Explain selection, Fix this (con Apply fix) e Write tests sul codice che hai davanti.packages/vscode
    • JetBrains IDEsUna chat in una finestra degli strumenti e le stesse tre azioni in IntelliJ IDEA, PyCharm e gli altri, con la chiave nel password safe dell’IDE.packages/jetbrains
    • ZedUndergrove come agente nell’Agent Panel, con /explain, /fix, /tests e /review, o come strumenti per l’agente di Zed. Entrambi eseguono la CLI, che cifra sul tuo computer.packages/zed
    • Neovim:UndergroveAsk, e :UndergroveExplain su una selezione visuale, con la risposta che arriva in streaming in una finestra mobile.packages/neovim
    • RaycastAsk Undergrove e Ask About Selected Text in qualsiasi app.packages/raycast
    • GitHub ActionUndergrove Review: una revisione per ogni pull request, con commenti sulle righe e suggerimenti, e l’attestazione nel piè di pagina.packages/github-action

    Playground

    Invia una richiesta di chat da questa pagina con la tua chiave e guarda la risposta, il suo utilizzo e la risposta grezza, insieme alla stessa richiesta in curl. Va dal tuo browser al /v1 di questo sito, come qualsiasi altra chiamata API, e consuma la tua quota o il tuo credito come di consueto.

    Scegli Cifrata per inviarla come fa la modalità cifrata degli SDK: l’SDK di Tinfoil verifica l’enclave in questa pagina, poi sigilla la richiesta per essa. Il playground mostra i byte che hanno effettivamente lasciato la pagina, cosa è tornato indietro e la ricevuta dell’enclave per cui è stata sigillata.

    Conservata solo nella memoria di questa pagina e inviata solo a https://undergrove.vercel.app/v1. Ricaricando o lasciando la pagina viene cancellata. Una chiave con un limite giornaliero limita quanto può costare un errore.

    Percorso

    La modalità cifrata richiede un modello che funzioni in un’enclave; questo no.

    L’API non risponde ancora: finché non si apre il primo percorso di modello, le richieste restituiscono 503 inference_not_configured. Puoi comunque costruire una richiesta e copiarla come curl.

    Messaggi

    Anche Ctrl o ⌘ e Invio in un messaggio lo inviano.

    La stessa richiesta con 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": "Aiutami a esplorare un’idea."
          }
        ],
        "stream": true
      }'

    Autenticazione

    Invia una chiave API di Undergrove come token Bearer. Le chiavi iniziano con ugk_ e durante l’alpha vengono rilasciate su invito. Dove le chiavi self-service sono attive, i membri dell’alpha ne creano fino a cinque nelle impostazioni dello spazio di lavoro, e ognuna consuma la quota e il credito del proprio account. Conserviamo solo un hash unidirezionale di ogni chiave, quindi una chiave persa non si può recuperare, solo sostituire.

    Ogni chiave self-service può avere i propri limiti, impostati alla creazione o in seguito nelle impostazioni: un tetto di spesa giornaliero, superato il quale le sue richieste vengono rifiutate fino alla mezzanotte UTC, e un elenco dei modelli che può usare. Una chiave con tetto continua a essere conteggiata nella quota del suo account. Le chiavi create prima che esistessero i limiti non ne hanno e continuano a funzionare come prima.

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

    Endpoint

    EndpointScopo
    GET /v1/modelsI modelli disponibili ora, ciascuno con il tipo (chat, embedding, trascrizione o sintesi vocale), la licenza, la dimensione del contesto, i prezzi, i livelli di ragionamento, il supporto per immagini, strumenti e ricerca web, il ritmo (rapido o ponderato) e dove viene eseguito.
    POST /v1/chat/completionsChat, streamed or as a single response, with images, function calling and structured outputs where the model supports them.
    POST /v1/embeddingsVettori per ricerca e somiglianza da Nomic Embed Text v1.5 (768 dimensioni). Fino a 256 input. 0,002 $ ogni 48 input o 67.200 caratteri, a seconda di quale valore è maggiore.
    POST /v1/audio/transcriptionsTrascrizioni da Whisper Large v3 Turbo. Caricamento multipart fino a 4,4 MB, con lingua e prompt facoltativi. 0,01 $ per ogni megabyte iniziato.
    POST /v1/audio/speechAudio WAV da Qwen3-TTS con nove voci: aiden, dylan, eric, ono_anna, ryan, serena, sohee, uncle_fu e vivian. Fino a 4.096 caratteri. 0,005 $ per ogni blocco di 600 caratteri iniziato.
    GET /api/statusStato del servizio, limiti di utilizzo e il registro completo dei modelli.

    Le risposte usano la forma standard chat.completion. Gli stream inviano eventi chat.completion.chunk e terminano con data: [DONE]. I modelli di ragionamento trasmettono il ragionamento a parte in delta.reasoning_content, e l’ultimo chunk riporta l’utilizzo dei token. Se uno stream si interrompe, l’evento finale è un oggetto di errore invece di [DONE], così una risposta parziale non viene mai scambiata per una 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"]
      }'

    Parametri

    CampoTipoPredefinitoSignificato
    modelstringObbligatorioUn id da /v1/models.
    messagesarrayObbligatorioFino a 200 messaggi system, user, assistant e tool. Per i modelli con visione, i messaggi user possono includere fino a 16 parti image_url come data URL in base64 (PNG, JPEG, GIF o WebP); i link non vengono recuperati. Ogni immagine conta come 3.000 token in input, salvo diversa indicazione del modello.
    tools, tool_choice, parallel_tool_callsarray, string | object, boolean—Function calling per i modelli con tools in /v1/models: fino a 128 funzioni. Le chiamate in streaming arrivano come delta tool_calls, e la risposta termina con finish_reason tool_calls. Rimanda i risultati come messaggi tool con il loro tool_call_id.
    response_formatobject—Output strutturati: {"type": "json_object"}, oppure json_schema con un nome e uno schema.
    streambooleanfalseInvia true per ricevere la risposta in streaming come server-sent events.
    max_tokensintegerIl limite del modelloAccettato anche come max_completion_tokens. Limitato al limite di output del modello.
    temperature, top_pnumberStabilito dal modelloControlli di campionamento: 0–2 e 0–1.
    reasoning_effortstringStabilito dal modelloQuanto a lungo un modello di ragionamento pensa prima di rispondere. I valori accettati variano da modello a modello e sono elencati in /v1/models; le modalità Veloce e Approfondita dello spazio di lavoro usano i valori fast e thorough elencati.
    web_search_optionsobject—Invia {} per permettere al modello di cercare sul web. Le query vengono filtrate per rimuovere i dati personali e i risultati per individuare prompt injection. Le fonti arrivano come annotazioni url_citation. Ogni richiesta con ricerca costa 0,05 $ della tua quota.
    stopstring | string[]—Una stringa o fino a quattro stringhe.
    presence_penalty, frequency_penalty, seednumber, integer—Inoltrati quando il percorso del modello li supporta.
    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"]
            }
          }
        }]
      }'

    Modalità cifrata

    L’API standard viene letta in memoria dal nostro gateway per controllare ogni richiesta. Per rendere le richieste illeggibili anche per Undergrove, usa l’SDK di Tinfoil con la tua chiave. Verifica l’attestazione hardware dell’enclave, poi cifra ogni richiesta in modo che solo quell’enclave possa leggerla. Il nostro relay controlla la tua chiave e la tua quota e inoltra il testo cifrato.

    1. Il tuo codiceL’SDK di Tinfoil verifica l’enclave, poi cifra ogni richiesta per quell’enclave.Può leggere la richiesta
    2. Sigillata per l’enclaveRelay di UndergroveControlla la chiave e la quota, inoltra il testo cifrato. Non può leggerlo.Vede solo testo cifrato
    3. Ancora sigillatoEnclave del modelloL’unico punto in cui la richiesta viene decifrata e il modello viene eseguito.Può leggere la richiesta
    Modalità cifrata: il tuo codice verifica l’enclave da sé, e la richiesta resta sigillata finché non è al suo interno.
    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);

    Lo stesso client raggiunge embeddings, audio/transcriptions e audio/speech. Poiché il relay non può leggere queste richieste, i modelli si indicano con il loro id enclave_model da /v1/models, le risposte tornano nel formato di Tinfoil e l’utilizzo viene stimato dalla dimensione dei dati cifrati. Quote e credito si applicano come di consueto.

    Server MCP

    Undergrove è anche un server Model Context Protocol, all’indirizzo https://undergrove.vercel.app/mcp. Ogni modello live è uno strumento, come ask_<model>, che i client MCP come Claude Code, Cursor e Claude Desktop possono chiamare con la tua chiave API. Uno strumento accetta un prompt e, facoltativamente, system, max_tokens e, per i modelli che cercano, web_search, e restituisce la risposta del modello come testo.

    Una chiamata a uno strumento è una richiesta API come le altre: il gateway la legge in memoria, la invia sul percorso pubblicato del modello, non ne conserva copia e la conteggia nei limiti, nella quota e nel credito della tua chiave. Il server usa HTTP in streaming, risponde in JSON e non mantiene sessioni. Supporta le versioni del protocollo 2025-11-25, 2025-06-18 e 2025-03-26.

    Claude Code

    Eseguilo una volta in un terminale:

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

    Cursor

    In ~/.cursor/mcp.json, o in .cursor/mcp.json in un progetto:

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

    Claude Desktop

    In claude_desktop_config.json (Settings › Developer › Edit Config). Claude Desktop raggiunge i server remoti tramite mcp-remote, che richiede 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"
          }
        }
      }
    }

    Qualsiasi client

    Oppure chiamalo direttamente. Parla JSON-RPC su HTTP in 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"}'

    Poi chiama lo strumento di un modello:

    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."}
        }
      }'

    Nella direzione opposta, lo spazio di lavoro può usare strumenti dei server MCP che colleghi nelle Impostazioni. Quelle chiamate passano dal server di Undergrove al server che hai collegato, in chiaro, quindi usa server a cui affideresti ciò che ricevono gli strumenti.

    Errori e limiti

    Gli errori usano il formato di OpenAI: {"error": {"message", "type", "code"}}.

    StatoCodici
    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 (una chiave ugk_sandbox_, a cui risponde solo il sostituto locale)
    403alpha_access_required, model_not_allowed_for_key (la chiave è limitata ad altri modelli)
    404model_not_found, webhook_not_found, thread_not_found, assistant_not_found, prompt_not_found, version_not_found
    409idempotency_in_progress (la prima richiesta con questo Idempotency-Key è ancora in corso), version_conflict (un elemento sigillato è cambiato da quando è stato letto), id_taken
    413request_too_large (chat e trascrizione oltre 4,4 MB, embedding oltre 1 MB, sintesi vocale oltre 100 KB)
    422idempotency_key_reused (lo stesso Idempotency-Key con una richiesta diversa)
    429rate_limited, budget_exhausted, capacity_exhausted, key_budget_exceeded (la chiave ha raggiunto il proprio tetto giornaliero; il messaggio indica l’ora di azzeramento). Attendi quanto indicato dall’header Retry-After, aggiungi credito nelle impostazioni dello spazio di lavoro, oppure alza lì il tetto della chiave.
    502model_unavailable, stream_interrupted
    503capacity_exhausted (con Retry-After), inference_not_configured, realtime_not_available (un percorso audio non è attestato)
    504model_timeout

    Durante l’alpha, ogni chiave o account ha una quota al minuto, una di richieste giornaliere e una di spesa giornaliera. I valori attuali sono pubblicati su /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"
      }
    }

    Sappi dove vanno i tuoi dati

    1. Il tuo serverInvia la richiesta con la tua chiave.Può leggere la richiesta
    2. HTTPSGateway di UndergroveControlla la chiave e i limiti, legge la richiesta in memoria, non ne conserva copia.Può leggere la richiesta
    3. Cifrata per l’enclave verificataEnclave del modelloL’enclave hardware di Tinfoil esegue il modello sul percorso pubblicato.Può leggere la richiesta
    L’API standard: il gateway legge ogni richiesta in memoria per controllarla, poi la invia cifrata all’enclave che ha verificato.

    Il gateway controlla la tua chiave, applica i limiti e inoltra al percorso del modello solo i campi indicati sopra. Non conserva copie di prompt o risposte e non li registra. I suoi log registrano solo nomi dei percorsi, codici di stato e tempi. I contatori dei limiti di frequenza usano un hash unidirezionale della tua chiave o del tuo account e scadono entro due giorni.

    L’operatore, la posizione e la conservazione dei dati di ogni percorso di modello sono pubblicati nel registro dei modelli su /api/status e nella pagina sulla privacy. Le richieste non passano mai a un percorso diverso come ripiego. Vercel, il nostro hosting, conserva i metadati standard delle richieste, come gli indirizzi IP.

    Sui percorsi di confidential computing, il gateway dell’API verifica l’attestazione hardware dell’enclave prima di connettersi e cifra ogni richiesta in modo che solo quell’enclave possa leggerla. Il gateway stesso continua però a leggere in memoria le richieste API per controllarle.

    Dati dell’area di lavoro

    Sincronizzazione, link condivisi e progetti condivisi conservano solo testo cifrato creato dal browser. Ogni diagramma mostra, passo per passo, chi ha la chiave.

    Cosa dimostra la verifica

    Prima di inviare qualsiasi cosa, lo spazio di lavoro esegue nel tuo browser il verificatore open source di Tinfoil. Controlla il report di attestazione hardware dell’enclave con le chiavi di firma di AMD o di Intel, controlla che il codice misurato nell’enclave corrisponda a una release che Tinfoil ha pubblicato e firmato su GitHub, e controlla che la chiave di cifratura che usa appartenga a quell’enclave. Seleziona Verificato nello spazio di lavoro per vedere ogni controllo, la release e la misurazione.

    Dimostra che i tuoi messaggi possono essere decifrati solo da quell’hardware mentre esegue quel codice pubblicato. Non dimostra che il codice pubblicato sia privo di difetti, non protegge da debolezze dei processori stessi e non copre ciò che fai con le risposte dopo che raggiungono il tuo browser. Il codice dello spazio di lavoro è servito da Undergrove, quindi ti fidi che serviamo il verificatore così come è stato pubblicato; Verifica questa pagina confronta il codice caricato dal tuo browser con il manifest della build, e npm run verify:site confronta il sito con una tua build dello stesso commit. È prevista una valutazione indipendente.

    Ogni risposta di un’enclave ha un pulsante Ricevuta. Mostra l’enclave, la release e le misurazioni verificate prima dell’invio di quella richiesta e la chiave per cui è stata cifrata, e li scarica come JSON. Finché la scheda resta aperta, il download include il report di attestazione, così puoi eseguire di nuovo il verificatore in seguito; dopo, contiene l’hash del report e dove recuperare quello attuale dell’enclave. La ricevuta è un registro del tuo browser, non una dichiarazione firmata, e l’enclave che indica è il router dei modelli di Tinfoil; il router passa le richieste all’enclave del modello. La ricevuta indica anche quell’enclave, e il tuo browser prova a verificarla allo stesso modo. La maggior parte delle enclave dei modelli funziona su Intel TDX, che il verificatore nel browser non può ancora controllare; in quei casi la ricevuta lo dice, e il controllo si basa sul router, il cui codice pubblicato verifica ogni enclave di modello prima di usarla.

    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

    Risposte verificabili

    Per i modelli che funzionano in un’enclave, ogni risposta dell’API dice quale enclave il gateway ha verificato prima di inviare la tua richiesta, e GET /v1/attestation restituisce il bundle di attestazione corrispondente, così puoi eseguire il verificatore open source di Tinfoil sulle stesse prove.

    HeaderValore
    x-undergrove-attestationverified-by-gateway
    x-undergrove-enclave-hostL’enclave attestata: il router dei modelli di Tinfoil
    x-undergrove-enclave-measurementLa misurazione del codice, confrontata con una release firmata
    x-undergrove-enclave-releaseIl tag della release (e -release-digest, il suo digest pubblicato)
    x-undergrove-enclave-keyLa chiave pubblica dell’enclave per cui è stata cifrata la richiesta
    x-undergrove-attestation-sha256Lo SHA-256 del report di attestazione
    x-undergrove-attestation-urlDove recuperare il bundle: /v1/attestation?sha256=…

    Leggili da qualsiasi client. Con l’SDK Python di OpenAI, chiedi la risposta grezza:

    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-'

    Per controllare una risposta, questo script Node invia una richiesta, recupera il bundle indicato nei suoi header, lo verifica con @tinfoilsh/verifier e controlla che chiave, misurazione, enclave e hash del report corrispondano. Il playground qui sopra fa lo stesso nel tuo browser con Controllalo in questo browser.

    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)}…).`,
    );
    

    Cosa dimostra un controllo superato

    Che è esistita un’enclave con quella chiave e quel codice misurato: hardware di confidential computing AMD o Intel autentico, che eseguiva la release che Tinfoil ha pubblicato e firmato su GitHub. E che il gateway dichiara di aver verificato quell’enclave e cifrato la tua richiesta per la sua chiave prima di inoltrarla.

    Cosa non dimostra

    Che il gateway abbia davvero cifrato la tua richiesta per quella chiave. Gli header sono il report del gateway, non una dichiarazione firmata dall’enclave, e il gateway legge in memoria le richieste API per controllarle. Per affidarti invece alla tua verifica, usa la modalità cifrata, in cui è il tuo client a controllare l’enclave e a cifrare ogni richiesta. L’enclave indicata è il router dei modelli di Tinfoil, che passa le richieste all’enclave del modello.

    Assistente per siti web

    Aggiungi un assistente al tuo sito con un solo tag script. Aggiunge un pulsante che apre un pannello di chat, a cui risponde il modello aperto che scegli, entro un tetto di spesa giornaliero che imposti tu. Il pannello è un frame servito da Undergrove, quindi la tua pagina non può leggere ciò che scrivono i visitatori.

    1. Nello spazio di lavoro, apri Impostazioni e crea una chiave pubblicabile in Assistente per siti web: il nome, il modello, i siti che possono usarla e un tetto giornaliero.
    2. Copia il codice che ti viene fornito e incollalo prima di </body> in ogni pagina.
    3. Provalo prima nella pagina demo, un negozio inventato.
    index.htmlHTML
    <script
      src="https://undergrove.vercel.app/embed.js"
      data-key="ugp_…"
      data-model="&lt;model-id&gt;"
      data-instructions="Siamo un laboratorio di ceramica. Aiuta con i corsi, gli orari di apertura e le spedizioni."
      async
    ></script>
    AttributoTipoPredefinitoSignificato
    data-keystringObbligatorioLa tua chiave pubblicabile, ugp_…
    data-modelstringObbligatorioUno dei modelli elencati nella chiave.
    data-titlestring“Ask a question”Il titolo del pannello.
    data-greetingstring—Il primo messaggio che vedono i visitatori.
    data-instructionsstring—Di cosa parla il tuo sito, in cosa aiutare e con quale tono. Viene inviato con ogni domanda; i visitatori possono leggerlo nella tua pagina.
    data-labelstring“Ask”Il testo del pulsante.
    data-positionstringrightright o left.
    data-themestringautoauto (il dispositivo del visitatore), light o dark.
    data-openbooleanfalsetrue apre il pannello al caricamento della pagina.

    Perché una chiave pubblicabile può stare in una pagina pubblica

    • Funziona solo per la chat, e solo dal frame dell’assistente di Undergrove. L’API la rifiuta in qualsiasi altro contesto.
    • Funziona solo mentre il frame è incorporato in uno dei siti che indichi. Il browser comunica quale pagina incorpora il frame, e il server lo controlla.
    • Ha sempre un tetto di spesa giornaliero e un elenco di modelli. Un programma al di fuori di un browser può fingere di essere qualsiasi sito, quindi considera la chiave pubblica e imposta il tetto su quanto sei disposto a spendere in un giorno.
    • Ogni visitatore ha un limite di domande al minuto e al giorno, pubblicato su /api/status. I visitatori vengono contati con un hash unidirezionale con chiave del loro indirizzo IP, mai con l’indirizzo, e i conteggi scadono entro due giorni.

    Le risposte consumano la quota e il credito del tuo account, come qualsiasi chiave. Per i modelli che funzionano in un’enclave, il frame cifra ogni messaggio nel browser del visitatore per l’enclave verificata, come fa lo spazio di lavoro; gli altri modelli vengono raggiunti tramite il gateway, che legge le richieste in memoria e non ne conserva copia. Il tuo sito non vede né gli uni né gli altri.

    Badge «Verified by Undergrove»

    Un sito con un assistente può mostrare un piccolo badge che rimanda alla pagina di Undergrove per la sua chiave. Quella pagina legge dal server di Undergrove i siti e i modelli della chiave, verifica nel browser del visitatore l’enclave che c’è dietro, e spiega cosa il badge dichiara e cosa no. Inserisci il tag dove deve comparire il badge.

    Oppure, senza script, un’immagine e un link (badge-dark.svg per le pagine scure):

    badge.htmlHTML
    <script src="https://undergrove.vercel.app/badge.js" data-key="ugp_…" async></script>
    • Il badge è un link. Non fa richieste dalla tua pagina e non controlla nulla da sé; il controllo lo fa la pagina che apre.
    • Indica la tua chiave pubblicabile, che è già nella tua pagina per l’assistente. La pagina del badge mostra i siti e i modelli della chiave, e mai l’account, il tetto o quanto è stato speso.
    • Il link chiede al browser di inviare l’origine del tuo sito, così la pagina può dire ai visitatori se arrivano da uno dei siti della chiave. Un badge copiato su un altro sito lo segnala lì.
    • Garantisce solo per l’assistente, non per il resto del tuo sito.

    Integrazioni

    La maggior parte dei client di Undergrove controlla l’attestazione dell’enclave con il verificatore di Tinfoil, poi cifra ogni richiesta per quell’enclave, così il relay la inoltra senza poterla leggere. Ognuno usa la tua chiave API e gli id dei modelli di Undergrove. Non sono ancora nei registri di pacchetti; ognuno si compila da packages/ nel repository del sito.

    I client Rust, Java e Kotlin e l’app per Make usano invece l’API per sviluppatori, perché non esiste un SDK di Tinfoil su cui basarli: il gateway legge ogni richiesta in memoria, verifica da sé l’enclave e riporta ciò che ha verificato negli header della risposta.

    PacchettoPerCosa fa
    @undergrove/sdkNodeUn client in stile OpenAI con streaming, strumenti, embedding, trascrizione e sintesi vocale.
    undergrovePythonIl client Python, con LangChain (ChatUndergrove) e LlamaIndex (UndergroveLLM) come extra.
    GoGoUn client basato sul verificatore Go di Tinfoil, con streaming e chiamate agli strumenti.
    UndergroveSwiftUn pacchetto Swift per macOS 14 e iOS 17, basato sul verificatore Swift di Tinfoil.
    @undergrove/ai-sdk-providerVercel AI SDKgenerateText, streamText e strumenti, con ciò che è stato verificato nei metadati del provider di ogni risposta.
    @undergrove/langchainLangChain.jsUn modello di chat ed embedding.
    @undergrove/reactReactComponenti e hook per la chat, e un percorso server che tiene la chiave API fuori dal browser.
    create-undergrove-appPer iniziarenpm create undergrove-app: un’app di chat privata con deploy su Vercel in due comandi.
    @undergrove/agentsAgentiUn ciclo di chiamate agli strumenti che registra quale enclave verificata ha risposto a ogni passaggio, in una traccia in cui chiunque può controllare eventuali modifiche, con enclave fissate.
    undergroveRustUn client per l’API per sviluppatori, con streaming e chiamate agli strumenti, e le prove di attestazione del gateway per ogni risposta. Non cifra da sé le richieste per l’enclave.
    dev.undergrove:undergroveJava e KotlinUn client per l’API per sviluppatori, con streaming, strumenti ed embedding, e coroutine e un Flow per Kotlin. Non è la modalità cifrata: il gateway legge le richieste in memoria.
    undergrove[jupyter]JupyterIl cell magic %%undergrove, con le variabili del notebook e i DataFrame come contesto, e un widget di attestazione.
    @undergrove/officeExcel e Word=UNDERGROVE.ASK() in qualsiasi cella, e in Word riassumi, riscrivi o fai domande sulla selezione.
    @undergrove/officeOutlookUn riepilogo del messaggio aperto e una bozza di risposta che controlli prima di inviarla.
    undergrove-obsidianObsidianFai domande sulla nota aperta, riassumi, riscrivi e continua a scrivere, con l’enclave verificata sotto ogni risposta. Solo desktop.
    Undergrove for AlfredAlfredScrivi ug e una domanda: la risposta in Large Type e negli appunti, cifrata tramite la CLI undergrove.
    n8n-nodes-undergroven8nUn nodo della community: interroga un modello, crea embedding ed elenca i modelli, con ciò che è stato verificato su ogni elemento.
    Undergrove for ZapierZapierAzioni Ask a Model e Create Embeddings, con ogni richiesta cifrata per l’enclave dal processo dello Zap.
    Undergrove for MakeMakeUn’app personalizzata basata sull’API per sviluppatori: il gateway verifica l’enclave e riporta ciò che ha verificato nell’output di ogni modulo.
    Undergrove Enclave PinGitHub ActionsUn passaggio che verifica l’enclave sul runner con il verificatore di Tinfoil e fa fallire il job quando non è quella che hai fissato.

    Con il 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);

    Con i componenti React e il progetto di partenza, i messaggi passano dalla pagina al tuo server, che li sigilla; chi gestisce quel server può leggerli, come in qualsiasi integrazione lato server.

    Test nella CI

    Il sostituto con cui vengono testati i pacchetti risponde alle chiavi sandbox: qualsiasi chiave che inizia con ugk_sandbox_. I tuoi test possono usarne una senza segreti né addebiti, e Undergrove stesso le rifiuta con sandbox_key. Le sue risposte sono preimpostate, non di un modello. In GitHub Actions, un passaggio lo avvia e imposta 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

    Prossimi passi

    DisponibileWorkspace 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.
    AvantiPrimo percorso di modello, accesso all’alpha privata, limiti di utilizzo condivisi.
    Pronto, non ancora attivatoChiavi self-service, piani a pagamento e credito prepagato per l’utilizzo oltre la quota giornaliera.
    PrevistoAccesso per i team.
    Richiede verificaValutazione di sicurezza indipendente dello spazio di lavoro, del relay e del gateway.