router-for-me/CLIProxyAPI · error
home jwt certificate_id is required
Error message
home jwt certificate_id is required
What it means
Claim-level validation in parseHomeJWTClaims: the decoded JWT payload's certificate_id claim is empty or whitespace-only. The certificate_id is required because it becomes the CSR CN when issuing the client certificate during home enrollment.
Source
Thrown at internal/home/certificate.go:95
},
}, nil
}
func parseHomeJWTClaims(rawJWT string) (homeJWTClaims, error) {
var claims homeJWTClaims
parts := strings.Split(strings.TrimSpace(rawJWT), ".")
if len(parts) != 3 {
return claims, fmt.Errorf("home jwt is invalid")
}
payload, errDecode := decodeJWTPart(parts[1])
if errDecode != nil {
return claims, errDecode
}
if errUnmarshal := json.Unmarshal(payload, &claims); errUnmarshal != nil {
return claims, errUnmarshal
}
if strings.TrimSpace(claims.CertificateID) == "" {
return claims, fmt.Errorf("home jwt certificate_id is required")
}
if strings.TrimSpace(claims.ClusterID) == "" {
return claims, fmt.Errorf("home jwt cluster_id is required")
}
if normalizeFingerprint(claims.CAFingerprint) == "" {
return claims, fmt.Errorf("home jwt ca_fingerprint is required")
}
if strings.TrimSpace(claims.EnrollmentSecret) == "" {
return claims, fmt.Errorf("home jwt enrollment_secret is required")
}
if strings.TrimSpace(claims.IP) == "" || claims.Port <= 0 {
return claims, fmt.Errorf("home jwt target address is invalid")
}
return claims, nil
}
func decodeJWTPart(part string) ([]byte, error) {
if decoded, errDecode := base64.RawURLEncoding.DecodeString(part); errDecode == nil {View on GitHub (pinned to 78f0c4079e)
Solutions
- Request a fresh enrollment token from the home coordinator / service that minted it
- If you mint tokens yourself, include a non-empty certificate_id claim
- Decode the payload (base64url of part 2) and confirm certificate_id is present before retrying
Example fix
// before (JWT payload)
{"cluster_id":"c1","ca_fingerprint":"AA:BB.."}
// after (JWT payload)
{"certificate_id":"client-123","cluster_id":"c1","ca_fingerprint":"AA:BB.."} Defensive patterns
Strategy: validation
Validate before calling
func hasClaim(payload []byte, key string) bool {
var m map[string]any
if json.Unmarshal(payload, &m) != nil {
return false
}
v, ok := m[key].(string)
return ok && strings.TrimSpace(v) != ""
} Prevention
- Decode and inspect the JWT payload before enrollment to verify certificate_id is present
- Keep coordinator and client versions in lockstep so claim contracts match
- Never hand-craft enrollment tokens; mint them from the coordinator
When it happens
Trigger: The home coordinator issued an enrollment JWT whose payload lacks (or blanks) the certificate_id claim. Decoding succeeds, so this is a token-content problem, not a token-format problem.
Common situations: A stale or mis-minted token from an older coordinator version, manual token construction that omits the claim, or a payload copied from a different JWT type.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- home jwt is invalid
- home jwt cluster_id is required
- home jwt ca_fingerprint is required
- home jwt enrollment_secret is required
- home jwt target address is invalid
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/2af247ac71db4b5a.
Report an issue: GitHub.