BunShip provides API key management for programmatic access. Keys are scoped to organizations, support granular permissions, and track usage automatically.
The prefix bunship_live_ (or bunship_test_ for test mode) makes keys identifiable in logs and configuration files. Only the first 8 characters of the random portion are stored as the display prefix:
The full API key is returned only once in the creation response. BunShip stores a SHA-256 hash
of the key, not the key itself. There is no way to retrieve the full key after creation.
The raw key is hashed with SHA-256 to produce a digest.
2
Looking up the hash
The hash is compared against stored key hashes in the database.
3
Checking key status
The key must be active and not expired.
Copy
if (!apiKey.isActive) { throw new AuthenticationError("API key is inactive");}if (apiKey.expiresAt && apiKey.expiresAt < new Date()) { throw new AuthenticationError("API key has expired");}
4
Updating last used timestamp
On successful validation, the lastUsedAt field is updated for usage tracking.
BunShip applies several measures to protect API keys:
SHA-256 hashing
Keys are hashed with SHA-256 before storage. The database never contains the raw key. Even if the database is compromised, the keys cannot be reversed.
Copy
export async function hashApiKey(key: string): Promise<string> { const encoder = new TextEncoder(); const data = encoder.encode(key); const hashBuffer = await crypto.subtle.digest("SHA-256", data); return Array.from(new Uint8Array(hashBuffer), (b) => b.toString(16).padStart(2, "0") ).join("");}
Prefix-only display
After creation, the API only shows the key prefix (e.g., bunship_live_a1b2c3d4). The full key is never returned again.
Automatic expiration
Keys can be given an expiration date. Expired keys are rejected during validation without any manual intervention.
Rate limiting
Each key can have an individual rate limit. The middleware enforces this alongside global rate limits.