Don't trust us. Verify.
A security tool that reads your AI sessions deserves more scrutiny than a vendor page can answer. So this page does not argue - it hands you the commands that would catch us lying. Skarn's telemetry statement says the scan makes no network call; here is how you check that on your own machine, against the binary you actually run.
Published 2026-07-20.
Watch it run: zero outbound traffic
Run a full-machine scan under a syscall trace. The scan reads session files from disk and writes a report; there is no socket to observe.
On Linux, that is the authoritative per-process answer: every network-family syscall the process makes, printed as it happens. On macOS, where SIP restricts syscall tracing, watch the open sockets instead - -r 1 re-lists them every second for as long as the scan or skarn serve runs, and the only entry that appears is serve's localhost listener:
Or go packet-level on any OS: capture on all interfaces, loopback included (tcpdump -i any, or Wireshark's "any" capture), while skarn assess scans everything on the machine, and observe that the capture holds no skarn traffic. An outbound-firewall tool that alerts per process (Little Snitch or LuLu on macOS, OpenSnitch on Linux) does not prompt for skarn, because there is no connection attempt to prompt about.
Or skip the trust entirely: deny it the network
The scan is offline by construction, so you can enforce the claim instead of verifying it. Everything except the two explicit online commands works identically with no network at all.
The first is a Linux network namespace with nothing in it (-r makes it work unprivileged where user namespaces are enabled; use sudo -E unshare -n on distros that restrict them). The second works on any OS with Docker: the pull is the one online step, then the run has no network interface at all - the example mounts the Claude Code session store read-only, which is what that container run scans (adjust the host path on Windows). firejail --net=none skarn check does the same under Firejail. These runs prove the offline surface genuinely needs no network - a connection attempt is what the traces above would catch - and you can keep them as your standard way of running Skarn permanently. That posture is one a cloud-connected DLP or scanning platform structurally cannot offer: their product stops working exactly where ours is designed to run.
Air-gapped operation is the same property taken to its conclusion: carry the binary in, and for paid tiers a signed license file, and the scan runs with no connectivity at any point in its lifecycle. See air-gapped licensing.
Enforced in our build, not just our copy
The no-egress claim is asserted by the release pipeline itself: the full integration suite - every command surface, including the license and rule-feed paths against local mock servers - runs inside a network-denied namespace on every change and on every release tag. A code change that introduced any new outbound connection would fail the build before it could ship. The complete egress surface (two explicit, user-invoked commands, nothing else) is written down as a normative contract, NETWORK.md, published with the release documents.
Verify the binary is what we built
Per channel: every claim paired with a command you can run today.
- macOS (Apple Silicon): the released arm64 binary is Developer ID signed and notarized by Apple.
codesign -dv --verbose=2 $(which skarn)shows the signing identity and team IDSJ4RVBA27V;spctl -a -vv -t install $(which skarn)confirms notarization. The signing key never leaves the maintainer's machine and is never in CI. (The x86_64 macOS binary ships unsigned for now: Apple's codesign corrupts Zig-built x86_64 binaries, a documented upstream toolchain issue.) - Homebrew: every formula pins the sha256 of its release tarball, so brew refuses an asset that does not match the checksum the pipeline computed from the bytes it built and executed.
- Container image: signed with cosign keyless (Sigstore); the signature is bound to the release workflow's OIDC identity and recorded in the public Rekor transparency log. Verify with
cosign verify ghcr.io/skarn-security/skarn:latest --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp '^https://github.com/skarn-security/skarn/.github/workflows/release.yml@refs/tags/v'- the identity pins the release workflow itself, not just the repository - and pin a version tag or digest instead oflatestin anything reproducible. The image also carries an SPDX SBOM and SLSA provenance attestations. - npm:
@skarn-security/skarnpublishes via npm trusted publishing (OIDC). There is no long-lived publish token that could be stolen - the exact attack vector behind most recent npm supply-chain incidents. - Windows: the install script resolves the release through the public GitHub API and verifies the download against the GitHub-computed digest before installing. That is transport integrity (the bytes arrived unmodified from the release), not build provenance; the signed checksums below add the provenance half.
- Release checksums and SBOM: every release ships a
SHA256SUMSfile over all binaries and tarballs, signed with cosign keyless bound to the release pipeline's identity, plus a CycloneDX SBOM. Verification is two steps: authenticate the sums file withcosign verify-blob SHA256SUMS --bundle SHA256SUMS.sigstore.json --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp '^https://github.com/skarn-security/skarn/.github/workflows/(release|publish-npm).yml@', then check the asset you downloaded against its entry withgrep ' skarn-aarch64-macos.tar.gz$' SHA256SUMS | sha256sum -c -, substituting the file you downloaded. The pipe fails when the sums file carries no entry for that name - unlikesha256sum --ignore-missing, which silently skips a file the list does not mention.
The supply-chain surface is two lines long
Skarn is one static binary with zero package-manager dependencies. The complete third-party inventory is two vendored, statically compiled libraries: PCRE2 (the regex engine, BSD licensed) and the SQLite amalgamation (public domain). There is no dependency tree to audit, no lockfile to poison, no postinstall script in any distribution channel - the npm launcher included - and no dynamic library loaded at runtime. Every published binary is executed on its own architecture by the release pipeline before it publishes: the bytes that ran are the bytes you download.
Questions, answered
- Why should I believe this page?
- You shouldn't have to - that is the point. Every claim above is paired with a command that tests it against the artifact on your machine, not against our word. If any of them ever fails, tell us at [email protected] and quote this page.
- Is the source code available?
- Not publicly today. The checks on this page are deliberately chosen to be verifiable from outside: syscall traces, packet captures, network-denied execution, and signature verification do not require source access, and they bind to the binary you actually run rather than to source we could claim corresponds to it.
- What about a supply-chain attack on your build?
- The realistic vectors are a stolen publish credential or a poisoned dependency. Both are structurally narrowed here: npm publishing has no token to steal (OIDC only), image and checksum signing are keyless and identity-bound in a public transparency log, the macOS signing key never enters CI, release tags are protected against force-push, and the dependency inventory is two vendored libraries whose source is pinned in-tree rather than fetched at build time.
- I ran a check and saw something unexpected.
- Report it to [email protected] (see the disclosure policy). A verified report of undisclosed network behavior would be treated as a critical vulnerability, not a documentation bug.