{"record":{"id":"66e3765bff213e50","repo":"jdx/mise","slug":"remote-cache-url-must-use-https","errorCode":null,"errorMessage":"remote cache URL must use HTTPS","messagePattern":"remote cache URL must use HTTPS","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/lib.rs","lineNumber":801,"sourceCode":"        || url.scheme() == \"http\"\n            && url.host().is_some_and(|host| match host {\n                Host::Domain(host) => host.eq_ignore_ascii_case(\"localhost\"),\n                Host::Ipv4(address) => address.is_loopback(),\n                Host::Ipv6(address) => address.is_loopback(),\n            })\n    {\n        Ok(())\n    } else {\n        bail!(\"GitHub Actions OIDC request URL must use HTTPS\")\n    }\n}\n\nfn validate_remote_url(base_url: &Url, authenticated: bool) -> Result<()> {\n    if base_url.scheme() == \"https\" {\n        return Ok(());\n    }\n    if base_url.scheme() != \"http\" {\n        bail!(\"remote cache URL must use HTTPS\");\n    }\n    let is_loopback = base_url.host().is_some_and(|host| match host {\n        Host::Domain(host) => host.eq_ignore_ascii_case(\"localhost\"),\n        Host::Ipv4(address) => address.is_loopback(),\n        Host::Ipv6(address) => address.is_loopback(),\n    });\n    if !is_loopback && authenticated {\n        bail!(\"remote cache URL must use HTTPS except for loopback development servers\");\n    }\n    if !is_loopback {\n        warn!(\n            \"using an unauthenticated remote build cache over plain HTTP; cache traffic can be read \\\n             or modified in transit\"\n        );\n    }\n    Ok(())\n}\n","sourceCodeStart":783,"sourceCodeEnd":819,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/crates/mise-cache-core/src/lib.rs#L783-L819","documentation":"RemoteCacheClient::new validates the configured base_url scheme: https passes, plain http gets special handling, and any other scheme (ftp, file, ws, or a malformed URL) is rejected immediately. This is a pure configuration error raised before any network I/O happens.","triggerScenarios":"Passing a RemoteCacheConfig.base_url such as \"ftp://cache.internal\", \"file:///var/cache\", one missing its scheme, or a typo like \"htps://cache.internal\"; config templating that drops or mangles the scheme.","commonSituations":"Templated configuration where the scheme variable is empty; passing a filesystem path where a URL is expected; environment-specific URL assembly bugs; secrets placeholders leaking into the URL field.","solutions":["Set base_url to an https:// URL (or http:// for loopback dev servers)","Parse and scheme-check the URL at config-load time and fail with the offending value in the message","Log the fully constructed base_url in debug builds to catch templating mistakes"],"exampleFix":"// before\nlet config = RemoteCacheConfig {\n    base_url: \"ftp://cache.internal\".parse()?,\n    ..\n};\n\n// after\nlet config = RemoteCacheConfig {\n    base_url: \"https://cache.internal\".parse()?,\n    ..\n};","handlingStrategy":"validation","validationCode":"fn parse_cache_base_url(raw: &str) -> eyre::Result<url::Url> {\n    let url: url::Url = raw.parse()?;\n    anyhow::ensure!(\n        matches!(url.scheme(), \"https\" | \"http\"),\n        \"remote cache URL must use https (or http for loopback), got {}\",\n        url.scheme()\n    );\n    Ok(url)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Scheme-check the URL at config-load time and report the raw value on failure","Render the final URL in debug logs to catch templating that drops the scheme","Never feed filesystem paths into base_url"],"tags":["url","validation","remote-cache","configuration"],"backgroundTag":"https-url-required","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}