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 verifierThe component that runs elsewhere, typically in CI: it sends a nonce, obtains references itself and compares. 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 attesterThe component on the machine being checked. It collects facts and reports them; it never decides the result. or the verifier in another language from the specification.
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:
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.
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 and 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.
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.
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.
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.
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.
.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.
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.
Rust#
Build with 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.
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 SigstoreA public signing service: short-lived certificates from Fulcio tied to an OIDC identity, with signatures logged in Rekor.), 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 and 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 and 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 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, or implement the format in another language from 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 TPMTrusted Platform Module: a chip with keys that cannot be exported and registers (PCRs) that can be extended but never set. 2.0 with IMAIntegrity Measurement Architecture: the Linux kernel hashes files as they are used, logs each hash and extends PCR 10., or run in an AMD SEV-SNPAMD Secure Encrypted Virtualization with Secure Nested Paging: confidential VMs whose launch measurement the AMD chip signs. or Intel TDXIntel Trust Domain Extensions: confidential VMs whose measurement is signed through Intel's quoting enclave. confidential VM, to make a forged answer fail. See TPM attestation, Confidential computing and Forged answers.