
Credential-Isolation für KI-Agenten. Agenten sehen niemals echte API-Schlüssel - strukturelle Garantie, keine Richtlinie.
Eine Credential-Firewall für KI-Agenten.
Die Kernaussage ist strukturell, nicht politisch: Agenten erhalten Platzhalter-Token, niemals echte API-Schlüssel. Der echte Schlüssel überschreitet nur eine Netzwerkschnittstelle — innerhalb des wardn-Proxys auf dem Weg zur vorgelagerten API — und wird aus den Antworten entfernt, bevor sie den Agenten erreichen. Logs, Umgebung, LLM-Kontextfenster, temporäre Dateien und Shell-Verlauf enthalten nur Platzhalter.```text agent process OPENAI_KEY=wdn_placeholder_a1b2c3d4e5f6g7h8 (useless) agent logs Authorization: Bearer wdn_placeholder_a1b2... (useless) LLM context wdn_placeholder_a1b2c3d4e5f6g7h8 (useless) wardn proxy injects the real key in-flight, single seam (deleted on response) ~/.vibeguard/vault.enc AES-256-GCM(Argon2id(passphrase)) (encrypted at rest)
Dies ist die tragende Behauptung und sie ist heute gegen Agentenkompromittierung, Prompt-Injection, Log-Diebstahl und Skill-Exfiltration verteidigbar.
Lesen Sie [docs/THREAT-MODEL.md](https://github.com/rohansx/wardn/blob/HEAD/docs/THREAT-MODEL.md) für die ehrliche Aufteilung zwischen dem, was abgedeckt ist und was nicht – einschließlich der Stufe, bei der die stärkere Behauptung „Host-Kompromittierung gibt nichts preis“ erreichbar wird.
Der Tresor selbst (verschlüsselt im Ruhezustand, passphrasenabgeleiteter Schlüssel) ist eine echte Komponente und der Grund, warum die Firewall auf einem einzelnen Rechner laufen kann.
Die kommende Stufe [docs/HOSTED-TIER.md](https://github.com/rohansx/wardn/blob/HEAD/docs/HOSTED-TIER.md) umhüllt den Proxy zusätzlich in einer vertraulichen Rechenenklave, sodass selbst ein vollständig kompromittierter VPS den Schlüssel nicht lesen kann.
[](https://crates.io/crates/wardn)
[](LICENSE)
## Das Problem
Jedes KI-Agenten-Framework speichert heute API-Schlüssel in Umgebungsvariablen oder `.env`-Dateien. Ein kompromittierter Agent, eine bösartige Fähigkeit, ein handelsüblicher Stealer oder eine Prompt-Injection, die `Authorization: Bearer sk-...` aus einem LLM-Log exfiltriert, erhält vollen Zugriff auf Ihre Anmeldeinformationen.```
~/.env → OPENAI_KEY=sk-proj-real-key # plaintext, readable by anyone
agent context → "Use OPENAI_KEY=sk-proj-real-key" # leaked into LLM context window
agent logs → Authorization: Bearer sk-proj-... # sitting in log files
wardn übergibt Agenten einen nutzlosen Platzhalter-String und entfernt den echten Schlüssel von jeder Oberfläche, die es erreichen kann. Echte Schlüssel werden auf der Netzwerkschicht injiziert — eine einzelne Naht — und aus Antworten entfernt, bevor sie den Agenten erreichen.``` agent environment → OPENAI_KEY=wdn_placeholder_a1b2c3d4e5f6g7h8 (useless) wardn vault → OPENAI_KEY=sk-proj-real-key (encrypted at rest) upstream request → Authorization: Bearer sk-proj-real-key (network transit only) upstream response → ...real keys stripped, placeholders returned... (re-injected on the way back) agent logs → Authorization: Bearer wdn_placeholder_a1b2... (useless) LLM context window → wdn_placeholder_a1b2c3d4e5f6g7h8 (useless)
## Architektur```mermaid
flowchart TB
subgraph Agent["AI Agent Process"]
A1["Agent Code"]
A2["ENV: OPENAI_KEY=wdn_placeholder_a1b2..."]
end
subgraph Wardn["wardn daemon · localhost:7777"]
direction TB
P["HTTP Proxy"]
MCP["MCP Server\n(stdio)"]
subgraph Pipeline["Request Pipeline"]
direction LR
S1["Identify\nAgent"] --> S2["Resolve\nPlaceholder"] --> S3["Check\nAuth"] --> S4["Rate\nLimit"] --> S5["Inject\nReal Key"]
end
subgraph ResponsePipeline["Response Pipeline"]
direction RL
R1["Strip Real\nKeys"] --> R2["Replace with\nPlaceholders"]
end
subgraph Vault["Encrypted Vault"]
V1["AES-256-GCM"]
V2["Argon2id KDF"]
V3["Placeholder Map\nper agent × credential"]
end
end
subgraph External["External APIs"]
E1["api.openai.com"]
E2["api.anthropic.com"]
E3["..."]
end
A1 -- "placeholder token\nin headers/body" --> P
A1 -. "MCP: get_credential_ref\nlist_credentials\ncheck_rate_limit" .-> MCP
MCP -. "placeholder token\n(never real keys)" .-> A1
P --> Pipeline
Pipeline --> External
External --> ResponsePipeline
ResponsePipeline -- "response with\nplaceholders only" --> A1
Pipeline <--> Vault
ResponsePipeline <--> Vault
style Agent fill:#1a1a2e,stroke:#e94560,color:#fff
style Wardn fill:#0f3460,stroke:#16213e,color:#fff
style Pipeline fill:#16213e,stroke:#e94560,color:#fff
style ResponsePipeline fill:#16213e,stroke:#e94560,color:#fff
style Vault fill:#1a1a2e,stroke:#00d2ff,color:#fff
style External fill:#0a0a0a,stroke:#533483,color:#fff
Agent sends request with placeholder in Authorization header │ ▼ ┌─────────────────────────┐ │ wardn proxy │ │ localhost:7777 │ │ │ │ 1. Identify agent │ │ 2. Resolve placeholder │ │ 3. Check authorization │ │ 4. Check rate limit │ │ 5. Inject real key │ │ 6. Forward request │ │ 7. Strip key from resp │ │ 8. Return to agent │ └─────────────────────────┘ │ ▼ External API (only place real key exists in transit)
## Demo
<p align="center">
<img src="https://assets.kitploit.com/production/public/readmes/12823/1fa6109ffd855ec98c173c5edd2d7ee77f6b0c918a3cdecb1ea5fbfe8326161d.gif" alt="wardn demo" width="800">
</p>
## Vertrauensstufen, ehrlich
| Stufe | Wo | Was gilt |
|---|---|---|
| **Selbsthosting (heute)** | Ihr Laptop, Ihre VPS, CI | Verschlüsseltes Ruhevault, Firewall-Anspruch gegen Agents. **Schützt nicht** vor Root auf dem Host. |
| **Gehostet (in Kürze)** | wardn-verwaltet oder BYO-Cloud | Confidential-Compute-Enklave (Nitro / SEV-SNP) + Remote-Attestation + Encrypt-to-the-Proxy-Flow. Echter „Host-Kompromittierung gibt nichts preis“-Anspruch. |
Die Selbsthosting-Stufe ist der tragende Anspruch und wird heute ausgeliefert. Die gehostete Stufe ist der strikte Aufwertungspfad: Sie kostet Geld und Betriebskomplexität, und ihr Design befindet sich in [docs/HOSTED-TIER.md](https://github.com/rohansx/wardn/blob/HEAD/docs/HOSTED-TIER.md). Vollständiges ehrliches Inventar dessen, was abgedeckt ist und was nicht:
👉 **[docs/THREAT-MODEL.md](https://github.com/rohansx/wardn/blob/HEAD/docs/THREAT-MODEL.md)** — Tabelle mit abgedeckt / nicht abgedeckt, „kein Software-Tresor eliminiert Host-Kompromittierung“ deutlich hervorgehoben, und der Aufwertungspfad.
## Installation```bash
# Prebuilt binary (Linux/macOS, amd64/arm64), checksum-verified
curl -sSf https://raw.githubusercontent.com/rohansx/wardn/main/install.sh | sh
# or from crates.io
cargo install wardn
# or Homebrew, once the tap is published (see Formula/wardn.rb)
brew install rohansx/wardn/wardn
wardn vault create wardn vault set OPENAI_KEY wardn vault set ANTHROPIC_KEY
wardn setup claude-code
Das war's. Claude Code verwendet jetzt wardns MCP-Server, um Platzhalter-Token zu erhalten, anstatt echte Schlüssel aus Ihrer Umgebung zu lesen.
### Was als Nächstes passiert
1. Claude Code ruft `get_credential_ref` auf → erhält `wdn_placeholder_a1b2...` (nicht den echten Schlüssel)
2. Agent sendet Anfrage mit Platzhalter über den wardn-Proxy
3. Proxy tauscht Platzhalter gegen echten Schlüssel aus, leitet an API weiter
4. Proxy entfernt den echten Schlüssel aus der Antwort, bevor er an den Agenten zurückgegeben wird
Der echte Schlüssel gelangt nie in den Speicher, die Protokolle oder das LLM-Kontextfenster des Agenten.
## Lokales Dashboard
Sobald der Daemon läuft (`wardn serve` oder von `wardn run` gestartet), öffnen Sie
**http://127.0.0.1:7777/ui** in einem Browser. Eine schreibgeschützte, nur lokale Ansicht von:
- **Anmeldedaten** — jeder gespeicherte Anmeldedatensatz mit seinen ACLs (erlaubte Agenten, erlaubte Domänen, Ratenbegrenzung + Budget-Abzeichen).
- **Letzte Aktivität** — die letzten 50 Proxy-Ereignisse mit Methode, Domäne, Pfad, Status, Agent, Request-ID und aufgezeichneten Kosten (`request_completed`, `credential_injected`, `rate_limit`, `budget_exceeded`, `loop_detected`, `request_error`).
- **Budgets** — das konfigurierte Budget jedes Anmeldedatensatzes (max, ausgegeben, verbleibend, Fenster, Modus) mit einem Fortschrittsbalken, der von warn → bad wechselt, sobald 50% / 80% überschritten werden.
Wird automatisch alle 2 Sekunden abgefragt. Keine Mutations-Endpunkte — der einzige Weg aus dem Dashboard ist die API selbst (`/api/summary`, `/api/credentials`, `/api/audit?limit=N`, `/api/budgets`).```bash
# Static, anonymous, never sees real keys
curl http://127.0.0.1:7777/api/summary | jq
wardn vault get OPENAI_KEY
wardn vault list
wardn serve
wardn serve --mcp --agent my-agent
## CLI-Referenz
### Vault-Verwaltung```bash
wardn vault create # create encrypted vault
wardn vault set OPENAI_KEY # store credential (prompts for value, no echo)
wardn vault get OPENAI_KEY # get placeholder token (never the real value)
wardn vault get OPENAI_KEY --agent bot # get placeholder for specific agent
wardn vault list # list all credentials
wardn vault rotate OPENAI_KEY # rotate value, placeholders unchanged
wardn vault remove OPENAI_KEY # remove credential
# Custom vault path
wardn --vault /path/to/vault.enc vault list
wardn serve # HTTP proxy on 127.0.0.1:7777 wardn serve --host 0.0.0.0 --port 8080 # custom bind address wardn serve --config wardn.toml # load config with rate limits + ACLs wardn serve --mcp --agent my-agent # proxy + MCP server (stdio)
### Claude Code / Cursor Integration```bash
wardn setup claude-code # register wardn as MCP server in Claude Code
wardn setup cursor # register wardn as MCP server in Cursor
# Or manually:
claude mcp add --transport stdio --scope user wardn -- wardn serve --mcp --agent claude-code
wardn setup machtwardn-Binärdatei auf Ihrem System.claude mcp add mit WARDN_PASSPHRASE in der Umgebungskonfiguration aus.~/.cursor/mcp.json in env.wardn serve --mcp als Unterprozess.Führen Sie nach der Einrichtung einen Neustart Ihrer IDE durch und probieren Sie diese Eingabeaufforderungen aus:``` "List my wardn credentials" → Claude calls list_credentials, shows credential names (never values)
"Get me a reference to OPENAI_KEY" → Claude calls get_credential_ref, gets wdn_placeholder_... (not the real key)
"Check my rate limit for OPENAI_KEY" → Claude calls check_rate_limit, shows remaining quota
#### Verfügbare MCP-Werkzeuge
| Werkzeug | Rückgabe | Sicherheit |
|------|----------------|----------|
| `get_credential_ref` | Platzhalter-Token (`wdn_placeholder_...`) | Nie der tatsächliche Wert |
| `list_credentials` | Anmeldedatennamen + Metadaten | Gefiltert nach Agentenzugriff |
| `check_rate_limit` | Verbleibendes Kontingent, Wiederholungsinfo | Schreibgeschützt |
### Anmeldedatenmigration```bash
wardn migrate --dry-run # audit Claude Code dir for exposed keys
wardn migrate --source claude-code # scan + migrate to vault
wardn migrate --source open-claw # scan OpenClaw config
wardn migrate --source directory --path ./my-proj # scan any directory
export, quoted valueswardn import dotenv ./.env
wardn import file ./creds.json wardn import file ./creds.yaml
op session. Default name iswardn import one-password op://Personal/openai/api_key wardn import one-password op://Work/anthropic/token --name ANTHROPIC_KEY
echo 'OPENAI_KEY=sk-...' | wardn import stdin
Jeder Importeur fordert bei der ersten Verwendung zur Eingabe des Tresor-Passworts auf (oder liest es
aus der Umgebungsvariablen `WARDN_PASSPHRASE` / dem OS-Schlüsselbund). Vorhandene Werte werden stillschweigend
überschrieben — der Importeur ist wertorientiert, Metadaten (erlaubte Agents /
Domains / Ratenlimit / Budget) bleiben erhalten.
### Automatisierung
Für CI/Skripte setzen Sie die Umgebungsvariablen `WARDN_PASSPHRASE` und `WARDN_VALUE`, um interaktive Eingabeaufforderungen zu überspringen:```bash
WARDN_PASSPHRASE=my-pass wardn vault list
WARDN_PASSPHRASE=my-pass WARDN_VALUE=sk-proj-xxx wardn vault set OPENAI_KEY
Fügen Sie Ihrer Cargo.toml hinzu:```toml
[dependencies]
wardn = "0.4"
### Vault Operationen```rust
use wardn::{Vault, config::CredentialConfig};
// Create an encrypted vault
let vault = Vault::create("vault.enc", "my-passphrase")?;
// Store a credential
vault.set_with_config("OPENAI_KEY", "sk-proj-real-key-123", &CredentialConfig {
allowed_agents: vec!["researcher".into(), "writer".into()],
allowed_domains: vec!["api.openai.com".into()],
rate_limit: Some(RateLimitConfig { max_calls: 200, per: TimePeriod::Hour }),
})?;
// Agent gets a placeholder (not the real key)
let placeholder = vault.get_placeholder("OPENAI_KEY", "researcher")?;
// → "wdn_placeholder_a1b2c3d4e5f6g7h8"
// Rotate the real key — all placeholders keep working
vault.rotate("OPENAI_KEY", "sk-proj-new-key-456")?;
use wardn::daemon::{Daemon, DaemonConfig};
let daemon = Daemon::new(vault, DaemonConfig::default()); daemon.serve_proxy().await?;
### MCP Server```rust
use wardn::mcp::WardenMcpServer;
// Serve over stdio (for Claude Code, Cursor, etc.)
WardenMcpServer::serve_stdio(vault, rate_limiter, "agent-id".into()).await?;
MCP-Tools bereitgestellt (nur lesend, keine Anmeldeinformationen werden jemals zurückgegeben):
| Tool | Beschreibung |
|---|---|
get_credential_ref | Holen Sie sich Ihren Platzhalter-Token für eine Anmeldeinformation |
list_credentials | Liste der Anmeldeinformationen, auf die Sie zugreifen dürfen |
check_rate_limit | Überprüfen Sie Ihr verbleibendes Kontingent |
Für einen tieferen Vergleich mit wardns nächsten direkten Konkurrenten (Infisical Agent Vault, 1Password for Agents, LiteLLM virtuelle Schlüssel) und wie wardns Garantien den OWASP Agentic Top 10 und den Sicherheitsrichtlinien der MCP-Spezifikation zugeordnet werden, siehe docs/comparison.md.
Wardn konzentriert Vertrauen in einen einzigen lokalen Prozess (den Proxy), anstatt es über jedes Plugin, Tool und LLM-Kontextfenster zu verteilen. Dies ist eine kleinere Angriffsfläche, nicht null Angriffsfläche:
localhost:7777, nicht gegen echte APIs, und kann pro Agent ratenbegrenzt und widerrufen werdenJeder Zugriff auf Anmeldeinformationen wird mit einer eindeutigen Anforderungs-ID zur Nachvollziehbarkeit protokolliert:``` INFO request_id=a1b2c3 agent=claude-code method=POST domain=api.openai.com path=/v1/chat/completions proxy request received INFO request_id=a1b2c3 agent=claude-code credential=OPENAI_KEY domain=api.openai.com credential injected INFO request_id=a1b2c3 agent=claude-code upstream_status=200 credentials_injected=1 credentials_stripped=0 proxy request completed
Set `RUST_LOG=wardn=info` (oder `debug`/`trace`) zur Steuerung der Ausführlichkeit. Logs gehen nach stderr, nie nach stdout.
## Konfiguration```toml
[warden]
vault_path = "~/.vibeguard/vault.enc"
[warden.credentials.OPENAI_KEY]
rate_limit = { max_calls = 200, per = "hour" }
allowed_agents = ["researcher", "writer"]
allowed_domains = ["api.openai.com"]
[warden.credentials.ANTHROPIC_KEY]
rate_limit = { max_calls = 100, per = "hour" }
allowed_agents = ["researcher"]
allowed_domains = ["api.anthropic.com"]
wardn/
├── src/
│ ├── main.rs # CLI entry point (clap + tokio)
│ ├── cli/
│ │ ├── mod.rs # Clap argument definitions
│ │ ├── vault_cmd.rs # Vault subcommand handlers
│ │ ├── serve_cmd.rs # Serve subcommand handler
│ │ ├── run_cmd.rs # wardn run — lazy-starts the daemon, wires
│ │ │ # agent env vars, execs the child
│ │ ├── setup_cmd.rs # Claude Code / Cursor MCP setup (+ shell alias)
│ │ └── migrate_cmd.rs # Migrate subcommand handler
│ ├── lib.rs # Public API, WardenError
│ ├── config.rs # TOML configuration parsing, [upstreams] map
│ ├── vault/
│ │ ├── mod.rs # Vault CRUD operations
│ │ ├── encryption.rs # AES-256-GCM + Argon2id + zeroize types
│ │ ├── storage.rs # On-disk format (WDNV), atomic writes
│ │ ├── placeholder.rs # Token generation, per-agent isolation
│ │ └── keyring_store.rs # OS keychain passphrase storage
│ ├── proxy/
│ │ ├── mod.rs # HTTP proxy server (axum)
│ │ ├── route.rs # Provider-prefix vs Host-header upstream routing
│ │ ├── inject.rs # Credential injection into requests
│ │ ├── strip.rs # Credential stripping (shared pair-building)
│ │ ├── stream.rs # Streaming (SSE/chunked) credential stripper
│ │ └── rate_limit.rs # Token bucket rate limiter
│ ├── mcp/
│ │ ├── mod.rs # MCP server (rmcp, stdio transport)
│ │ └── tools.rs # Tool parameter/response types
│ ├── migrate/
│ │ ├── mod.rs # Migration orchestrator + risk scoring
│ │ └── scanners/
│ │ └── credentials.rs # API key pattern scanner
│ └── daemon/
│ └── mod.rs # Daemon (proxy + MCP in single process)
└── tests/
├── cli_tests.rs # CLI integration tests
├── vault_tests.rs # Vault integration tests
├── proxy_tests.rs # Proxy tests without a real upstream
├── proxy_e2e_tests.rs # Real upstream via wiremock (header/body/SSE)
└── run_cmd_tests.rs # Real end-to-end wardn run
## Entwicklung```bash
# Integration tests use a fast (insecure) KDF so the suite runs in
# milliseconds instead of paying the real Argon2id cost per test —
# always pass this feature flag when running tests locally or in CI:
cargo test --features test-fast-kdf
cargo build
cargo clippy --all-targets --features test-fast-kdf
flowchart LR subgraph Input Pass["Passphrase"] Salt["Random Salt\n(16 bytes)"] Creds["Credentials\n(JSON)"] end
subgraph KDF["Key Derivation"]
Argon["Argon2id\nm=19456 t=2 p=1"]
end
subgraph Encrypt["Encryption"]
AES["AES-256-GCM"]
Nonce["Random Nonce\n(12 bytes)"]
end
subgraph Output["WDNV File"]
direction TB
Magic["WDNV (4B)"]
Ver["Version (2B)"]
SaltOut["Salt (16B)"]
Payload["Nonce ‖ Ciphertext ‖ Tag"]
end
Pass --> Argon
Salt --> Argon
Argon -- "256-bit key" --> AES
Creds --> AES
Nonce --> AES
AES --> Payload
style Input fill:#1a1a2e,stroke:#e94560,color:#fff
style KDF fill:#16213e,stroke:#00d2ff,color:#fff
style Encrypt fill:#16213e,stroke:#00d2ff,color:#fff
style Output fill:#0f3460,stroke:#533483,color:#fff
### Dateiformat```
Bytes 0-3: Magic "WDNV"
Bytes 4-5: Version (u16 LE)
Bytes 6-21: Argon2id salt (16 bytes)
Bytes 22+: AES-256-GCM encrypted payload (nonce ‖ ciphertext ‖ tag)
Wardn ist die Credential-Isolation-Schicht von VibeGuard — ein Sicherheitsdaemon für KI-Agenten. Weitere geplante Module:
MIT
| Eigenschaft | Garantie |
|---|
| Keine Anmeldeinformationen im Agentenspeicher | Der Agentenprozess enthält nur Platzhalter-Strings |
| Keine Anmeldeinformationen im Klartext auf der Festplatte | AES-256-GCM-verschlüsselter Tresor mit Argon2id-KDF |
| Keine Anmeldeinformationen in Protokollen | Nur Platzhalter erscheinen in jeder Protokollausgabe |
| Keine Anmeldeinformationen im LLM-Kontext | Platzhalter in die Umgebung eingefügt, echter Schlüssel auf Netzwerkebene |
| Begrenzte Kostenexposition | Token-Bucket-Ratenbegrenzung pro Anmeldeinformation pro Agent |
| Schutz vor Anmeldeinformations-Echo | Echte Schlüssel werden aus API-Antworten entfernt, bevor sie den Agenten erreichen |
| Speichersicherheit | SensitiveString/SensitiveBytes werden bei Freigabe gelöscht |
| Atomare Persistenz | Schreiben-tmp-dann-umbenennen verhindert Tresorbeschädigung |
| Angriff | Wie wardn ihn stoppt |
|---|
.env-Anmeldeinformationsdiebstahl | Keine .env-Dateien. Schlüssel nur im verschlüsselten Tresor |
Bösartiges Skill liest $OPENAI_KEY | Erhält wdn_placeholder_... – nutzlos |
| Stealer zielt auf Agentenkonfiguration | Findet nur Platzhalter-Tokens |
| Prompt-Injection exfiltriert Schlüssel | Schlüssel nie im Agenten-Kontextfenster |
| Agentenprotokolle enthalten Anmeldeinformationen | Protokolle enthalten nur Platzhalter-Strings |
| Vollständiger Agentenkompromiss | Angreifer hat einen nutzlosen Platzhalter |
| Kostenexplosion durch schleifenartigen Agenten | Ratenbegrenzung pro Anmeldeinformation pro Agent |
| Tool | Was es tut | Wie wardn sich unterscheidet |
|---|
| Secrets-Manager (Vault, AWS SM, 1Password) | Sichere Speicherung + Abruf | Agent erhält den echten Schlüssel zur Laufzeit. Wardn stellt sicher, dass der Agent ihn nie berührt. |
| Varlock | Schema-basierte .env-Validierung + KI-sichere Konfiguration | Konzentriert sich auf Konfigurationsverwaltung und Leck-Scans. Wardn führt Laufzeit-Injektion von Anmeldeinformationen durch – der Schlüssel gelangt nie in den Agentenprozess. |
| OpenRouter | API-Routing + Schlüsselverwaltung | Vertraut dem Client mit einem API-Schlüssel. Wardn tut das nicht – der Agent hält einen nutzlosen Platzhalter. |
| dotenv + .gitignore | Schlüssel außerhalb von Git halten | Schlüssel immer noch im Speicher, in Umgebungsvariablen, in Protokollen. Wardn entfernt sie aus allen dreien. |
| Service Meshes (Istio, Linkerd) | Service-zu-Service-Authentifizierung | Lösen Infrastruktur-mTLS. Wardn löst Agent-zu-API-Authentifizierung, bei der der Agent selbst nicht vertrauenswürdig ist. |