ruvnet/ruflo · critical · ReleaseVerificationError
SHA256SUMS.sig failed Ed25519 verification — refusing to…
Error message
SHA256SUMS.sig failed Ed25519 verification — refusing to install
What it means
verifyRelease() first checks the Ed25519 signature over the SHA256SUMS manifest using the public key embedded in the CLI — per ADR-307 there is no partial-trust outcome, so any signature failure refuses the install outright. This is the root-of-trust check: per-file hashes are only consulted after it passes. A wrong key, tampered manifest, or corrupted download all produce this one error.
Solutions
- Update the ruflo CLI to the latest release — key rotations ship with an updated embedded public key
- Clear cached downloads and retry `ruflo proxy install` from a clean network path (in-transit corruption is common)
- If it persists on the latest CLI from a clean network, report it to maintainers immediately — consistent failure can indicate a genuine supply-chain compromise
- Never attempt to bypass it; there is intentionally no override flag
Defensive patterns
Strategy: try-catch
Type guard
const isReleaseVerification = (e: unknown): e is Error & { name: 'ReleaseVerificationError' } =>
e instanceof Error && e.name === 'ReleaseVerificationError'; Try / catch
try {
const { sha256 } = verifyRelease(input);
} catch (e) {
if (isReleaseVerification(e)) {
// NEVER bypass: report and abort. Signature failure on a current CLI
// from a clean network is a supply-chain signal.
securityLog.fatal('release signature invalid', { detail: e.message });
process.exit(4);
}
throw e;
} Prevention
- Auto-update the CLI so the embedded signing key stays current
- Never add a skip-verification escape hatch in wrappers
- Alert on this error class in fleet telemetry — consistent failures warrant incident response
When it happens
Trigger: A signature produced by a different key than the one embedded in this CLI version (upstream key rotation without updating ruflo); a modified SHA256SUMS or .sig in transit; a truncated download of either file failing crypto.verify.
Common situations: Upstream rotated the signing key while users run an older CLI; MITM/proxy rewriting downloads; interrupted downloads leaving truncated files; releases published with a mis-signed manifest.
Related errors
- extracted binary path failed validation
- LocalTransport: signature verification failed for message…
- sha256 mismatch for : expected …, got …
- SHA256SUMS has no entry for
- AI budget file is a symlink (refusing)
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/e855d7bd4e59a8bd.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/proxy/verify.ts:79
sigBase64: string;
assetBytes: Buffer;
assetFilename: string;
pubkeyPem?: string;
}
export interface VerifyReleaseResult {
sha256: string;
}
/**
* Full verification: signature over SHA256SUMS, then the asset's own hash
* against the matching line. Throws `ReleaseVerificationError` on ANY
* failure — there is no partial-trust outcome, matching ADR-307's "refuses
* on any mismatch" requirement.
*/
export function verifyRelease(input: VerifyReleaseInput): VerifyReleaseResult {
if (!verifySha256SumsSignature(input.sumsBytes, input.sigBase64, input.pubkeyPem)) {
throw new ReleaseVerificationError('SHA256SUMS.sig failed Ed25519 verification — refusing to install');
}
const sums = parseSha256Sums(input.sumsBytes.toString('utf-8'));
const expected = sums[input.assetFilename];
if (!expected) {
throw new ReleaseVerificationError(`SHA256SUMS has no entry for ${input.assetFilename}`);
}
const actual = sha256Hex(input.assetBytes);
if (actual !== expected) {
throw new ReleaseVerificationError(
`sha256 mismatch for ${input.assetFilename}: expected ${expected.slice(0, 12)}…, got ${actual.slice(0, 12)}…`,
);
}
return { sha256: actual };
}
View on GitHub (pinned to fa13ee4ad6)