Keyring

Key format

192 bits, a CRC-32, the environment inside the hashed material, and two hashes for two purposes.

kr_live_<32 base64url chars><8 hex chars>     48 characters
kr_test_<32 base64url chars><8 hex chars>
krsk_live_<...>                               vendor secret key, 50 characters
krsk_test_<...>
  • 24 random bytes, 192 bits of entropy, from the operating system's CSPRNG only. Never a PRNG, never a UUID. The 192 bits can never be reduced: the lookup hash is unpeppered by architectural necessity, so entropy is the sole defence against offline brute force.
  • A CRC-32 over the prefix and the body, as eight lowercase hex digits. It is not a security control: it lets a typo be refused with no lookup, and it is one of GitHub's stated requirements for a scannable secret. Covering the prefix as well as the body means an environment cannot be swapped in place: kr_live_ plus a test key's body is a checksum failure, not a lookup miss.
  • The environment is inside the key, so it is inside the hashed material. There is no code path where a mis-set boolean promotes a test key to live.
  • Two environments, closed. A third is a schema migration and a major SDK version, deliberately.
  • The display prefix is the prefix plus six body characters: 36 of 192 bits, enough to identify a key in a log and useless as a credential. It is the only form of a key that appears anywhere after minting.

Two hashes

Lookup hashSecret hash
ValueSHA-256(key)HMAC-SHA-256(pepper, key)
Computed byThe SDK, on the hot pathThe control plane only
PurposeFind the policy record in the cacheProve the key against a database dump without the pepper

The pepper lives in the closed @keyring/pepper package and never ships into your process. The SDK verifies by lookup hash and a constant-time compare of the policy record's own material; it never sees a secret hash.

Secret scanning

The format meets GitHub's requirements for a scannable secret, and the control plane exposes the partner webhook GitHub would deliver to. Registration with GitHub's secret-scanning partner programme is not yet done. Once a report arrives, a leaked test key is revoked on report; a leaked live key is quarantined, its expiry pulled in to one hour and never extended, because silently killing a production credential on a scanner's say-so is an outage you cannot explain to your customer. The hour reaches your fleet the same way an expiry does, on the next poll, and the workspace's owners get a mail carrying the key's display prefix and the URL it was found at.

The conformance fixtures

conformance/fixtures/*.json are generated by an independent Python implementation of the format, and every SDK, including the Python and Go ports to come, has to pass them. They are what makes "the same key verifies the same way in every language" a test rather than a sentence.

On this page