# Integration guide by language and stack

Attestium verifies a service by how it is built and deployed, not by the language it is written in. For each stack, the verifier needs a public reference for every file that runs: the git commit for your own code, a lockfile with hashes for installed packages, and a release, image digest or signed archive for everything else.

The library itself runs on Node.js 18 or later, on Linux. The service under test can be written in any language. The evidence format is open, so you can also write the attester or the verifier in another language from the [specification](https://attestium.com/spec/).

## Supported stacks at a glance

| Stack | Attester reads | Verifier compares with | Needs in your repository |
| --- | --- | --- | --- |
| Node.js (npm, pnpm) | `node_modules` | Registry tarballs pinned by the lockfile | `pnpm-lock.yaml`, `npm-shrinkwrap.json` or `package-lock.json` |
| Python | Virtual environments | Wheels pinned by the lockfile | `uv.lock`, `pylock.toml`, `poetry.lock`, `Pipfile.lock` or hashed `requirements.txt` |
| Ruby | `vendor/bundle` | `.gem` files pinned by `CHECKSUMS` | `Gemfile.lock` from Bundler 2.6 or later |
| Elixir, Erlang | `deps/` | Hex tarballs pinned by outer checksums | `mix.lock` |
| PHP | `vendor/` | The git tree of the pinned commit | `composer.lock` |
| Java, Kotlin | Directories of jars | Jar hashes the build pins | `gradle/verification-metadata.xml` or a Maven `lockfile.json` |
| .NET | A published application | Packages pinned by content hash | `packages.lock.json` |
| Go | Build information inside the binary | `go.sum` and the commit | `go.mod`, `go.sum` |
| Rust | The cargo-auditable crate list | `Cargo.lock` | `Cargo.lock`, a binary built with cargo-auditable |
| Containers | Image, mounts, writable layer, root filesystem | Image layers fetched by digest | An image pushed to a registry, referenced by digest |
| Debian, Ubuntu | The owning package of each running file | The `.deb` from the signed `InRelease` file | Nothing: the archive is the reference |

Seven package ecosystems share one plugin interface, `ecosystems.INSTALLED`: `npm`, `pypi`, `rubygems`, `hex`, `composer`, `maven` and `nuget`. Go and Rust binaries, containers and distribution packages have their own modules.

## One loop for every package ecosystem

`ecosystems.detectInstalls(root)` finds installed packages under a project root for all seven ecosystems. Each plugin then scans on the attester and compares on the verifier:

```js
const {ecosystems} = require('attestium');

(async () => {
  const store = new ecosystems.ReferenceStore({cacheDir: './cache'});
  for (const install of ecosystems.detectInstalls('./app')) {
    const plugin = ecosystems[install.ecosystem];
    const scan = await plugin.scan(install.dir, {root: './app'}); // attester
    const lock = plugin.readLock('./reference'); // verifier, its own checkout of the commit
    const result = await plugin.compare({scan, lock, store});
    console.log(install.ecosystem, result.passed, result.summary);
  }
})();
```

A package that differs from its reference is `failed`. A package no reference can exist for, such as one pinned without a hash, is `unverifiable`. A reference that could not be fetched is `error`. Any of the three makes `passed` false. The full loop, with release checks and missing lockfiles, is in [How a package check works](https://attestium.com/docs/ecosystems/#how-a-package-check-works).

## Node.js

Commit the lockfile and deploy with a frozen install (`pnpm install --frozen-lockfile` or `npm ci`). Each package in `node_modules` is compared with the registry tarball whose SRI hash the lockfile pins. pnpm patches, bundled dependencies and packages allowed to run install scripts are accounted for. The `node` binary is compared with the official release archive listed in `SHASUMS256.txt`. Details: [npm and pnpm](https://attestium.com/docs/ecosystems/#npm-npm-and-pnpm) and [Language runtimes](https://attestium.com/docs/ecosystems/#language-runtimes).

## Python

Use a lockfile that pins hashes, such as `uv.lock`, and install into a virtual environment. Install without compiling bytecode (uv's default, or `pip install --no-compile`) and run with `PYTHONDONTWRITEBYTECODE=1`: `.pyc` files cannot be compared with anything and are reported as warnings. A package built on the server from a source distribution is unverifiable. Details: [PyPI](https://attestium.com/docs/ecosystems/#pypi).

## Ruby

Run `bundle lock --add-checksums` so `Gemfile.lock` carries a `CHECKSUMS` section, and install into `vendor/bundle`. Gems from git sources are unverifiable. Native extensions compiled on the server are reported as warnings, because their output has no published reference. Details: [RubyGems](https://attestium.com/docs/ecosystems/#rubygems-bundler).

## Elixir and Erlang

`mix.lock` pins the outer checksum of each Hex tarball, and the verifier compares `deps/` with it. The code that runs is compiled from those sources into `_build` or a release, so verify that output by reproducing the build. Details: [Hex](https://attestium.com/docs/ecosystems/#hex-erlang-elixir).

## PHP

`composer.lock` pins each package to a commit of its source repository. The verifier reads that commit's tree from a blobless git clone and compares `vendor/` file by file. The generated autoloader is build output: reproduce the build to cover it. Details: [Composer](https://attestium.com/docs/ecosystems/#composer-php).

## Java and Kotlin

Turn on Gradle dependency verification (`gradle/verification-metadata.xml`) or use maven-lockfile, so every jar has a pinned SHA-256. Jars the build does not pin are compared with Maven Central and reported as warnings. Your application's own jar is build output. Details: [Maven](https://attestium.com/docs/ecosystems/#maven-java).

## .NET

Set `RestorePackagesWithLockFile` so `packages.lock.json` pins the SHA-512 of every package. Every assembly and native library in the published directory must be a file one of the locked packages ships. The application's own assembly and `deps.json` are verified by reproducing the build. Details: [NuGet](https://attestium.com/docs/ecosystems/#nuget-net).

## Go

A Go binary built with Go 1.18 or later records its module, every dependency with its `go.sum` hash, and the commit. `elf.goBuildInfo()` reads that record and `ecosystems.go.compareBuildInfo()` checks it against `go.sum` at the deployed commit. Build with `-trimpath` so the binary can be reproduced. The record proves which dependencies were compiled in; the binary's own hash, matched with a reproduced build, an attested release or a signed checksum list, proves the binary. Details: [Go binaries](https://attestium.com/docs/ecosystems/#go-binaries).

## Rust

Build with [cargo-auditable](https://github.com/rust-secure-code/cargo-auditable) so the binary carries its crate list. `ecosystems.cargo.compareAuditable()` checks that list against `Cargo.lock`. As with Go, the binary's own hash is what proves the binary. Details: [Rust binaries](https://attestium.com/docs/ecosystems/#rust-binaries).

## Native binaries and releases deployed without git

A database server, a proxy or any downloaded program is explained by its distribution package, a signed checksum list (gpg, minisign or Sigstore), a GitHub artifact attestation for its SHA-256, or a pinned hash. For your own compiled or bundled releases, publish a release manifest from CI and attest it. Details: [Native binaries](https://attestium.com/docs/ecosystems/#native-binaries) and [Release manifests](https://attestium.com/docs/signatures/#release-manifests).

## Containers and Kubernetes

The attester finds the container of each process from its cgroup (Docker, containerd, CRI-O, Podman and Kubernetes), reads the image the runtime names, and hashes every file of the root filesystem. The verifier fetches the image by digest from the registry, applies the layers in order and compares. Deploy by digest, keep code out of volumes, and attest your images in CI if you want the verifier to require the build workflow. Details: [Containers and OCI images](https://attestium.com/docs/ecosystems/#containers-and-oci-images) and [Container image attestations](https://attestium.com/docs/signatures/#container-image-attestations).

## Every process, whatever its runtime

`ProcessIntegrity` recognizes 10 runtimes: Node.js, Python, the JVM, Ruby, .NET, BEAM, PHP, Perl, Deno and Bun. For each process it compares executable memory with the files it maps and reports the runtime's code injection vectors, such as `NODE_OPTIONS`, `PYTHONPATH`, `JAVA_TOOL_OPTIONS`, `RUBYOPT`, `DOTNET_STARTUP_HOOKS`, `ERL_FLAGS` and `LD_PRELOAD`, as well as debuggers, open inspectors and `memfd` code. Keep these variables unset in production, or the verifier reports them.

## Two ways to adopt

*   **Use Audit Status.** [Audit Status](https://auditstatus.com/) is a ready-made attester and verifier built on Attestium. It detects the languages and package managers of a repository, runs the verifier on a schedule in CI and publishes reports and a badge. No code to write.
*   **Build on the library.** Use the modules above to write your own attester and verifier in Node.js, starting from [Getting started](https://attestium.com/docs/getting-started/), or implement the format in another language from [Other languages](https://attestium.com/docs/other-languages/), which gives canonical JSON rules, a Python reference and test vectors.

## Raise the evidence level

Everything above is software evidence: it detects drift, failed deploys and modified files, but root on the server controls the attester and can forge it. Add a TPM 2.0 with IMA, or run in an AMD SEV-SNP or Intel TDX confidential VM, to make a forged answer fail. See [TPM attestation](https://attestium.com/tpm-attestation/), [Confidential computing](https://attestium.com/confidential-computing/) and [Forged answers](https://attestium.com/docs/forged-answers/).
