router-for-me/CLIProxyAPI · error

home jwt is invalid

Error message

home jwt is invalid

What it means

Thrown by parseHomeJWTClaims in internal/home/certificate.go when the home enrollment JWT does not have exactly three dot-separated segments (header.payload.signature). It is a shape check performed before any decoding — the token is not a structurally valid JWT.

Source

Thrown at internal/home/certificate.go:85

		Enabled: true,
		NodeID:  strings.TrimSpace(claims.CertificateID),
		Host:    strings.TrimSpace(claims.IP),
		Port:    claims.Port,
		TLS: config.HomeTLSConfig{
			Enable:              true,
			CACert:              paths.CACert,
			ClientCert:          paths.ClientCert,
			ClientKey:           paths.ClientKey,
			UseTargetServerName: true,
		},
	}, 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) == "" {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Re-copy the full enrollment JWT; it must look like xxxxx.yyyyy.zzzzz with exactly two dots
  2. Verify the variable/argument carrying it was not truncated (echo ${#TOKEN} and compare with the source)
  3. Confirm you are passing the home enrollment token, not an API key or session token

Example fix

# before
CLI_PROXY_HOME_TOKEN="eyJhbGciOiJFUzI1NiIsImtpZCI6"   # truncated

# after
CLI_PROXY_HOME_TOKEN="eyJhbGciOiJFUzI1NiIs...J9.eyJjZXJ0aWZpY2F0ZV9pZCI6...In0.SIG"
Defensive patterns

Strategy: validation

Validate before calling

func looksLikeCompactJWT(s string) bool {
    parts := strings.Split(strings.TrimSpace(s), ".")
    return len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != ""
}

Prevention

When it happens

Trigger: Passing a value that is not a compact JWS: a raw opaque token, a truncated/copied-incomplete string, a JWE (5 parts), or an empty/whitespace string with extra dots.

Common situations: Copy-pasting the enrollment token and losing the tail, environment-variable wrapping/truncation, passing an API key instead of the enrollment JWT, or shell quoting that splits the token.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/37a1617827905d6b. Report an issue: GitHub.