Skip to content

Key providers

Every signing operation in the SDKs — records, JWTs, HTTP signatures, lifecycle events — goes through a KeyProvider, the interface that abstracts where private keys live. Two implementations ship with each SDK (a local file-backed store and an AWS KMS-backed provider), and you can implement your own for other KMSs or HSMs without touching the verification APIs.

All providers share one key lifecycle:

  • Pending — freshly generated, unpublished; can sign only for proof-of-possession during rotation, and is hidden from key listings and JWKS.
  • Active — the one signing key; always listed first.
  • Retained — rotated out; published for verifying old signatures, can never sign again.

The store is a JSON file (written mode 0600) holding the active, retained, and pending private JWKs. All three local providers support Ed25519 and ES256.

// Load, or generate + persist on first run.
kp, err := dnsid.LoadOrCreateLocalKeyProvider("keys.json", dnsid.JoseAlgEdDSA)
if err != nil {
log.Fatal(err)
}
fmt.Println(kp.JWK()) // active public JWK
fmt.Println(kp.ListKeyIds()) // active kid first, then retained
// Rotate: generate pending -> activate -> supersede the old key.
pendingKid, _ := kp.GenerateKey(dnsid.JoseAlgEdDSA)
_ = kp.Activate(pendingKid) // old active becomes retained
_ = kp.Supersede(oldKid) // drop the retained key

In Go, config.IdentityManagerFromDnsid loads the CLI’s identity and key together. For the key alone, call dnsid.NewLocalKeyProvider with the domain’s private.jwk path.

The public JWKS to host at /.well-known/jwks.json is the same construction in every language: one entry per listed key id — active and retained keys, never pending ones.

Private key material never leaves KMS: the provider fetches and caches public keys as JWKs and delegates signing to kms:Sign. Supported algorithms are ECDSA_SHA_256 (JOSE ES256) and ED25519_SHA_512 (JOSE EdDSA); pass a key ARN, key ID, or alias — aliases resolve to canonical IDs on load. Minimum IAM: kms:GetPublicKey + kms:Sign (plus kms:CreateKey to generate, kms:ScheduleKeyDeletion for deletion on cleanup).

// Separate module: go get github.com/dnsid-ai/dnsid-go/key/aws
import (
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/kms/types"
awskms "github.com/dnsid-ai/dnsid-go/key/aws"
)
client := awskms.SDKClient{Client: kms.NewFromConfig(awsCfg)}
provider, err := awskms.Load(ctx, client, awskms.Config{
State: awskms.State{ActiveKeyID: "alias/dnsid-current"},
Algorithm: types.SigningAlgorithmSpecEcdsaSha256, // the default
})
// Persist provider.State() after GenerateKey/Activate/Supersede.

Any key store can back DNSid by implementing the provider surface — the in-repo KMS providers are the model for wrapping a cloud client behind a facade.

// Satisfy the dnsid.KeyProvider interface (8 methods). The in-repo
// providers add a compile-time check worth copying:
var _ dnsid.KeyProvider = (*MyKeyProvider)(nil)
// JWK(kid ...string) jwk.Key // public JWK; no kid = active
// ListKeyIds() []string // active first, then retained
// Sign(payload []byte) (*dnsid.KeySignature, error)
// SignKey(kid string, payload []byte) (*dnsid.KeySignature, error)
// GenerateKey(alg dnsid.JoseAlg) (string, error)
// Activate(kid string) error
// Supersede(kid string) error
// Purge(kid string) error

Full key-provider surface per language: Go and Go AWS KMS · TypeScript AWS KMS · Python.