This page is fully self-contained: no external network request is possible (see the Content-Security-Policy above). A live mode twin of this same post can load map tiles and remote SPARQL endpoints.

The block-engine MVP has a real canonical byte object now: an IBK1 file is a shared RDF-term dictionary plus ID rows, framed by CRC32C. The native Lean query path can additionally require a trusted SHA-256 digest before decoding. This page gives the browser the same artifact boundary: choose bytes, inspect the untrusted header, calculate their identity, and optionally cache the exact bytes locally. It remains an IBK1 inspector. The newer three-block query notebook runs current IBK3 artifacts through a narrow Lean-WASM scan operation.

blockFormat = ({
  magic: "IBK1",
  version: 1,
  headerBytes: 5,
  integrityNow: "CRC32C framing plus SHA-256 checked against a trusted digest",
  browserBoundary: "this page inspects IBK1; the Lean-WASM ABI now scans complete IBK2 and IBK3 artifacts",
})

Try the bundled 77-triple music sample first, or inspect a file of your own. The ordinary file input works in current browsers. Where the File System Access picker is available, the button uses it; the input is the portable fallback. The optional cache is the origin-private file system (OPFS), not an upload: it is private to this HTTPS site and can later support a worker-owned range-read block cache. Delete the browser's site data to remove it.

blockArtifactPicker = {
  const root = html`<div>
    <p><strong>IBK1 artifact inspector.</strong> Start with the bundled music sample, or select a local <code>.ibk1</code> file.</p>
    <p><button type="button" class="sample">Inspect bundled music sample</button> <button type="button" class="choose">Choose block</button> <input type="file" accept=".ibk1,application/octet-stream" hidden></p>
    <label><input type="checkbox"> Cache a copy in this browser's OPFS after inspection</label>
    <pre aria-live="polite">No file selected.</pre>
  </div>`;
  const sample = root.querySelector(".sample");
  const choose = root.querySelector(".choose");
  const input = root.querySelector('input[type="file"]');
  const cache = root.querySelector('input[type="checkbox"]');
  const output = root.querySelector("pre");
  const hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
  async function inspect(file) {
    const bytes = new Uint8Array(await file.arrayBuffer());
    const magic = new TextDecoder().decode(bytes.slice(0, 4));
    const version = bytes[4];
    const digest = crypto?.subtle
      ? hex(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)))
      : "unavailable: Web Crypto is required for SHA-256";
    let cached = false;
    if (cache.checked && navigator.storage?.getDirectory) {
      const dir = await navigator.storage.getDirectory();
      const blocks = await dir.getDirectoryHandle("factoidal-blocks", { create: true });
      const handle = await blocks.getFileHandle(`${digest}.ibk1`, { create: true });
      const stream = await handle.createWritable();
      await stream.write(bytes);
      await stream.close();
      cached = true;
    }
    output.textContent = JSON.stringify({
      name: file.name, bytes: bytes.length, magic, version,
      recognizedIBK1: magic === blockFormat.magic && version === blockFormat.version,
      sha256: digest, cachedInOPFS: cached,
    }, null, 2);
  }
  input.addEventListener("change", () => input.files?.[0] && inspect(input.files[0]).catch((e) => output.textContent = `Inspection failed: ${e.message}`));
  sample.addEventListener("click", async () => {
    sample.disabled = true;
    output.textContent = "Fetching the bundled IBK1 sample…";
    try {
      const response = await fetch(new URL("../assets/blocks/music.ibk1", location.href));
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      await inspect(new File([await response.blob()], "music.ibk1", { type: "application/octet-stream" }));
    } catch (e) {
      output.textContent = `Sample inspection failed: ${e.message}`;
    } finally {
      sample.disabled = false;
    }
  });
  choose.addEventListener("click", async () => {
    try {
      if (window.showOpenFilePicker) {
        const [handle] = await window.showOpenFilePicker({ types: [{ description: "Factoidal IBK block", accept: { "application/octet-stream": [".ibk1"] } }], multiple: false });
        await inspect(await handle.getFile());
      } else input.click();
    } catch (e) {
      if (e.name !== "AbortError") output.textContent = `Selection failed: ${e.message}`;
    }
  });
  return root;
}

recognizedIBK1: true means only that the five-byte public header agrees with the current format. It is not acceptance: the Lean decoder still checks all lengths, term/ID references and CRC32C, and assurance-grade use also compares the displayed SHA-256 to a digest obtained from a trusted manifest. The next profile is a signed snapshot manifest; a later multi-block profile may use a Merkle root plus a block inclusion proof. Those strengthen what identity the bytes are expected to have; they do not change this canonical block file.

For the full native path and its verified-digest command, see the segmented-IBK design note. The smallest browser deployment target has the same shape: bytes + trusted identity + narrow Lean block-core operation → result bytes. The current IBK3 notebook demonstrates complete-artifact scans; authenticated partial-range execution remains a later interface.

blockFormat is pinned in tests/hub/post46_test.mjs; the picker is deliberately tested in the deployed browser, since file permission must result from a real user gesture rather than a headless test bypass.