Your registry password never touches our database
June 16, 2026 · RestEpp team
Every RestEpp API key is, structurally, an encrypted envelope around your EPP registry credential — not a lookup key into a table where we keep it. This post is the design behind that decision.
The naive approach, and why we didn’t do it
The obvious way to build “REST in front of EPP” is: store the registrar’s username/password in a database row next to an API key, and look up the credential on every request. It works, but it means a single database compromise leaks every customer’s registry access — passwords that, for many registries, can create, transfer, or delete domains.
We wanted a design where the credential simply isn’t in our database to steal.
How key minting works
When you create a key, RestEpp:
- Validates the credential live — a real EPP
connect()+login()against the registry host you provide. Invalid credentials never get this far: we return400and store nothing. - Encodes the credential into the key itself using AES-256-GCM:
keyId: 16 random bytes, base64url — an opaque identifier, not derived from the credential.- The ciphertext is
iv(12) | authTag(16) | ctoverJSON.stringify({ host, port, username, password }), base64url-encoded. - The full key is
re_live_${keyId}.${ciphertext}.
- Stores a row with no credential fields at all — just
keyId, akeyHash(SHA-256 of the full key, for verification), acredHash(derived fromhost|port|username|password, used to bind one key to one registry identity without revealing it),label,status, and timestamps.
The master AES key lives in Google Secret Manager, fetched once at cold start and cached in memory — never per-request round trips, never in your key.
What this buys you
- Tamper-evident. AES-GCM’s authentication tag means flipping a single bit of a key makes it fail to decrypt, full stop. There’s no “close enough” credential.
- One key, one registry.
credHashis derived from the connection identity (host/port/username/password), so we can detect and dedupe attempts to mint two keys for the same underlying registry account without ever storing the password in a queryable form. - Revocation is real deletion. Revoke a key and its
statusflips — there was never a plaintext credential sitting in a table to separately purge. - A database leak doesn’t leak registry access. An attacker with read
access to our
ApiKeystable getskeyIds, hashes, and labels. Without the full key string (which only you have, shown once at creation) and our Secret-Manager-held master key, there’s nothing to decrypt.
The trade-off, stated plainly
If you lose your full API key, we cannot recover it — there’s no “forgot key” flow, because recovering it would mean we could decrypt it without you, which defeats the point. You revoke and mint a new one. That’s the deal.
We think it’s the right one. Read the request/response shape in the API
reference, or see how the warm connection pool
uses this same credHash to know which EPP session to reuse.