# API-Schlüssel sicher verwalten

> Wo Schlüssel hingehören, welcher Scope für welches System, wie Sie ohne Ausfall wechseln und was zu tun ist, wenn ein Schlüssel bekannt wird.

Ein API-Schlüssel (`bp_live_…`) gehört Ihrer Organisation, nicht einer Person. Wer ihn hat, liest alles, was ein
Betrachter Ihrer Organisation lesen darf, einschließlich eingeschränkter Passdaten und Exporte, und meldet mit dem
Scope `condition` Messwerte und Ereignisse im Namen Ihrer Organisation. Behandeln Sie jeden Schlüssel wie ein
Passwort. Wie Schlüssel funktionieren, beschreibt [Authentifizierung](https://passport.kabin.sh/docs/de/authentication); den größeren
Rahmen [Sicherheit und Datenschutz](https://passport.kabin.sh/docs/de/general/security).

Eigentümer erstellen und widerrufen Schlüssel im Portal unter **Einstellungen → API-Schlüssel**. Eine API dafür
gibt es nicht, und das Secret eines Schlüssels wird nur einmal angezeigt, beim Erstellen.

## Regeln

| Regel                                                                                                                                | Warum                                                                                                                      |
| ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| Schlüssel in einem Secrets Manager (oder dem Secret-Speicher Ihrer Plattform) halten und dem Prozess als Umgebungsvariable übergeben | So bleiben sie aus Code, Images und Backups heraus                                                                         |
| Nie in Quellcode, Repository, Container-Image, URL, Logzeile, Ticket oder Chat                                                       | All das wird kopiert, indiziert oder jahrelang aufbewahrt                                                                  |
| Nie an einen Browser, eine mobile App oder ein Gerät im Feld ausliefern                                                              | Alles auf einem Client lässt sich auslesen; öffentliche Passabrufe brauchen ohnehin keinen Schlüssel                       |
| Schlüssel nur im Header `Authorization: Bearer` senden, nur über HTTPS                                                               | Header landen nicht wie URLs in Zugriffsprotokollen und Browserverläufen                                                   |
| Ein Schlüssel je System                                                                                                              | Sie können ein System widerrufen, ohne die anderen anzuhalten, und das Portal zeigt für jeden Schlüssel die letzte Nutzung |
| Der kleinste Scope, der genügt                                                                                                       | Ein bekannt gewordener Leseschlüssel kann keine Daten melden; ein Schlüssel für Zustandsdaten kann Ihre Daten nicht lesen  |

## Welcher Scope für welches System

| System                                                                | Scope                                                 |
| --------------------------------------------------------------------- | ----------------------------------------------------- |
| ERP oder MES, das Einheiten und Pässe übernimmt                       | `read`                                                |
| Data Warehouse oder Reporting                                         | `read`                                                |
| Batteriemanagementsystem oder Gateway, das Messwerte sendet           | `condition`                                           |
| System eines Servicepartners, das Inspektionen und Reparaturen meldet | `condition`                                           |
| Ein Werkzeug, das vor dem Senden die Zustandsfelder nachschlägt       | `read` und `condition`, oder zwei getrennte Schlüssel |
| Middleware, die Einheiten aus der Fertigung als Import bereitstellt   | `import` (Mitglieder übernehmen und geben frei)       |

Ein Schlüssel ist nicht auf bestimmte Modelle oder Einheiten beschränkt: Ein Schlüssel mit `condition`, den Sie
einem Servicepartner geben, kann für jede Einheit Ihrer Organisation melden. Geben Sie ihn nur Partnern, denen Sie
das zutrauen.

## Den Schlüssel beim Start laden

Lesen Sie den Schlüssel einmal aus der Umgebung, brechen Sie früh ab, wenn er fehlt, und geben Sie ihn nie aus:

```bash title="Shell"
# In a systemd unit, a CI secret or your orchestrator's secret store, not in a script under version control
export BATTERYPASS_API_KEY="$(cat /run/secrets/batterypass_api_key)"
```

```ts title="TypeScript"
const key = process.env.BATTERYPASS_API_KEY;
if (!key?.startsWith('bp_live_')) throw new Error('BATTERYPASS_API_KEY is missing or malformed');
const headers = { Authorization: `Bearer ${key}` }; // pass this object along; never log it
```

```python title="Python"
import os
import sys

key = os.environ.get("BATTERYPASS_API_KEY", "")
if not key.startswith("bp_live_"):
    sys.exit("BATTERYPASS_API_KEY is missing or malformed")
headers = {"Authorization": f"Bearer {key}"}  # pass this dict along; never log it
```

```go title="Go"
package main

import (
	"log"
	"net/http"
	"os"
	"strings"
)

func main() {
	key := os.Getenv("BATTERYPASS_API_KEY")
	if !strings.HasPrefix(key, "bp_live_") {
		log.Fatal("BATTERYPASS_API_KEY is missing or malformed")
	}
	req, _ := http.NewRequest(http.MethodGet, os.Getenv("BATTERYPASS_URL")+"/api/v1/registry", nil)
	req.Header.Set("Authorization", "Bearer "+key) // never log req.Header
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	res.Body.Close()
	log.Println("getRegistryInfo:", res.Status)
}
```

Wenn Ihr HTTP-Client oder Proxy Anfragen protokolliert, sorgen Sie dafür, dass er den Header `Authorization`
maskiert.

## Ohne Ausfall wechseln

Schlüssel laufen nicht ab. Wechseln Sie sie in einem Rhythmus, den Sie festlegen, und immer dann, wenn jemand geht,
der einen Schlüssel kannte. Eine Organisation kann 25 aktive Schlüssel haben; alter und neuer Schlüssel passen
während des Wechsels also nebeneinander.

1. **Neuen Schlüssel erstellen**

   Eine Eigentümerin oder ein Eigentümer erstellt einen Schlüssel mit denselben Scopes und einem Namen, der die beiden
   unterscheidet, etwa `ERP-Abgleich 2026-10`, und legt das Secret direkt im Secrets Manager ab.

2. **Ausrollen**

   Stellen Sie das System auf das neue Secret um und starten oder laden Sie es neu. Beide Schlüssel funktionieren
   gleichzeitig; dazwischen schlägt also nichts fehl.

3. **Prüfen, dass der neue Schlüssel genutzt wird**

   Unter **Einstellungen → API-Schlüssel** zeigt der neue Schlüssel eine aktuelle Angabe **Zuletzt genutzt**, und die
   des alten ändert sich nicht mehr. Die Angabe wird höchstens einmal pro Minute aktualisiert.

4. **Alten Schlüssel widerrufen**

   Widerrufen Sie den alten Schlüssel. Der Widerruf gilt sofort und endgültig; der Schlüssel bleibt als widerrufen in
   der Liste.

## Wenn ein Schlüssel bekannt wird

Ein Schlüssel gilt als bekannt geworden, sobald er irgendwo außerhalb Ihres Secrets Managers aufgetaucht ist: in
einem Commit, einem Log, einem Screenshot, einem Supportticket, den Notizen einer ehemaligen Mitarbeiterin.

1. **Sofort widerrufen**

   Eine Eigentümerin oder ein Eigentümer widerruft den Schlüssel unter **Einstellungen → API-Schlüssel**. Ab diesem
   Moment ist jede Anfrage damit `401 unauthenticated`. Tun Sie das zuerst, bevor Sie wissen, ob der Schlüssel
   missbraucht wurde.

2. **Ersetzen**

   Erstellen Sie einen neuen Schlüssel mit denselben Scopes und rollen Sie ihn aus wie beim Wechsel oben. Bis dahin
   steht das System, das den alten Schlüssel nutzte; das ist der Preis dafür, zuerst zu widerrufen.

3. **Prüfen, was geschehen ist**

   Schreibzugriffe mit einem Schlüssel werden mit dem Schlüssel als Akteur protokolliert: Sehen Sie im
   Änderungsprotokoll des Portals nach und in Lebenszyklus-Ereignissen mit `source: api` und dem Namen des Schlüssels
   in `api_key`. Lesezugriffe werden nicht protokolliert; gehen Sie also davon aus, dass alles gelesen wurde, was der
   Schlüssel lesen konnte.

4. **Die Kopie entfernen**

   Löschen Sie den Schlüssel dort, wo er bekannt wurde (Commit umschreiben, Log bereinigen, Ticket bearbeiten), auch
   wenn er nicht mehr funktioniert, damit Scanner ihn nicht weiter melden.

> [!TIP]
> **Schlüssel finden, bevor andere es tun**
>
> Secrets haben eine feste Form: `bp_live_` gefolgt von 43 base64url-Zeichen (Buchstaben, Ziffern, `-` und `_`).
> Nehmen Sie das Muster `bp_live_[A-Za-z0-9_-]{43}` in Ihren Secret-Scanner und Ihre Pre-Commit-Hooks auf. Das
> Portal zeigt die ersten 12 Zeichen jedes Schlüssels; das genügt, um einen Fund einem Schlüssel zuzuordnen, ohne das
> Secret offenzulegen.

Alle Schlüssel Ihrer Organisation teilen sich ein Limit von 600 Anfragen pro Minute. Ein bekannt gewordener,
noch aktiver Schlüssel kann deshalb auch Ihre regulären Integrationen ausbremsen ([Ratenlimits](https://passport.kabin.sh/docs/de/rate-limits)).
