router-for-me/CLIProxyAPI · error
home jwt enrollment_secret is required
Error message
home jwt enrollment_secret is required
What it means
Claim-level validation in parseHomeJWTClaims: the enrollment_secret claim is empty or whitespace-only. The secret authenticates the client's certificate-signing request to the home coordinator during enrollment, so the token is unusable without it.
Source
Thrown at internal/home/certificate.go:104
}
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 {
return decoded, nil
}
return base64.URLEncoding.DecodeString(part)
}
func defaultCertificatePaths() (certificatePaths, error) {
homeDir, errHome := os.UserHomeDir()
if errHome != nil {
return certificatePaths{}, errHomeView on GitHub (pinned to 78f0c4079e)
Solutions
- Request an unredacted enrollment token from the coordinator
- If the secret was removed for security when sharing, get a newly minted token instead of the sanitized copy
- Verify the claim exists by base64url-decoding the payload before starting the server
Example fix
// before (JWT payload)
{"certificate_id":"c","cluster_id":"k","ca_fingerprint":"AA","enrollment_secret":""}
// after (JWT payload)
{"certificate_id":"c","cluster_id":"k","ca_fingerprint":"AA","enrollment_secret":"s3cr3t"} Defensive patterns
Strategy: validation
Validate before calling
// verify enrollment_secret claim is non-empty before use
payload, _ := base64.RawURLEncoding.DecodeString(strings.Split(tok, ".")[1])
var c map[string]any
_ = json.Unmarshal(payload, &c)
if s, ok := c["enrollment_secret"].(string); !ok || strings.TrimSpace(s) == "" {
log.Fatal("enrollment token lacks enrollment_secret")
} Prevention
- Never use redacted/sanitized copies of enrollment tokens — request fresh ones
- Keep token transfer channels lossless (secret manager or file, not chat-redacted logs)
- Check all claims up front instead of discovering them missing one at a time
When it happens
Trigger: The decoded JWT payload lacks enrollment_secret. Structural parsing and the earlier claim checks passed, so the token was minted incompletely.
Common situations: Token redaction (someone stripped the secret before sharing/logs), coordinator bug, or a token minted for a different flow that never includes the secret.
Related errors
- home jwt is invalid
- home jwt certificate_id is required
- home jwt cluster_id is required
- home jwt ca_fingerprint 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/1ff93304bdc8d52d.
Report an issue: GitHub.