headroomlabs-ai/headroom · warning

comparator cache_aligner not implemented (Phase 0)

Error message

comparator cache_aligner not implemented (Phase 0)

What it means

HTTP 403 from require_same_origin (loopback_guard.py:245): the request carries 'Origin: null'. Browsers send this from sandboxed iframes, file:// pages, or redirect chains; the guard treats it as an untrustworthy origin and rejects CSRF-style attempts on mutating routes. Non-browser clients send no Origin at all and pass.

Source

Thrown at crates/headroom-parity/src/lib.rs:173

// * `cache_aligner` — needs the volatile-content detector, which lives in
//   `headroom-proxy` while this crate depends only on `headroom-core`. Either
//   add the dependency or move the detector down into core.
// * `ccr` — the fixtures compare `ccr_retrieve` tool-definition injection
//   (`headroom/ccr/tool_injection.py`), which has no Rust port at all.

macro_rules! stub_comparator {
    ($ty:ident, $name:literal) => {
        pub struct $ty;
        impl TransformComparator for $ty {
            fn name(&self) -> &str {
                $name
            }
            fn run(
                &self,
                _input: &serde_json::Value,
                _config: &serde_json::Value,
            ) -> Result<serde_json::Value> {
                anyhow::bail!(concat!("comparator ", $name, " not implemented (Phase 0)"))
            }
        }
    };
}

stub_comparator!(CacheAlignerComparator, "cache_aligner");
stub_comparator!(CcrComparator, "ccr");

/// Real comparator for the `log_compressor` transform.
///
/// Two wrinkles beyond the usual adapter shape:
///
/// * **bias.** Python's signature is `compress(content, context="", bias=1.0)`
///   and the recorder captured only `content`, so every fixture was produced at
///   the default `bias = 1.0`. Rust takes `bias` positionally — pass 1.0.
/// * **CCR store.** The Python compressor owns its store internally, while Rust
///   mints a `cache_key` only when one is handed to `compress_with_store`.
///   Without a store the CCR branch bails out with `"no store provided"` and

View on GitHub (pinned to 322425c43b)

Solutions

  1. Serve the page over http(s) from a proper origin (same host) instead of file://
  2. Add allow-same-origin to the embedding iframe's sandbox attribute
  3. For scripted access, use a non-browser client (curl, SDK) which sends no Origin header

Example fix

<!-- before -->
<iframe src="http://127.0.0.1:8080/settings" sandbox="allow-scripts"></iframe>
<!-- after -->
<iframe src="http://127.0.0.1:8080/settings" sandbox="allow-scripts allow-same-origin"></iframe>
Defensive patterns

Strategy: validation

Validate before calling

// Browser-side: refuse to call mutating endpoints from a null origin
if (location.origin === "null") throw new Error("serve this page over http(s), not file://");

Try / catch

if (resp.status === 403 && detail === "cross-origin request rejected") {
  console.error("null origin — serve the page from a real origin");
}

Prevention

When it happens

Trigger: A dashboard page served from a file:// URL or a sandboxed iframe issues fetch()/XHR with JSON bodies to the local mutating endpoints; a page that redirected through a data: or blob: URL before the request.

Common situations: Opening an HTML report or local tool from disk that talks to the proxy; embedding the dashboard in a sandboxed iframe without allow-same-origin; edge redirects that nullify the origin.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/165b4dc82f17a675. Report an issue: GitHub.