Mit Klarheit entwickeln.
Eine OpenAI-kompatible API mit veröffentlichten Modellrouten und klarer Datenverarbeitung.
Wo wir stehen
Die API ist gebaut und getestet. Bis die erste Modellroute öffnet, liefern Chat-Anfragen 503 inference_not_configured. Den aktuellen Stand finden Sie immer unter https://undergrove.vercel.app/api/status.
Sie folgt dem Format der OpenAI-API, vorhandene SDKs funktionieren also, wenn Sie die Basis-URL ändern: Chat mit Bildern, Function Calling und strukturierten Ausgaben, dazu Embeddings, Transkription und Sprachausgabe. Damit Anfragen selbst für unser Gateway unlesbar bleiben, nutzen Sie den verschlüsselten Modus, wie der Arbeitsbereich.
Schnellstart
Basis-URL: https://undergrove.vercel.app/v1. Modelle mit GET /v1/models auflisten, dann eine Antwort streamen:
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."}]
}'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
Die Antwort bisher
Mit einer Zeile von OpenAI wechseln
Undergrove spricht die OpenAI-API, die offiziellen SDKs funktionieren also unverändert. Geben Sie dem Client in der einen Zeile, die ihn erstellt, die Basis-URL und einen Schlüssel von Undergrove, und nennen Sie dann eines der hier angebotenen offenen Modelle.
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)
2 Zeilen ändern sich: Basis-URL und Schlüssel des Clients sowie das Modell.
Oder ändern Sie gar keinen Code: Beide SDKs lesen OPENAI_BASE_URL und OPENAI_API_KEY, wenn der Client ohne sie erstellt wird; setzen Sie diese auf https://undergrove.vercel.app/v1 und Ihren Schlüssel, wechselt eine App unverändert. Streaming, Bilder, Function Calling und strukturierte Ausgaben funktionieren wie unten beschrieben, bei den Modellen, die sie unterstützen.
Entwicklerwerkzeuge
Clients, die die Enklave prüfen und auf Ihrem eigenen Rechner verschlüsseln. Jeder prüft zuerst die Attestierung der Tinfoil-Enklave und verschlüsselt dann jede Anfrage für diese Enklave, sodass das Relay von Undergrove sie weiterreicht, ohne sie lesen zu können. Sie liegen im Ordner packages/ des Repositorys, werden gegen eine lokale Nachbildung getestet und sind noch in keiner Registry veröffentlicht.
Die CLI ausprobieren
Dieses Terminal spielt den echten Befehl undergrove nach. Seine Ausgabe wurde gegen die lokale Nachbildung aufgezeichnet, mit der die Pakete getestet werden: verify ist eine echte Prüfung einer aufgezeichneten Tinfoil-Attestierung, models listet den aktuellen Katalog, und die Antworten auf ask sind geskriptet. Hinter dieser Seite steht kein Modell, und nichts, was Sie eingeben, verlässt sie.
Das Terminal lädt, sobald Sie es erreichen.
Beispiel-Apps
Drei kleine Web-Apps in packages/examples, jede eine Seite und ein Node-Server, der den Schlüssel hält. Ohne Schlüssel laufen sie gegen die lokale Nachbildung mit geskripteten Antworten, und nichts verlässt Ihren Computer; mit gesetztem UNDERGROVE_API_KEY prüft derselbe Code die Enklave und verschlüsselt jede Anfrage für sie.
Ein Chat, der aus einer Hilfecenter-Datei antwortet, als Server-Sent Events an die Seite gestreamt.
npm run support-botin packages/examples; der Quellcode liegt 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 });
}
}In Ihrem Editor und in CI
- Node-SDK@undergrove/sdk: Chat im OpenAI-Stil, Streaming, Embeddings, Transkription und Sprachausgabe, standardmäßig verschlüsselt.
packages/sdk-js - Python-SDKundergrove, auf Basis des Python-SDK von Tinfoil, ebenfalls standardmäßig verschlüsselt.
packages/sdk-python - CLIundergrove ask, models und verify, mit dem Schlüssel im Schlüsselbund des Systems. Für Editoren: --events streamt JSON-Zeilen, acp startet es als Agent und mcp als Werkzeuge.
packages/cli - VS CodeEin Chat-Panel sowie Explain selection, Fix this (mit Apply fix) und Write tests für den Code, den Sie gerade vor sich haben.
packages/vscode - JetBrains IDEsEin Chat im Werkzeugfenster und dieselben drei Aktionen in IntelliJ IDEA, PyCharm und den übrigen IDEs, mit dem Schlüssel im Passwortspeicher der IDE.
packages/jetbrains - ZedUndergrove als Agent im Agent Panel, mit /explain, /fix, /tests und /review, oder als Werkzeuge für den eigenen Agenten von Zed. Beides nutzt die CLI, die auf Ihrem Rechner verschlüsselt.
packages/zed - Neovim:UndergroveAsk sowie :UndergroveExplain auf einer visuellen Auswahl, wobei die Antwort in ein schwebendes Fenster streamt.
packages/neovim - RaycastAsk Undergrove und Ask About Selected Text in jeder App.
packages/raycast - GitHub ActionUndergrove Review: ein Review pro Pull Request, mit Zeilenkommentaren und Vorschlägen und der Attestierung in der Fußzeile.
packages/github-action
Playground
Senden Sie von dieser Seite mit Ihrem eigenen Schlüssel eine Chat-Anfrage und sehen Sie die Antwort, ihre Nutzung und die Rohantwort, dazu dieselbe Anfrage als curl. Sie geht von Ihrem Browser an /v1 dieser Website, wie jeder andere API-Aufruf, und verbraucht wie gewohnt Ihr Kontingent oder Guthaben.
Wählen Sie „Verschlüsselt“, um sie so zu senden wie der verschlüsselte Modus der SDKs: Das SDK von Tinfoil verifiziert die Enklave in dieser Seite und versiegelt die Anfrage dann für sie. Der Playground zeigt die Bytes, die die Seite tatsächlich verlassen haben, was zurückkam, und den Beleg für die Enklave, für die versiegelt wurde.
Dieselbe Anfrage mit 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": "Hilf mir, eine Idee zu erkunden."
}
],
"stream": true
}'Authentifizierung
Senden Sie einen API-Schlüssel von Undergrove als Bearer-Token. Schlüssel beginnen mit ugk_ und werden während der Alpha auf Einladung ausgegeben. Wo Self-Service-Schlüssel eingeschaltet sind, erstellen Alpha-Mitglieder bis zu fünf in den Einstellungen des Arbeitsbereichs, und jeder verbraucht Kontingent und Guthaben seines Kontos. Wir speichern nur einen Einweg-Hash jedes Schlüssels; ein verlorener Schlüssel lässt sich daher nicht wiederherstellen, nur ersetzen.
Jeder Self-Service-Schlüssel kann eigene Limits haben, beim Erstellen oder später in den Einstellungen festgelegt: ein Tageslimit für Ausgaben, nach dessen Erreichen seine Anfragen bis Mitternacht UTC abgewiesen werden, und eine Liste der Modelle, die er nutzen darf. Ein begrenzter Schlüssel zählt weiterhin gegen das Kontingent seines Kontos. Schlüssel, die vor Einführung der Limits erstellt wurden, haben keine und funktionieren wie bisher.
curl https://undergrove.vercel.app/v1/models \
-H "Authorization: Bearer $UNDERGROVE_API_KEY"Endpunkte
| Endpunkt | Zweck |
|---|---|
| GET /v1/models | Die jetzt verfügbaren Modelle, jeweils mit Typ (Chat, Embedding, Transkription oder Sprachausgabe), Lizenz, Kontextgröße, Preisen, Reasoning-Stufen, Unterstützung für Bilder, Werkzeuge und Websuche, Tempo (schnell oder überlegt) und Ausführungsort. |
| POST /v1/chat/completions | Chat, streamed or as a single response, with images, function calling and structured outputs where the model supports them. |
| POST /v1/embeddings | Vektoren für Suche und Ähnlichkeit aus Nomic Embed Text v1.5 (768 Dimensionen). Bis zu 256 Eingaben. $0.002 je 48 Eingaben oder 67.200 Zeichen, je nachdem, was mehr ist. |
| POST /v1/audio/transcriptions | Transkripte von Whisper Large v3 Turbo. Multipart-Upload bis 4,4 MB, mit optionaler Sprache und Prompt. $0.01 je angefangenem Megabyte. |
| POST /v1/audio/speech | WAV-Audio von Qwen3-TTS mit neun Stimmen: aiden, dylan, eric, ono_anna, ryan, serena, sohee, uncle_fu und vivian. Bis zu 4.096 Zeichen. $0.005 je angefangenen 600 Zeichen. |
| GET /api/status | Dienststatus, Nutzungslimits und das vollständige Modellregister. |
Antworten haben die Standardform chat.completion. Streams senden chat.completion.chunk-Ereignisse und enden mit data: [DONE]. Reasoning-Modelle streamen ihr Reasoning getrennt in delta.reasoning_content, und der letzte Chunk meldet die Token-Nutzung. Bricht ein Stream ab, ist das letzte Ereignis ein Fehlerobjekt statt [DONE], sodass eine Teilantwort nie für eine vollständige gehalten wird.
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"]
}'Parameter
| Feld | Typ | Standard | Bedeutung |
|---|---|---|---|
model | string | Erforderlich | Eine id aus /v1/models. |
messages | array | Erforderlich | Bis zu 200 system-, user-, assistant- und tool-Nachrichten. Bei Modellen mit Bilderkennung können user-Nachrichten bis zu 16 image_url-Teile als Base64-Data-URLs enthalten (PNG, JPEG, GIF oder WebP); Links werden nicht abgerufen. Jedes Bild zählt als 3.000 Eingabe-Tokens, sofern das Modell nichts anderes meldet. |
tools, tool_choice, parallel_tool_calls | array, string | object, boolean | — | Function Calling für Modelle mit tools in /v1/models: bis zu 128 Funktionen. Gestreamte Aufrufe kommen als tool_calls-Deltas, und die Antwort endet mit finish_reason tool_calls. Senden Sie Ergebnisse als tool-Nachrichten mit ihrer tool_call_id zurück. |
response_format | object | — | Strukturierte Ausgaben: {"type": "json_object"} oder json_schema mit Name und Schema. |
stream | boolean | false | Senden Sie true, um die Antwort als Server-Sent Events zu streamen. |
max_tokens | integer | Das Limit des Modells | Auch als max_completion_tokens akzeptiert. Begrenzt auf das Ausgabelimit des Modells. |
temperature, top_p | number | Vom Modell bestimmt | Sampling-Steuerung: 0–2 und 0–1. |
reasoning_effort | string | Vom Modell bestimmt | Wie lange ein Reasoning-Modell vor dem Antworten nachdenkt. Zulässige Werte unterscheiden sich je nach Modell und stehen in /v1/models; Schnell und Gründlich im Arbeitsbereich nutzen die dort genannten Werte fast und thorough. |
web_search_options | object | — | Senden Sie {}, damit das Modell im Web sucht. Anfragen werden auf persönliche Angaben und Ergebnisse auf Prompt-Injection geprüft. Quellen kommen als url_citation-Annotationen. Jede Suchanfrage kostet $0.05 Ihres Kontingents. |
stop | string | string[] | — | Ein String oder bis zu vier Strings. |
presence_penalty, frequency_penalty, seed | number, integer | — | Werden weitergegeben, wenn die Modellroute sie unterstützt. |
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"]
}
}
}]
}'Verschlüsselter Modus
Die Standard-API liest unser Gateway im Arbeitsspeicher, um jede Anfrage zu prüfen. Damit Anfragen auch für Undergrove unlesbar bleiben, nutzen Sie das SDK von Tinfoil mit Ihrem Schlüssel. Es prüft die Hardware-Attestierung der Enklave und verschlüsselt dann jede Anfrage so, dass nur diese Enklave sie lesen kann. Unser Relay prüft Ihren Schlüssel und Ihr Kontingent und reicht den Chiffretext weiter.
- Ihr CodeDas SDK von Tinfoil prüft die Enklave und verschlüsselt dann jede Anfrage für sie.Kann die Anfrage lesen
- Für die Enklave versiegeltUndergrove-RelayPrüft Schlüssel und Kontingent und gibt den Geheimtext weiter. Kann ihn nicht lesen.Sieht nur Geheimtext
- Weiterhin versiegeltModell-EnklaveDer einzige Ort, an dem die Anfrage entschlüsselt wird und das Modell läuft.Kann die Anfrage lesen
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);Derselbe Client erreicht embeddings, audio/transcriptions und audio/speech. Da das Relay diese Anfragen nicht lesen kann, werden Modelle über ihre enclave_model-id aus /v1/models benannt, Antworten kommen im Format von Tinfoil zurück, und die Nutzung wird aus der Größe der verschlüsselten Daten geschätzt. Kontingente und Guthaben gelten wie gewohnt.
MCP-Server
Undergrove ist auch ein Model-Context-Protocol-Server, unter https://undergrove.vercel.app/mcp. Jedes Live-Modell ist ein Werkzeug, etwa ask_<model>, das MCP-Clients wie Claude Code, Cursor und Claude Desktop mit Ihrem API-Schlüssel aufrufen können. Ein Werkzeug nimmt einen prompt und optional system, max_tokens sowie, bei Modellen mit Suche, web_search, und gibt die Antwort des Modells als Text zurück.
Ein Werkzeugaufruf ist eine API-Anfrage wie jede andere: Das Gateway liest ihn im Arbeitsspeicher, sendet ihn über die veröffentlichte Route des Modells, bewahrt keine Kopie auf und rechnet ihn auf Limits, Kontingent und Guthaben Ihres Schlüssels an. Der Server nutzt Streamable HTTP, antwortet in JSON und hält keine Sitzungen. Er unterstützt die Protokollversionen 2025-11-25, 2025-06-18 und 2025-03-26.
Claude Code
Einmal im Terminal ausführen:
claude mcp add --transport http undergrove https://undergrove.vercel.app/mcp \
--header "Authorization: Bearer ugk_your_key"Cursor
In ~/.cursor/mcp.json oder in einem Projekt in .cursor/mcp.json:
{
"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 erreicht entfernte Server über mcp-remote, das Node.js benötigt:
{
"mcpServers": {
"undergrove": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://undergrove.vercel.app/mcp",
"--header",
"Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer ugk_your_key"
}
}
}
}Jeder Client
Oder rufen Sie ihn direkt auf. Er spricht JSON-RPC über Streamable HTTP:
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"}'Rufen Sie dann das Werkzeug eines Modells auf:
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."}
}
}'Umgekehrt kann der Arbeitsbereich Werkzeuge von MCP-Servern nutzen, die Sie in den Einstellungen verbinden. Diese Aufrufe gehen im Klartext über den Server von Undergrove an den verbundenen Server, nutzen Sie also Server, denen Sie mit dem anvertrauen, was die Werkzeuge erhalten.
Fehler und Limits
Fehler nutzen das OpenAI-Format: {"error": {"message", "type", "code"}}.
| Status | Codes |
|---|---|
| 400 | invalid_request, invalid_json, unsupported_parameter, unsupported_content, context_length_exceeded, model_rejected_request, unsupported_api_version, async_needs_webhook, async_stream, invalid_webhook_url |
| 401 | authentication_required, invalid_api_key, invalid_session, sandbox_key (ein ugk_sandbox_-Schlüssel, den nur der lokale Stand-in beantwortet) |
| 403 | alpha_access_required, model_not_allowed_for_key (der Schlüssel ist auf andere Modelle beschränkt) |
| 404 | model_not_found, webhook_not_found, thread_not_found, assistant_not_found, prompt_not_found, version_not_found |
| 409 | idempotency_in_progress (die erste Anfrage mit diesem Idempotency-Key läuft noch), version_conflict (ein versiegeltes Element hat sich seit dem Lesen geändert), id_taken |
| 413 | request_too_large (Chat und Transkription über 4,4 MB, Embeddings über 1 MB, Sprachausgabe über 100 KB) |
| 422 | idempotency_key_reused (derselbe Idempotency-Key mit einer anderen Anfrage) |
| 429 | rate_limited, budget_exhausted, capacity_exhausted, key_budget_exceeded (der Schlüssel hat sein eigenes Tageslimit erreicht; die Meldung nennt den Zeitpunkt des Zurücksetzens). Warten Sie, was der Retry-After-Header angibt, laden Sie in den Einstellungen des Arbeitsbereichs Guthaben auf oder erhöhen Sie dort das Limit des Schlüssels. |
| 502 | model_unavailable, stream_interrupted |
| 503 | capacity_exhausted (mit Retry-After), inference_not_configured, realtime_not_available (eine Audio-Route ist nicht attestiert) |
| 504 | model_timeout |
Während der Alpha hat jeder Schlüssel und jedes Konto ein Kontingent pro Minute, für tägliche Anfragen und für tägliche Ausgaben. Die aktuellen Werte stehen unter /api/status.
HTTP/2 429
retry-after: 41
{
"error": {
"message": "You’re sending messages quickly. Please wait a moment.",
"type": "rate_limit_error",
"code": "rate_limited"
}
}Wissen, wohin Ihre Daten gehen
- Ihr ServerSendet die Anfrage mit Ihrem Schlüssel.Kann die Anfrage lesen
- HTTPSUndergrove-GatewayPrüft Schlüssel und Limits, liest die Anfrage im Arbeitsspeicher und behält keine Kopie.Kann die Anfrage lesen
- Für die geprüfte Enklave verschlüsseltModell-EnklaveDie Hardware-Enklave von Tinfoil führt das Modell auf der veröffentlichten Route aus.Kann die Anfrage lesen
Das Gateway prüft Ihren Schlüssel, wendet Limits an und leitet nur die oben genannten Felder an die Modellroute weiter. Es bewahrt keine Kopie von Prompts oder Antworten auf und protokolliert sie nicht. Seine Logs erfassen nur Routennamen, Statuscodes und Zeiten. Zähler für Ratenlimits nutzen einen Einweg-Hash Ihres Schlüssels oder Kontos und verfallen innerhalb von zwei Tagen.
Betreiber, Standort und Aufbewahrung jeder Modellroute sind im Modellregister unter /api/status und auf der Datenschutzseite veröffentlicht. Anfragen weichen nie auf eine andere Route aus. Vercel, unser Hoster, bewahrt übliche Anfrage-Metadaten wie IP-Adressen auf.
Auf Confidential-Computing-Routen prüft das API-Gateway vor dem Verbinden die Hardware-Attestierung der Enklave und verschlüsselt jede Anfrage so, dass nur diese Enklave sie lesen kann. Das Gateway selbst liest API-Anfragen weiterhin im Arbeitsspeicher, um sie zu prüfen.
Daten im Arbeitsbereich
Synchronisierung, geteilte Links und geteilte Projekte speichern nur Geheimtext, den der Browser erzeugt hat. Jedes Diagramm zeigt Schritt für Schritt, wer den Schlüssel hat.
Was die Prüfung belegt
Bevor etwas gesendet wird, führt der Arbeitsbereich den Open-Source-Prüfer von Tinfoil in Ihrem Browser aus. Er prüft den Hardware-Attestierungsbericht der Enklave anhand der Signaturschlüssel von AMD oder Intel, prüft, dass der in der Enklave gemessene Code einem von Tinfoil auf GitHub veröffentlichten und signierten Release entspricht, und prüft, dass der verwendete Verschlüsselungsschlüssel zu dieser Enklave gehört. Wählen Sie im Arbeitsbereich Geprüft, um jede Prüfung, das Release und die Messung zu sehen.
Sie belegt, dass Ihre Nachrichten nur von dieser Hardware mit diesem veröffentlichten Code entschlüsselt werden können. Sie belegt nicht, dass der veröffentlichte Code fehlerfrei ist, schützt nicht vor Schwächen der Prozessoren selbst und erfasst nicht, was Sie mit Antworten tun, sobald sie Ihren Browser erreichen. Der Code des Arbeitsbereichs wird von Undergrove ausgeliefert, Sie vertrauen also darauf, dass wir den Prüfer so ausliefern, wie er veröffentlicht ist; Diese Seite prüfen vergleicht den Code, den Ihr Browser geladen hat, mit dem Build-Manifest, und npm run verify:site vergleicht die Website mit Ihrem eigenen Build desselben Commits. Eine unabhängige Prüfung ist geplant.
Jede Antwort aus einer Enklave hat eine Schaltfläche Beleg. Sie zeigt die Enklave, das Release und die Messwerte, die vor dem Senden dieser Anfrage geprüft wurden, sowie den Schlüssel, für den verschlüsselt wurde, und lädt sie als JSON herunter. Bis der Tab geschlossen wird, enthält der Download den Attestierungsbericht, sodass Sie den Prüfer später erneut darauf anwenden können; danach enthält er den Hash des Berichts und wo der aktuelle Bericht der Enklave abgerufen werden kann. Der Beleg ist die Aufzeichnung Ihres Browsers, keine signierte Erklärung, und die Enklave, die er nennt, ist der Modell-Router von Tinfoil; der Router gibt Anfragen an die eigene Enklave des Modells weiter. Der Beleg nennt auch diese Enklave, und Ihr Browser versucht, sie auf dieselbe Weise zu prüfen. Die meisten Modell-Enklaven laufen auf Intel TDX, das der Prüfer im Browser noch nicht prüfen kann; in diesen Fällen sagt der Beleg das, und die Prüfung stützt sich auf den Router, dessen veröffentlichter Code jede Modell-Enklave prüft, bevor er sie nutzt.
# 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Überprüfbare Antworten
Bei Modellen, die in einer Enklave laufen, nennt jede API-Antwort die Enklave, die das Gateway vor dem Senden Ihrer Anfrage geprüft hat, und GET /v1/attestation liefert das zugehörige Attestierungspaket, sodass Sie den Open-Source-Prüfer von Tinfoil auf denselben Nachweis anwenden können.
| Header | Wert |
|---|---|
x-undergrove-attestation | verified-by-gateway |
x-undergrove-enclave-host | Die attestierte Enklave: der Modell-Router von Tinfoil |
x-undergrove-enclave-measurement | Die Code-Messung, abgeglichen mit einem signierten Release |
x-undergrove-enclave-release | Das Release-Tag (und -release-digest, sein veröffentlichter Digest) |
x-undergrove-enclave-key | Der öffentliche Schlüssel der Enklave, für den die Anfrage verschlüsselt wurde |
x-undergrove-attestation-sha256 | Der SHA-256 des Attestierungsberichts |
x-undergrove-attestation-url | Wo das Paket abgerufen wird: /v1/attestation?sha256=… |
Lesen Sie sie mit jedem Client aus. Mit dem OpenAI-Python-SDK fordern Sie die Rohantwort an:
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-'Um eine Antwort zu prüfen, sendet dieses Node-Skript eine Anfrage, ruft das in den Headern genannte Paket ab, prüft es mit @tinfoilsh/verifier und kontrolliert, dass Schlüssel, Messung, Enklave und Berichts-Hash übereinstimmen. Der Playground oben tut dasselbe in Ihrem Browser mit In diesem Browser prüfen.
verify-response.mjs
// 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)}…).`,
);
Was eine bestandene Prüfung zeigt
Dass eine Enklave mit diesem Schlüssel und diesem gemessenen Code existierte: echte Confidential-Computing-Hardware von AMD oder Intel, die das von Tinfoil auf GitHub veröffentlichte und signierte Release ausführt. Und dass das Gateway angibt, diese Enklave geprüft und Ihre Anfrage vor der Weiterleitung mit ihrem Schlüssel verschlüsselt zu haben.
Was sie nicht zeigt
Dass das Gateway Ihre Anfrage tatsächlich mit diesem Schlüssel verschlüsselt hat. Die Header sind der Bericht des Gateways, keine von der Enklave signierte Erklärung, und das Gateway liest API-Anfragen im Arbeitsspeicher, um sie zu prüfen. Wenn Sie sich stattdessen auf Ihre eigene Prüfung verlassen wollen, nutzen Sie den verschlüsselten Modus, in dem Ihr Client die Enklave prüft und jede Anfrage selbst verschlüsselt. Die genannte Enklave ist der Modell-Router von Tinfoil, der Anfragen an die eigene Enklave des Modells weitergibt.
Website-Assistent
Setzen Sie mit einem einzigen Script-Tag einen Assistenten auf Ihre eigene Website. Er fügt eine Schaltfläche hinzu, die ein Chat-Panel öffnet, beantwortet vom offenen Modell Ihrer Wahl, innerhalb eines Tageslimits für Ausgaben, das Sie festlegen. Das Panel ist ein Frame, den Undergrove ausliefert, Ihre Seite kann also nicht lesen, was Besucher eintippen.
- Öffnen Sie im Arbeitsbereich die Einstellungen und erstellen Sie unter Website-Assistent einen veröffentlichbaren Schlüssel: Name, Modell, die Websites, die ihn nutzen dürfen, und ein Tageslimit.
- Kopieren Sie den angezeigten Code und fügen Sie ihn auf jeder Seite vor
</body>ein. - Probieren Sie es zuerst auf der Demoseite aus, einem erfundenen Laden.
<script
src="https://undergrove.vercel.app/embed.js"
data-key="ugp_…"
data-model="<model-id>"
data-instructions="Wir sind eine Töpferwerkstatt. Hilf bei Kursen, Öffnungszeiten und Versand."
async
></script>| Attribut | Typ | Standard | Bedeutung |
|---|---|---|---|
data-key | string | Erforderlich | Ihr veröffentlichbarer Schlüssel, ugp_… |
data-model | string | Erforderlich | Eines der Modelle, die der Schlüssel nennt. |
data-title | string | “Ask a question” | Der Titel des Panels. |
data-greeting | string | — | Die erste Nachricht, die Besucher sehen. |
data-instructions | string | — | Worum es auf Ihrer Website geht, wobei geholfen werden soll und in welchem Ton. Wird mit jeder Frage gesendet; Besucher können es in Ihrer Seite lesen. |
data-label | string | “Ask” | Der Text der Schaltfläche. |
data-position | string | right | right oder left. |
data-theme | string | auto | auto (das Gerät des Besuchers), light oder dark. |
data-open | boolean | false | true öffnet das Panel, wenn die Seite lädt. |
Warum ein veröffentlichbarer Schlüssel in einer öffentlichen Seite stehen darf
- Er funktioniert nur für Chat und nur aus dem Assistenten-Frame von Undergrove. Überall sonst weist die API ihn ab.
- Er funktioniert nur, solange der Frame in eine der von Ihnen genannten Websites eingebettet ist. Der Browser meldet, welche Seite den Frame einbettet, und der Server prüft das.
- Er hat immer ein Tageslimit für Ausgaben und eine Modellliste. Ein Programm außerhalb eines Browsers kann sich als beliebige Website ausgeben, behandeln Sie den Schlüssel also als öffentlich und setzen Sie das Limit auf das, was Sie an einem Tag ausgeben möchten.
- Für jeden Besucher gilt ein Fragenlimit pro Minute und pro Tag, veröffentlicht unter /api/status. Besucher werden über einen verschlüsselten Einweg-Hash ihrer IP-Adresse gezählt, nie über die Adresse selbst, und die Zähler verfallen innerhalb von zwei Tagen.
Antworten verbrauchen das Kontingent und Guthaben Ihres Kontos, wie bei jedem Schlüssel. Bei Modellen, die in einer Enklave laufen, verschlüsselt der Frame jede Nachricht im Browser des Besuchers für die geprüfte Enklave, wie der Arbeitsbereich; andere Modelle werden über das Gateway erreicht, das Anfragen im Arbeitsspeicher liest und keine Kopie aufbewahrt. Ihre Website sieht beides nicht.
Badge „Verified by Undergrove“
Eine Website mit Assistent kann ein kleines Badge zeigen, das auf die Seite von Undergrove zu ihrem Schlüssel verlinkt. Diese Seite liest die Websites und Modelle des Schlüssels vom Server von Undergrove, prüft die Enklave dahinter im Browser des Besuchers und sagt, was das Badge behauptet und was nicht. Setzen Sie das Tag dort ein, wo das Badge erscheinen soll.
Oder, ganz ohne Skript, ein Bild und ein Link (badge-dark.svg für dunkle Seiten):
<script src="https://undergrove.vercel.app/badge.js" data-key="ugp_…" async></script>- Das Badge ist ein Link. Es stellt von Ihrer Seite aus keine Anfragen und prüft selbst nichts; das übernimmt die Seite, die es öffnet.
- Es nennt Ihren veröffentlichbaren Schlüssel, der für den Assistenten ohnehin schon in Ihrer Seite steht. Die Badge-Seite zeigt die Websites und Modelle des Schlüssels, nie aber das Konto, das Limit oder die bisherigen Ausgaben.
- Der Link bittet den Browser, die Herkunft (Origin) Ihrer Website mitzusenden, damit die Seite Besuchern sagen kann, ob sie von einer der Websites des Schlüssels kamen. Wird das Badge auf eine andere Website kopiert, sagt die Seite das den Besuchern von dort.
- Es bürgt nur für den Assistenten, nicht für den Rest Ihrer Website.
Integrationen
Die meisten Clients von Undergrove prüfen die Attestierung der Enklave mit Tinfoils Prüfprogramm und verschlüsseln dann jede Anfrage für diese Enklave, sodass das Relay sie weitergibt, ohne sie lesen zu können. Jeder nimmt Ihren API-Schlüssel und verwendet die Modell-IDs von Undergrove. Sie sind noch nicht in Paket-Registrys; jeder wird aus packages/ im Repository der Website gebaut.
Die Clients für Rust, Java und Kotlin und die Make-App verwenden stattdessen die Entwickler-API, da es für sie kein Tinfoil-SDK gibt: Das Gateway liest jede Anfrage im Arbeitsspeicher, prüft die Enklave selbst und meldet in den Headern der Antwort, was es geprüft hat.
| Paket | Für | Was es tut |
|---|---|---|
@undergrove/sdk | Node | Ein Client im OpenAI-Stil mit Streaming, Werkzeugen, Embeddings, Transkription und Sprachausgabe. |
undergrove | Python | Der Python-Client, mit LangChain (ChatUndergrove) und LlamaIndex (UndergroveLLM) als Extras. |
Go | Go | Ein Client auf Basis des Go-Prüfers von Tinfoil, mit Streaming und Werkzeugaufrufen. |
Undergrove | Swift | Ein Swift-Paket für macOS 14 und iOS 17, auf Basis des Swift-Prüfers von Tinfoil. |
@undergrove/ai-sdk-provider | Vercel AI SDK | generateText, streamText und Werkzeuge, wobei die Provider-Metadaten jeder Antwort festhalten, was geprüft wurde. |
@undergrove/langchain | LangChain.js | Ein Chatmodell und Embeddings. |
@undergrove/react | React | Chat-Komponenten und Hooks sowie eine Serverroute, die den API-Schlüssel vom Browser fernhält. |
create-undergrove-app | Starter | npm create undergrove-app: eine private Chat-App, die sich mit zwei Befehlen auf Vercel bereitstellen lässt. |
@undergrove/agents | Agenten | Eine Schleife mit Werkzeugaufrufen, die festhält, welche geprüfte Enklave jeden Schritt beantwortet hat – in einem Protokoll, das jeder auf Änderungen prüfen kann –, mit festgelegten Enklaven. |
undergrove | Rust | Ein Client für die Entwickler-API mit Streaming und Werkzeugaufrufen und dem Attestierungsnachweis des Gateways zu jeder Antwort. Er verschlüsselt Anfragen nicht selbst für die Enklave. |
dev.undergrove:undergrove | Java und Kotlin | Ein Client für die Entwickler-API mit Streaming, Werkzeugen und Embeddings, dazu Coroutinen und ein Flow für Kotlin. Kein verschlüsselter Modus: Das Gateway liest Anfragen im Arbeitsspeicher. |
undergrove[jupyter] | Jupyter | Die Zellen-Magic %%undergrove mit Notebook-Variablen und DataFrames als Kontext und ein Attestierungs-Widget. |
@undergrove/office | Excel und Word | =UNDERGROVE.ASK() in jeder Zelle, und in Word die Auswahl zusammenfassen, umschreiben oder dazu fragen. |
@undergrove/office | Outlook | Eine Zusammenfassung der geöffneten Nachricht und ein Antwortentwurf, den Sie vor dem Senden prüfen. |
undergrove-obsidian | Obsidian | Fragen zur geöffneten Notiz, zusammenfassen, umschreiben und weiterschreiben, mit der geprüften Enklave unter jeder Antwort. Nur auf dem Desktop. |
Undergrove for Alfred | Alfred | ug und eine Frage eingeben: die Antwort in großer Schrift und in der Zwischenablage, verschlüsselt über die undergrove-CLI. |
n8n-nodes-undergrove | n8n | Ein Community-Node: ein Modell fragen, Embeddings erstellen und Modelle auflisten, mit dem, was zu jedem Element geprüft wurde. |
Undergrove for Zapier | Zapier | Die Aktionen Ask a Model und Create Embeddings, jede Anfrage aus dem Prozess des Zaps für die Enklave verschlüsselt. |
Undergrove for Make | Make | Eine eigene App auf der Entwickler-API: Das Gateway prüft die Enklave und meldet in der Ausgabe jedes Moduls, was es geprüft hat. |
Undergrove Enclave Pin | GitHub Actions | Ein Schritt, der die Enklave auf dem Runner mit Tinfoils Prüfprogramm prüft und den Job scheitern lässt, wenn sie nicht die festgelegte ist. |
Mit dem Vercel AI SDK:
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);Mit den React-Komponenten und dem Starter gehen Nachrichten von der Seite an Ihren eigenen Server, der sie versiegelt; wer diesen Server betreibt, kann sie lesen, wie bei jeder serverseitigen Integration.
Tests in CI
Der Stand-in, gegen den die Pakete getestet werden, beantwortet Sandbox-Schlüssel: jeden Schlüssel, der mit ugk_sandbox_ beginnt. Ihre Tests können einen ohne Geheimnis und ohne Kosten verwenden; Undergrove selbst lehnt sie mit sandbox_key ab. Seine Antworten sind vorgegeben, nicht die eines Modells. In GitHub Actions startet ihn ein Schritt und setzt UNDERGROVE_BASE_URL und UNDERGROVE_API_KEY:
- 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: 9134838da8aa4e1792566b2988820340356517c4db0c94261eb8154843e0357554ba72fcf185f950d9301fbede574c90Was als Nächstes kommt
| Verfügbar | Workspace 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. |
|---|---|
| Weiter | Erste Modellroute, Anmeldung zur privaten Alpha, gemeinsame Nutzungslimits. |
| Gebaut, noch nicht eingeschaltet | Self-Service-Schlüssel, Bezahltarife und Prepaid-Guthaben für Nutzung über das Tageskontingent hinaus. |
| Geplant | Teamzugang. |
| Erfordert Prüfung | Unabhängige Sicherheitsprüfung von Arbeitsbereich, Relay und Gateway. |