> ## Documentation Index
> Fetch the complete documentation index at: https://www.towbar.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# CyberChef

> CyberChef is a browser-based toolkit for encoding, decoding, and transforming data.

export const UseCaseNavigation = () => {
  useLayoutEffect(() => {
    const storageKey = "towbar-use-case-open-groups";
    const groupSelector = "#sidebar li[data-title] > button[aria-expanded]";
    let restoring = false;
    let frame = 0;
    function storedGroups() {
      try {
        return new Set(JSON.parse(sessionStorage.getItem(storageKey) || "[]"));
      } catch {
        return new Set();
      }
    }
    function save(groups) {
      try {
        sessionStorage.setItem(storageKey, JSON.stringify([...groups]));
      } catch {}
    }
    function groupName(button) {
      return button.parentElement?.dataset.title;
    }
    function rememberOpenGroups() {
      const groups = new Set();
      for (const button of document.querySelectorAll(groupSelector)) {
        if (button.getAttribute("aria-expanded") === "true") {
          const name = groupName(button);
          if (name) groups.add(name);
        }
      }
      save(groups);
    }
    function restoreOpenGroups() {
      frame = 0;
      const groups = storedGroups();
      if (!groups.size) return;
      restoring = true;
      for (const button of document.querySelectorAll(groupSelector)) {
        if (groups.has(groupName(button)) && button.getAttribute("aria-expanded") === "false") {
          button.click();
        }
      }
      restoring = false;
    }
    function scheduleRestore() {
      if (!frame) frame = requestAnimationFrame(restoreOpenGroups);
    }
    function onClick(event) {
      if (restoring || !(event.target instanceof Element)) return;
      const button = event.target.closest(groupSelector);
      if (button) {
        const name = groupName(button);
        if (!name) return;
        const groups = storedGroups();
        if (button.getAttribute("aria-expanded") === "true") groups.delete(name); else groups.add(name);
        save(groups);
        return;
      }
      if (event.target.closest('a[href^="/docs/use-cases/"]')) {
        rememberOpenGroups();
      }
    }
    const observer = new MutationObserver(scheduleRestore);
    document.addEventListener("click", onClick, true);
    observer.observe(document.getElementById("sidebar") || document.body, {
      childList: true,
      subtree: true
    });
    scheduleRestore();
    return () => {
      document.removeEventListener("click", onClick, true);
      observer.disconnect();
      cancelAnimationFrame(frame);
    };
  }, []);
  return null;
};

<UseCaseNavigation />

**Upstream source:** [Current installation guide](https://github.com/gchq/CyberChef#running-locally).

The official CyberChef image serves the web application directly. This recipe has no server-side database, user accounts, or persistent Service volume.

## Towbar manifest

```yaml title=".towbar/services/cyberchef.service.yml" theme={"system"}
id: cyberchef
name: CyberChef
buildServer: null
deployment:
  type: image
  image: ghcr.io/gchq/cyberchef@sha256:f04a39926344faf8e4cbbc8c92fb125d2b466f9be62db09aff23c7c406101a51
container:
  port: 8080
environments:
  production:
    server: 192.0.2.10
domains:
  primary: cyberchef.example.com
tls:
  mode: direct
```

## Configure

1. Prepare the example server, connect the repository, and map `production` to the branch containing these manifests. Replace the example server IP and domain. Commit the manifests, then sync the repository and inspect the resolved configuration.
2. Deploy the Service, then perform the checks below before enabling auto-deploy.

## Verify

Open the page, run a simple Base64 encode/decode operation, and confirm it also works after redeployment.

For field constraints, see [Service manifest](/docs/services/manifest).
