Safeguard
Supply Chain Security•

Provenance and AIBOM for Agent-Made Code, Skills and Memory

Record model, agent version, skills, task, tools and human approver for every agent-made commit, skill and memory change, then sign it as SLSA provenance.

Safeguard Security Team
46 min read

To give agent-made code, skills and memory real provenance, record for every change the model and provider, the agent and skill versions, the task, the tools used, the human approver and the time. Then store that record where tools can verify it: commit trailers, signed commits, a signed SLSA provenance attestation, and an SBOM linking to the model's AIBOM entry.

Most AIBOM writing is about models and datasets. This post is about the other half: the files a self-improving agent writes for you. A coding agent leaves commits. An agent like Nous Research's Hermes Agent also writes its own skills and memory files, and those change what it does tomorrow. Each of those is a software supply chain artifact with an author, a set of inputs and a reviewer, or the absence of one. The standards to describe them already exist. What most teams lack is the habit of filling them in.

Key takeaways

  • A self-improving agent produces five kinds of artifact: code changes, scripts it runs, skills it writes or patches, memory entries, and session logs. Code and skills can reach production and other people; memory and sessions usually stay local but still steer future output.
  • The minimum provenance record per artifact is: model identifier and provider, agent name and version, skill names with content hashes, task or prompt reference, tools invoked, human approver, and start and finish times.
  • Commit trailers such as Co-Authored-By are labels, not proof. A signed commit proves which key signed. Neither proves which model wrote the code. Proof needs a signed attestation produced by something the agent cannot edit.
  • SLSA provenance v1 maps cleanly onto an agent run: the task goes in externalParameters, the base commit, skills and model in resolvedDependencies, the runner in builder.id, the session id in invocationId.
  • CycloneDX 1.7 can describe the run with formulation and attribute fields to it with the new citations array. SPDX 3.0.1 has a Build class and invokedBy, delegatedTo, usesTool, hasInput and hasOutput relationships that read almost like an agent audit trail.
  • Keep skills and memory in git with signed commits. Hermes Agent already writes a per-mutation skill ledger with SHA-256 manifests; git adds signatures and a shareable history.
  • Regulators are not yet asking for per-commit AI provenance on ordinary software. Customers increasingly ask for SBOMs, signed build provenance and a written AI use policy. Build for those first.

Why agent-made artifacts need their own provenance

An AIBOM entry for a model answers "what is this model, who made it, what was it trained on and under which licence". Our existing posts cover that ground: the state of AI-BOM and ML-BOM standards in 2026, the SPDX 3.0 AI profile implementation guide, and whether you need an AI-BOM entry for a model you only call through an API. This post does not repeat them.

The gap is downstream. When an agent uses that model to write a function, add a dependency, or rewrite one of its own skills, the output is a new artifact. It has its own author (partly the model, partly the agent harness, partly whoever approved it), its own inputs (the repository at a commit, the task text, the skills loaded) and its own risk. If a reviewer later asks "why does this service import that package" or "who taught the agent to deploy this way", the AIBOM entry for the model cannot answer. A provenance record for the change can.

Self-improving agents raise the stakes because the output feeds back in. Hermes Agent's README describes a learning loop with agent-curated memory, autonomous skill creation after complex tasks and skills that improve during use. Its skills documentation says plainly that "The agent can modify or delete any skill." That is a useful feature. It also means the instructions your agent follows next week were partly written by the agent this week, and without provenance you cannot tell which parts.

What artifacts does a self-improving agent produce, and which ship?

The table below uses Hermes Agent as the worked example because its documentation is explicit about where each artifact lives. Paths and defaults were checked against the Hermes docs on the main branch on 23 October 2026, when the latest tagged release was v0.22.5 (published 23 October 2026 on GitHub). Hermes ships several releases a week, so check the docs for your installed version.

ArtifactWhere it lives (Hermes example)Who else sees itCan it reach production or other users?
Code changes and commitsYour repository; with worktree: true, a fresh git worktree per session under .worktrees/Reviewers, CI, every downstream consumerYes, directly, once merged
Dependencies the agent addsManifests and lockfiles in your repoEveryone who builds the projectYes, and they carry their own CVEs
Scripts and helper filesRepo files, or skill scripts/ foldersWhoever runs the skill or the repoYes, if committed or bundled
Skills (SKILL.md plus supporting files)~/.hermes/skills/ by default, or skills.create_dir if setOther sessions, cron jobs, and anyone who installs a profile distribution or a published skillYes, through profile distributions and hermes skills publish
Memory entries~/.hermes/memories/MEMORY.md (2,200 character cap) and USER.md (1,375 character cap)Every later session of that profileNot through distributions, which never ship memories; an export file can include them
Session history~/.hermes/state.db, SQLite with FTS5The agent itself through session_searchOnly if exported
Skill and curator ledgers~/.hermes/skills/.curator_ledger.jsonl, ~/.hermes/skills/.hub/lock.jsonOperatorsNo, but they are your best provenance input

Three of those rows deserve a closer look.

Skills are code with a prose wrapper. A Hermes skill is a folder with a SKILL.md file (frontmatter with name, description and version, then sections for When to Use, Procedure, Pitfalls and Verification) and optional references/, templates/, scripts/, assets/ and examples/ folders. The agent creates and changes them with the skill_manage tool, whose actions include create, patch, delete, write_file and remove_file. A skill's scripts/ folder can hold executable code. When a skill ships in a profile distribution, which the Hermes docs describe as packaging "a complete Hermes agent" as a git repository that anyone with access can install, an agent-written script reaches other people's machines.

Memory is small but sticky. Hermes injects MEMORY.md and USER.md into the system prompt "as a frozen snapshot at session start". The cap keeps it small, but every session of that profile reads it. Profile distributions deliberately never ship memories or sessions. Profile export files are different: the Hermes docs warn that an export "can include memories/, sessions/, and USER.md" and that nothing scans their content for personal data. So memory usually stays on one machine, but it can travel.

Sessions are the evidence. The session transcript is where the task text, the tool calls and the approvals actually happened. hermes sessions export writes JSONL by default, with Markdown, HTML and trace formats, a --session-id selector and a --redact flag that scrubs secrets from exported content. That export, or a hash of it, is the evidence your provenance record points to.

What provenance facts should you capture for each artifact?

Here is the field list we recommend, with where each value comes from in a Hermes setup and where it lands in the formats covered later. You do not need every field on day one. The first five are the ones that answer most incident and audit questions.

FactWhy it mattersWhere to get it (Hermes example)Where to record it
Model identifier and providerTies the change to an AIBOM entry; answers "which model wrote this"model.default and model.provider in ~/.hermes/config.yamlCommit trailer; SLSA resolvedDependencies; CycloneDX machine-learning-model component; SPDX AIPackage
Agent name and versionHarness behaviour changes between releaseshermes --versionCommit trailer; SLSA builder.version or builderDependencies; CycloneDX metadata.tools
Skills loaded, with content hashesSkills are instructions; a changed skill changes outputSHA-256 of each SKILL.md and supporting file; the curator ledger's before and after manifestsSLSA resolvedDependencies; CycloneDX formulation inputs; SPDX hasInput
Task or prompt referenceThe instruction is an untrusted input in SLSA termsSession id plus a hash of the exported user prompts (--only user-prompts)SLSA externalParameters; trailer with session id
Tools invokedShows whether the agent touched the network, the shell or MCP serversSession export; ~/.hermes/logs/SLSA byproducts (hash of the log); CycloneDX formulation steps; SPDX usesTool
Human approverSeparates "agent proposed" from "person accepted"Pull request review, /skills approve, /memory approveReviewed-by trailer; merge commit signature; SPDX delegatedTo
Start and finish timeCorrelates with logs and incidentsSession metadataSLSA startedOn and finishedOn; SPDX buildStartTime and buildEndTime
Runner or host identityWhere the run happened and with which isolationCI runner id, container image digestSLSA builder.id; SPDX hasHost
Base commit and output digestsMakes the record verifiablegit rev-parse HEAD before and after; sha256sum of outputsin-toto subject; SLSA resolvedDependencies
Approval mode and guard settingsContext for how much a human sawapprovals.mode, skills.write_approval, memory.write_approvalSLSA internalParameters

Two cautions apply to the whole table.

First, record identifiers and hashes, not raw content. A task prompt can contain customer data, credentials pasted by mistake, or personal information. A SHA-256 of the redacted export plus the session id lets an auditor with access pull the full record without you spraying it into every commit message.

Second, the model identifier you record is whatever your provider returns. Provider-side model aliases can point to different weights over time. If your provider exposes a dated or versioned model identifier, record that rather than a floating alias, and say in your policy that the identifier is the provider's claim.

How do you mark agent-authored commits, and what do the marks prove?

There are four common marks. They are often confused, and they prove different things. We covered the mechanics of labelling AI-assisted commits with Co-Authored-By and the policy side of disclosing AI-written code in commits and PRs; here is how they fit into provenance.

Commit trailers

The git documentation describes trailers as key and value metadata "at the end of the otherwise free-form part of a commit message". The key is ASCII letters, digits and hyphens. git commit --trailer adds them at commit time and git interpret-trailers adds or parses them in scripts. Signed-off-by is the best-known trailer; git commit --signoff adds it, and git's own docs say its meaning "depends on the project", for example agreement to a Developer Certificate of Origin.

Some agents add an attribution trailer for you. Claude Code's settings reference says commits get "a git trailer such as Co-Authored-By by default", configurable through its attribution setting. We did not find a default attribution trailer documented in the Hermes Agent docs we read, so with Hermes you add your own, either in your instructions to the agent or in a wrapper script.

A useful trailer set for agent work looks like this:

Add retry with backoff to the export client

Agent: hermes-agent 0.22.5
Agent-Model: example-provider/example-model-2026-09-01
Agent-Session: 20261023_101500_a1b2c3d4
Agent-Skills: http-retries@sha256:3f9a...e1, test-driven-development@sha256:77c0...9b
Reviewed-by: Priya Raman <priya@example.com>

What it proves: nothing on its own. Anyone can type any trailer. What it gives you: searchability. git log --format='%h %(trailers:key=Agent-Model,valueonly)' lists every commit with its model, and a CI check can refuse merges from the agent's identity that lack the trailers.

Signed commits

git commit -S signs a commit with GPG, SSH or X.509 keys, and commit.gpgsign true signs every commit. Sigstore's gitsign signs with short-lived certificates tied to an OIDC identity; its README configures it with gpg.x509.program gitsign, gpg.format x509 and commit.gpgsign true, and verifies with gitsign verify --certificate-identity=... --certificate-oidc-issuer=... HEAD. Our Sigstore explainer covers the certificate and transparency log model.

What it proves: the holder of that key or identity created the commit object. If the agent runs under its own bot identity with its own key, a signature from that identity proves the commit came out of the agent's environment, not that a particular model wrote it. If the agent signs with your personal key, the signature proves almost nothing about agent involvement, which is the most common mistake we see.

Pull request labels and bot accounts

A label such as agent-authored on the pull request, or a dedicated machine account that opens agent pull requests, is easy to filter in dashboards and audit queries. It lives in your forge's database rather than in git, so it does not travel with a fork or a mirror. Treat it as a workflow aid that mirrors the trailers.

Signed attestations

An in-toto attestation signed by the runner that hosted the agent, stored next to the commit or the release, is the only mark in this list that can bind "this output, these inputs, this agent, this model, this approver" together under a signature the agent itself could not forge, provided the agent cannot reach the signing identity. The next section shows how.

MarkLives inProvesDoes not prove
Co-Authored-By or custom trailersCommit messageNothing cryptographic; a searchable claimWhich model wrote the code, or that a human reviewed it
Signed-off-byCommit messageThe committer asserted the project's sign-off termsAuthorship by a human
Signed commit (GPG, SSH, gitsign)Commit objectWhich key or OIDC identity created the commitWhich model or agent produced the diff
PR label or bot accountForge databaseHow the change entered reviewAnything after a mirror or fork
Runner-signed in-toto attestationAttestation store, release assetsThat a named builder observed these inputs and produced these outputsThat the model behaved as claimed; that the code is safe

How can SLSA provenance and in-toto describe an agent run as a build step?

The in-toto attestation framework wraps a signed claim in three layers. The middle layer, the Statement, has _type set to https://in-toto.io/Statement/v1, a subject array of artifacts each with a required digest, a predicateType URI and a predicate. The in-toto spec notes that subjects are assumed to be immutable.

SLSA provenance is one predicate type, https://slsa.dev/provenance/v1. SLSA 1.2 is the current version on slsa.dev at the time of writing, and its Build Provenance page keeps that same predicate type URI. The model is simple: a build platform, identified by builder.id, ran a process described by buildDefinition and produced the subjects. buildDefinition holds buildType, externalParameters, internalParameters and resolvedDependencies. runDetails holds builder (with id, version and builderDependencies), metadata (invocationId, startedOn, finishedOn) and byproducts.

The SLSA spec's distinction between parameter types matters for agents. External parameters are "the external interface to the build" and in SLSA "these values are untrusted; they MUST be included in the provenance and MUST be verified downstream". Internal parameters are set by the platform and are trusted because the platform is trusted. A task prompt typed by a person, or an issue body the agent was asked to fix, is exactly an external parameter: untrusted input that a verifier should see.

Here is the mapping we use:

SLSA fieldAgent run meaningExample value
subjectThe outputs: a patch file, a commit, a skill archiveagent-change.patch with its sha256; or a git commit via gitCommit digest
buildTypeYour own URI naming "an agent run on our runner"https://example.com/agent-run/v1
externalParametersTask reference, requester, repository and refSession id, sha256 of redacted user prompts, repo URL, base branch
internalParametersSettings the platform enforcedapprovals.mode, write approval flags, container image
resolvedDependenciesBase commit, skills loaded, model, MCP serversBase commit as gitCommit; each SKILL.md with sha256; model as a URI with annotations
builder.idThe runner that hosted the agentYour CI runner or agent host identity
builder.version / builderDependenciesAgent harness and versionhermes-agent 0.22.5
metadata.invocationIdSession id20261023_101500_a1b2c3d4
startedOn / finishedOnSession start and endRFC 3339 timestamps
byproductsEvidence filessha256 of the redacted session export and the ledger excerpt

What SLSA levels mean for an agent run

Be honest about levels. SLSA's Build track levels are about who generates and signs provenance, and whether the build can tamper with it. If the agent writes its own provenance file on your laptop and you sign it with your key, that is useful documentation but it is self-asserted: the thing being described wrote the description. The stronger pattern is to run the agent inside a hosted, ephemeral CI job and let the platform generate and sign the attestation after the agent step finishes, using an identity the agent's process cannot reach. That mirrors how SLSA expects a trusted build platform to behave.

Even then, the attestation proves what the runner observed: these inputs went in, these outputs came out. It cannot prove the model reasoned correctly, that the code is safe, or that the provider served the model it named. Provenance narrows the questions you must answer during an incident. It does not replace review and testing.

How do CycloneDX and SPDX 3.0 represent the agent, the model and the outputs?

Both formats can describe an agent run today without extensions. They model it differently, and most teams will end up using one for inventory and the in-toto attestation for proof.

CycloneDX 1.7

The CycloneDX specification overview lists 1.7 as the current version, released 21 October 2025 and developed by OWASP. The pieces that matter here, all present in the 1.7 JSON schema:

  • Component types include application, library, file, machine-learning-model and data. The agent harness is an application; the model is a machine-learning-model, which the schema says SHOULD carry a modelCard; each skill can be a file or application component with hashes.
  • metadata.tools is defined as "the tool(s) used in the creation, enrichment, and validation of the BOM". The SBOM generator goes here. The agent goes here only if it actually wrote the BOM.
  • formulation "describes the formulation of any referencable object within the BOM", and the spec lists build pipelines and "AI/ML model training" among the examples. A formula contains workflows; a workflow has tasks, steps, inputs, outputs, timeStart, timeEnd and taskTypes (values include build, test, scan, merge and other). Each step can carry commands with an executed field. An agent session fits as one workflow with one task per logical action.
  • citations, new in 1.7, is "a collection of attributions indicating which entity supplied information for specific fields within the BOM". Each citation has pointers (JSON Pointers into the BOM) or expressions, plus attributedTo (a bom-ref to a component, service, tool, organisation or person) or process (a bom-ref to a formula, workflow, task or step). That lets you say, in a standard field, "this component entry was added by the agent workflow" or "this licence field was supplied by the human reviewer".
  • pedigree.commits records commits with uid, url, author, committer and message, so the agent-made commit and its trailers can sit on the component it changed.

To link the change to the model's AIBOM entry without copying it, use a CycloneDX BOM-Link in externalReferences. A BOM-Link points at a specific BOM by serial number and version, optionally at one bom-ref inside it, using the form urn:cdx: followed by the serial number, a slash, the version, and an optional # plus the bom-ref.

SPDX 3.0.1

SPDX 3.0.1 is the current released version; the v3.1 path on the spec site redirects to a development draft. The Build profile's Build class is "a representation of the process in which a piece of software or artifact is built". It has buildType (required), buildId, buildStartTime, buildEndTime, configSourceUri, configSourceDigest, configSourceEntrypoint, environment and parameter. The spec notes that a build log can be referenced through an ExternalIdentifier with the comment LogReference.

The Core profile supplies the agent vocabulary. SoftwareAgent is defined as "a software program that is given the authority (similar to a user's authority) to act on a system". Relationship types then describe the run:

  • hasInput: "The from Build has each to Element as an input". Use it for the base commit, the task reference and each skill file.
  • hasOutput: "The from Build element generates each to Element as an output". Use it for changed files, new skills and the patch.
  • invokedBy: "The from Element was invoked by the to Agent", with the spec's own example of "a Build element that describes a build step". The Build is invoked by the Hermes SoftwareAgent.
  • delegatedTo: "The from Agent is delegating an action to the Agent of the to Relationship (which must be of type invokedBy)". This is how you say "Priya delegated this run to the agent".
  • usesTool: "The from Element uses each to Element as a tool". The Build uses the model, described as an AIPackage, and any MCP servers.
  • hasHost: "The from Build was run on the to Element". The runner or container.

The AI profile's AIPackage carries properties such as autonomyType, domain, informationAboutApplication, limitation, safetyRiskAssessment and standardCompliance, which is where the model's AIBOM facts live. Our SPDX 3.0 AI profile guide walks through filling them in.

Which format for what

NeedCycloneDX 1.7SPDX 3.0.1in-toto plus SLSA
Inventory of what the change added (dependencies, files, skills)Components with hashes and pedigree.commitsPackages and Files with hasOutputSubjects only
Describe the agent run as a processformulation workflow, tasks, stepsBuild element with relationshipsbuildDefinition and runDetails
Say who delegated to whomcitations.attributedTo plus organisational entitiesdelegatedTo and invokedByNot modelled; use externalParameters
Link to the model's AIBOM entryBOM-Link in externalReferencesusesTool to an AIPackage, or an external referenceresolvedDependencies with annotations
Signed, verifiable claimsignature field (JSF), less common in practiceCan be wrapped in an attestationNative: DSSE envelope, Sigstore bundle
Typical consumerSCA tools, customers asking for SBOMsLicence and compliance tooling, procurementCI gates, admission controllers, auditors

Our recommendation: keep publishing the SBOM format your customers already ask for, add the formulation or Build description for agent runs, and put the binding claim in a signed in-toto attestation. Do not invent a fourth format.

How do you keep skills and memory in git with signed history?

Code is already in git. Skills and memory are the artifacts most teams forget, and in a self-improving agent they are the ones that change most often. Hermes gives you several hooks that make this practical.

Point skill creation at a git repository

Hermes writes new agent-created skills to ~/.hermes/skills/ by default. The skills.create_dir setting redirects creation, and the docs list "a git-tracked repo" as one intended target. Two details from the docs matter:

  • Only creation moves. "Existing skills are still modified in place wherever they live", and bundled skill sync, the hub and the curator keep operating on the profile-local directory.
  • skills.external_dirs "are not a write-protection boundary". If a shared skills directory is writable by the Hermes process, the agent can change it. Use filesystem permissions if a shared directory must stay read-only.

Project-local skills under a repository's .agents/skills/ load only after hermes skills trust, are scanned before entering the index, and are never modified by the curator. That makes a repository the natural home for skills you want reviewed like code.

Use the ledgers Hermes already writes

Hermes records more provenance than most people realise:

  • Every skill mutation, whether from the curator, from the agent's skill_manage calls or from your own CLI commands, appends an entry to the append-only ~/.hermes/skills/.curator_ledger.jsonl. Each entry records the actor (curator, agent or user), the action, evidence including the triggering session id when available, and before and after per-file manifests with SHA-256 hashes. hermes curator ledger --skill NAME reads it.
  • Hub and URL installs record "the source URL, exact content hash, scanner version, findings, timestamp, and fresh-or-cached status" in ~/.hermes/skills/.hub/lock.json.
  • Project skill scan results are cached by content hash outside the repository.

The ledger is a strong local record, but it lives on the agent's own machine, inside a directory the agent can write to. Copy it, or the facts in it, into signed git history or an attestation store so it survives a rebuilt host and cannot be quietly edited.

Gate writes so a human signs off

Two settings stage writes for review instead of applying them, and both are off by default in the current docs:

  • skills.write_approval: true stages every skill_manage write under ~/.hermes/pending/skills/, reviewed with /skills pending, /skills diff ID, /skills approve ID and /skills reject ID.
  • memory.write_approval: true gates memory writes. In the interactive CLI it prompts inline; elsewhere, including the background self-improvement review, writes are staged for /memory pending.

A separate setting, skills.guard_agent_created, runs a dangerous-pattern content scanner on agent-created skill writes and surfaces flagged writes as approval prompts. The docs are clear that it is a scanner, not an approval gate, and the two are independent.

With approvals on, the approval itself becomes a provenance fact. The step-by-step below turns it into a signed commit.

Memory in git: handle with care

MEMORY.md and USER.md are plain files, so git init in the memories directory works. Three cautions before you do it:

  1. USER.md is a profile of a person. Treat the repository as personal data: private, access-controlled, and covered by your retention rules.
  2. Never push it anywhere shared. Profile distributions exclude memories for good reason.
  3. Memory entries pass a Hermes scan for prompt injection, credential exfiltration, SSH backdoor patterns and invisible Unicode before acceptance, but that scan does not make an entry true. Git history lets you see when a bad fact arrived and from which session, which is what you need after a memory poisoning incident.

Step by step: SBOM, attestation and AIBOM link for one agent-made change

This walkthrough takes one agent task from start to a verified, signed record. It assumes a Python repository, Hermes Agent installed on a Linux CI runner or a dedicated host, git 2.34 or later (for --trailer and SSH signing), cosign, and the CycloneDX Python and CLI tools. Replace the example identifiers with your own. Commands were checked against the tools' own documentation on 23 October 2026.

Step 1: Configure the agent for traceable output

Add these keys to ~/.hermes/config.yaml on the host that runs the agent. Every key below is documented in the Hermes configuration, skills or memory pages.

model:
  default: example-model-2026-09-01   # record a dated identifier if your provider offers one
  provider: example-provider

worktree: true            # each session gets its own git worktree and branch

checkpoints:
  enabled: true           # shadow-git snapshots before destructive file operations
  max_snapshots: 20

skills:
  create_dir: /srv/agent-skills/skills   # a git-tracked repository
  write_approval: true                   # stage every skill write for review
  guard_agent_created: true              # scan agent-created skill writes

memory:
  write_approval: true                   # stage or prompt on memory writes

The checkpoint store is a shadow git repository under ~/.hermes/checkpoints/store/; Hermes says your project's real .git is never touched. It helps with recovery, not provenance, because nothing in it is signed.

Step 2: Give the agent its own signing identity

Create a dedicated identity for the agent so its commits are distinguishable from yours. With gitsign in CI, the identity is the workload's OIDC identity. On a long-lived host, an SSH signing key works:

# On the agent host, as the agent's service user
ssh-keygen -t ed25519 -N '' -C 'agent-bot@example.com' -f ~/.ssh/agent_signing
git config --global user.name  "agent-bot"
git config --global user.email "agent-bot@example.com"
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/agent_signing.pub
git config --global commit.gpgsign true

Keep that key out of the directories the agent's tools can read if you can. On Hermes's Docker backend, docker_forward_env is empty by default; do not mount the key into the container. Better still, let the CI platform sign in step 7 and treat the commit signature as a second, weaker signal.

Step 3: Version the skills and memory directories

Warning: the commands below create repositories that will contain whatever your agent has written so far, which may include personal data in USER.md. Back up first, and keep the memory repository private.

# Back up before changing anything
tar -czf ~/hermes-backup-$(date +%Y%m%d%H%M).tar.gz -C ~ .hermes/skills .hermes/memories

# Skills repository (the create_dir target from step 1)
mkdir -p /srv/agent-skills && cd /srv/agent-skills
git init
mkdir -p skills
git add -A && git commit -S -m "Initial import of agent skills"

# Memory history (private, never pushed to a shared remote)
cd ~/.hermes/memories
git init
git add MEMORY.md USER.md && git commit -S -m "Baseline memory snapshot"

Then add a scheduled job, or a post-session hook in your wrapper, that commits any change with the session id in a trailer:

cd ~/.hermes/memories
if ! git diff --quiet; then
  git add MEMORY.md USER.md
  git commit -S -m "Memory update" \
    --trailer "Agent-Session: ${SESSION_ID}" \
    --trailer "Approved-by: ${APPROVER}"
fi

Step 4: Run the task and capture the inputs

Record the base state before the agent starts, then run the task. hermes -z is the documented scripted one-shot mode.

cd /work/repo
BASE=$(git rev-parse HEAD)
AGENT_VERSION=$(hermes --version | head -n1)
cyclonedx-py environment --sv 1.7 --of JSON -o /tmp/sbom-before.cdx.json

# Hash every skill file the agent could load (adjust paths to your setup)
find ~/.hermes/skills /srv/agent-skills/skills -name SKILL.md -print0 \
  | xargs -0 sha256sum > /tmp/skills-before.sha256

hermes -z "Add retry with exponential backoff to the export client and update tests"

After the run, find the session id with hermes sessions list and export a redacted record of it:

SESSION_ID=20261023_101500_a1b2c3d4   # from hermes sessions list
hermes sessions export /tmp/session.jsonl --session-id "$SESSION_ID" --redact
hermes sessions export /tmp/prompts.jsonl --session-id "$SESSION_ID" --only user-prompts --redact
hermes curator ledger --limit 50 > /tmp/skill-ledger.txt

Step 5: Review, commit with trailers, and diff the SBOM

A human reviews the diff in the agent's worktree, and any staged skill writes with /skills diff ID. Once approved, commit with trailers that point back to the evidence:

git add -A
git commit -S -m "Add retry with backoff to the export client" \
  --trailer "Agent: ${AGENT_VERSION}" \
  --trailer "Agent-Model: example-provider/example-model-2026-09-01" \
  --trailer "Agent-Session: ${SESSION_ID}" \
  --trailer "Reviewed-by: Priya Raman <priya@example.com>"

HEAD_AFTER=$(git rev-parse HEAD)
git format-patch -1 --stdout > /tmp/agent-change.patch

cyclonedx-py environment --sv 1.7 --of JSON -o /tmp/sbom-after.cdx.json
cyclonedx-cli validate --input-file /tmp/sbom-after.cdx.json
cyclonedx-cli diff /tmp/sbom-before.cdx.json /tmp/sbom-after.cdx.json --component-versions

The SBOM diff answers the first incident question about any agent change: did it add or upgrade a dependency? If it did, that dependency now needs the same vulnerability, licence and policy checks as one a person added. The Safeguard CLI can run the same check on the working tree with safeguard scan --dir ., which writes results as JSON under ./output.

Step 6: Write the provenance predicate

Build a SLSA provenance predicate that describes the run. Hash the evidence files first:

sha256sum /tmp/session.jsonl /tmp/prompts.jsonl /tmp/skill-ledger.txt

Then write /tmp/agent-run.provenance.json. This is the predicate only; cosign wraps it in the in-toto Statement and fills in the subject in the next step.

{
  "buildDefinition": {
    "buildType": "https://example.com/agent-run/v1",
    "externalParameters": {
      "repository": "https://git.example.com/acme/export-service",
      "baseRef": "refs/heads/main",
      "task": {
        "sessionId": "20261023_101500_a1b2c3d4",
        "promptsSha256": "PROMPTS_SHA256"
      },
      "requestedBy": "priya@example.com"
    },
    "internalParameters": {
      "approvalsMode": "manual",
      "skillsWriteApproval": true,
      "memoryWriteApproval": true,
      "terminalBackend": "docker"
    },
    "resolvedDependencies": [
      {
        "uri": "git+https://git.example.com/acme/export-service@refs/heads/main",
        "digest": { "gitCommit": "BASE_COMMIT_SHA" }
      },
      {
        "name": "skill:http-retries/SKILL.md",
        "digest": { "sha256": "SKILL_SHA256" }
      },
      {
        "name": "model",
        "uri": "https://api.example-provider.com/v1/models/example-model-2026-09-01",
        "annotations": {
          "provider": "example-provider",
          "aibomRef": "urn:cdx:3e671687-395b-41f5-a30f-a58921a69b79/4#model-example-2026-09-01"
        }
      }
    ]
  },
  "runDetails": {
    "builder": {
      "id": "https://ci.example.com/runners/agent-runner-01",
      "version": { "hermes-agent": "0.22.5" }
    },
    "metadata": {
      "invocationId": "20261023_101500_a1b2c3d4",
      "startedOn": "2026-10-23T10:15:00Z",
      "finishedOn": "2026-10-23T10:31:42Z"
    },
    "byproducts": [
      { "name": "session.jsonl", "digest": { "sha256": "SESSION_SHA256" } },
      { "name": "skill-ledger.txt", "digest": { "sha256": "LEDGER_SHA256" } }
    ]
  }
}

The aibomRef annotation is the link to the model's AIBOM entry: a CycloneDX BOM-Link naming the serial number and version of the BOM that describes the model, and the bom-ref of the model component inside it. If your AIBOM lives in SPDX, use the model AIPackage's spdxId instead.

Step 7: Sign the attestations

Sign the provenance over the patch, and attach the after-change SBOM as its own attestation. These use cosign's documented attest-blob flags; --type accepts slsaprovenance1 and cyclonedx among its values.

# Keyless signing with the runner's OIDC identity (CI), writing Sigstore bundles
cosign attest-blob /tmp/agent-change.patch \
  --predicate /tmp/agent-run.provenance.json \
  --type slsaprovenance1 \
  --bundle /tmp/agent-change.provenance.sigstore.json --yes

cosign attest-blob /tmp/agent-change.patch \
  --predicate /tmp/sbom-after.cdx.json \
  --type cyclonedx \
  --bundle /tmp/agent-change.sbom.sigstore.json --yes

On GitHub Actions, actions/attest@v4 does the same job and stores the result in the repository's attestation store. Its README describes three modes: SLSA build provenance when no SBOM or predicate inputs are given, an SBOM attestation when sbom-path is set, and a custom attestation with predicate-type and predicate-path. It needs id-token: write, attestations: write and artifact-metadata: write permissions, and attestations for private repositories require a GitHub Enterprise Cloud plan.

permissions:
  contents: read
  id-token: write
  attestations: write
  artifact-metadata: write

steps:
  - uses: actions/attest@v4
    with:
      subject-path: agent-change.patch
      predicate-type: https://slsa.dev/provenance/v1
      predicate-path: agent-run.provenance.json
  - uses: actions/attest@v4
    with:
      subject-path: agent-change.patch
      sbom-path: sbom-after.cdx.json

Step 8: Describe the run in the SBOM and link the model

Add the agent run to the release SBOM so inventory tools see it without reading attestations. A trimmed CycloneDX 1.7 fragment:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.7",
  "components": [
    {
      "bom-ref": "agent-hermes",
      "type": "application",
      "name": "hermes-agent",
      "version": "0.22.5"
    },
    {
      "bom-ref": "model-ref",
      "type": "machine-learning-model",
      "name": "example-model-2026-09-01",
      "externalReferences": [
        { "type": "bom", "url": "urn:cdx:3e671687-395b-41f5-a30f-a58921a69b79/4#model-example-2026-09-01" }
      ]
    },
    {
      "bom-ref": "pkg-backoff",
      "type": "library",
      "name": "backoff",
      "version": "2.2.1",
      "purl": "pkg:pypi/backoff@2.2.1"
    }
  ],
  "formulation": [
    {
      "bom-ref": "formula-agent-run",
      "workflows": [
        {
          "bom-ref": "wf-20261023_101500_a1b2c3d4",
          "uid": "20261023_101500_a1b2c3d4",
          "name": "Agent session: add retry with backoff",
          "taskTypes": ["other"],
          "timeStart": "2026-10-23T10:15:00Z",
          "timeEnd": "2026-10-23T10:31:42Z",
          "resourceReferences": [
            { "ref": "agent-hermes" },
            { "ref": "model-ref" }
          ]
        }
      ]
    }
  ],
  "citations": [
    {
      "bom-ref": "cite-backoff",
      "pointers": ["/components/2"],
      "process": "wf-20261023_101500_a1b2c3d4",
      "timestamp": "2026-10-23T10:31:42Z"
    }
  ]
}

The citation says, in a standard field, that the backoff component entry came from the agent workflow. The model component carries a BOM-Link to its full AIBOM entry rather than duplicating the model card. Validate the final document with cyclonedx-cli validate before you publish it. If you publish SPDX, the equivalent is a Build element with hasInput to the base commit and skill files, hasOutput to the changed files, invokedBy to a SoftwareAgent for Hermes, usesTool to the model's AIPackage, and delegatedTo from the reviewer.

Step 9: Verify before merge or release

Verification is what turns records into a control. Check the attestation against the identity you expect, not just any valid signature:

cosign verify-blob-attestation /tmp/agent-change.patch \
  --bundle /tmp/agent-change.provenance.sigstore.json \
  --type slsaprovenance1 \
  --certificate-identity "https://ci.example.com/runners/agent-runner-01" \
  --certificate-oidc-issuer "https://token.ci.example.com"

# On GitHub
gh attestation verify agent-change.patch --repo acme/export-service

Then enforce three rules in CI: commits from the agent identity must be signed and carry the Agent, Agent-Model and Agent-Session trailers; a valid attestation must exist for the change before merge; and any new dependency in the SBOM diff must pass your dependency policy. A minimal trailer check:

for c in $(git rev-list origin/main..HEAD --author="agent-bot@example.com"); do
  git log -1 --format='%(trailers:key=Agent-Session,valueonly)' "$c" | grep -q . \
    || { echo "Commit $c lacks Agent-Session trailer"; exit 1; }
done

What can regulators and customers ask for, and what is premature?

This is the area where overclaiming is easiest, so here is what the texts say and what they do not.

EU AI Act

Article 12 of Regulation (EU) 2024/1689 requires that high-risk AI systems "technically allow for the automatic recording of events (logs) over the lifetime of the system", for traceability appropriate to the intended purpose. It applies to systems that fall into the high-risk categories. A coding agent used by a software team is not high-risk by default. If your agent is part of a high-risk system, or is used to build one, the logging practices in this post are the kind of evidence Article 12 expects, but the obligation attaches to the high-risk system, not to every commit an agent writes.

Article 50(2) requires providers of AI systems "generating synthetic audio, image, video or text content" to ensure outputs are "marked in a machine-readable format and detectable as artificially generated or manipulated", with an exception for systems that perform "an assistive function for standard editing" or "do not substantially alter the input data". The obligation falls on the provider of the generating system. Whether and how it applies to source code produced inside a development workflow is not settled by the text, and we are not aware of a harmonised standard that defines machine-readable marking for code. A commit trailer is a reasonable, honest disclosure; calling it Article 50 compliance would be premature. Check the current consolidated text and the Commission's guidance for application dates and any amendments before you rely on a date.

NIST SP 800-218A

NIST SP 800-218A (July 2024) is an SSDF Community Profile that adds AI-specific practices to SSDF 1.1, intended for "producers of AI models, the producers of AI systems that use those models, and the acquirers of those AI systems". It is about developing AI models and systems securely. It does not directly regulate code written by an agent, a point our post on whether SP 800-218A covers AI-written code explores. The underlying SSDF practices, protecting code from tampering, keeping provenance for components and verifying third-party software, apply to agent output just as they apply to human output.

Customers and procurement

The questions we see in security questionnaires now are practical: do you produce SBOMs, do you sign build provenance, do you have a policy for AI-assisted development, and can you show who approved a change. Every item in this post maps to one of those.

AskReasonable today?Evidence you can produce
SBOM for each releaseYesCycloneDX or SPDX document per release
Signed build provenanceYesin-toto attestation with SLSA predicate, verifiable with cosign or gh attestation verify
A written AI-assisted development policyYesPolicy naming approved agents, models, review rules and disclosure trailers
Proof that a human reviewed agent changesYesSigned merge commits, review records, Reviewed-by trailers
List of models used to produce the softwareYes, at the level of models and providersAIBOM entries and the Agent-Model trailer history
Per-line labelling of AI-written codePrematureNo settled standard; trailers work at commit level
Detection that code "was not AI-written"PrematureNo reliable method; do not promise it
A SLSA level for agent runsPremature as a formal claimSLSA levels describe build platforms; describe your setup in plain terms instead
Article 50 marking for source codePrematureText and standards do not yet settle it for code

Common mistakes

  • Signing agent commits with a person's key. It erases the one cryptographic signal that would have told you the change came from the agent's environment. Give the agent its own identity.
  • Treating Co-Authored-By as proof. It is a label. It is valuable for search and disclosure, and it proves nothing about which model wrote the code or whether anyone reviewed it.
  • Letting the agent write and sign its own provenance. A record produced by the thing it describes is documentation, not evidence. Generate and sign it from the runner, after the agent step, with an identity the agent cannot reach.
  • Recording the prompt in full in commit messages. Prompts carry personal data and occasionally secrets. Record the session id and a hash; keep the full redacted export in access-controlled storage.
  • Recording a floating model alias. If the provider moves the alias, your record points to the wrong model. Use a dated or versioned identifier when one exists.
  • Forgetting skills. Teams version code and ignore ~/.hermes/skills/, then cannot explain why the agent's deployment procedure changed. Skills are instructions with executable attachments; version them like code.
  • Assuming external_dirs is read-only. The Hermes docs say it is not a write-protection boundary. Use filesystem permissions.
  • Pushing memory history to a shared remote. USER.md is a profile of a person. Keep it private, and remember export files can include memories even though distributions do not.
  • Inventing a new BOM format. CycloneDX formulation and citations, SPDX Build relationships and in-toto attestations already cover agent runs. Extending them with properties beats a private schema nobody else can read.
  • Skipping the SBOM diff. The dependency an agent adds is the part of its change most likely to carry a future CVE. Diff the SBOM on every agent change and run your dependency policy on the result.

FAQ

What is an AIBOM for agent-made code?

It is the AIBOM entry for the model and agent that produced a change, plus a provenance record that links each agent-made commit, skill or memory change to that entry. In practice that means trailers on the commit, a signed in-toto attestation with a SLSA provenance predicate, and a CycloneDX or SPDX description of the run that points to the model's AIBOM entry.

Does Hermes Agent sign its commits or add a Co-Authored-By trailer?

We did not find a default commit signing setting or attribution trailer in the Hermes Agent docs we read on 23 October 2026. Hermes can commit in isolated worktrees with worktree: true, and it uses whatever git identity and signing configuration the host provides. Configure a dedicated signing identity and add trailers through your instructions or a wrapper.

Does Hermes Agent keep an audit trail of skill changes?

Yes. Every skill mutation appends an entry to ~/.hermes/skills/.curator_ledger.jsonl with the actor (curator, agent or user), the action, the triggering session id when available, and before and after SHA-256 file manifests. Hub installs are recorded in ~/.hermes/skills/.hub/lock.json. The ledger lives on the agent host, so copy it into signed storage if you need it to survive.

Is a signed commit enough provenance for AI-written code?

No. A signed commit proves which key or identity created the commit object. It does not say which model produced the diff, which skills shaped it, what task it answered or who approved it. Pair the signature with trailers for search and a runner-signed attestation for the facts.

Can SLSA provenance describe an agent run?

Yes. Define your own buildType URI for agent runs and map the task to externalParameters, the base commit, skills and model to resolvedDependencies, the runner to builder.id and the session id to invocationId. Be careful about claiming a SLSA level: levels describe how trustworthy the build platform and its provenance are, so describe your setup plainly unless an assessor has reviewed it.

Should agent memory be in git?

For an agent that runs unattended or serves several people, yes, in a private repository with signed commits and the session id in a trailer. It gives you a timeline of when each fact arrived. Keep it off shared remotes because USER.md describes a person.

Does the EU AI Act require labelling AI-written code?

Not clearly. Article 50(2) requires providers of systems generating synthetic text and other content to mark outputs in a machine-readable way, with exceptions for assistive editing. How that applies to source code in a development workflow is not settled by the text. Article 12 logging applies to high-risk AI systems. Disclose agent involvement honestly with trailers and policy, and avoid claiming compliance with an obligation that may not apply.

What is the difference between CycloneDX formulation and an in-toto attestation?

CycloneDX formulation describes how something was made inside a BOM: workflows, tasks, steps, inputs and outputs. An in-toto attestation is a signed statement about specific artifacts identified by digest. Formulation is for inventory and explanation; the attestation is for verification. Use both and link them.

Should the model's AIBOM entry be copied into every SBOM?

No. Link to it. A CycloneDX BOM-Link or an SPDX identifier pointing to the model's AIBOM entry keeps one source of truth for the model card, licence and training data facts, and keeps each change's SBOM small.

Is Hermes Agent safe to run in a production repository?

That depends on configuration more than on the agent. Hermes documents container isolation, approval modes, write approval for skills and memory, and a hardline command blocklist; it also has two published CVE records, covered in our posts on CVE-2026-71963 and CVE-2026-85105. Run it in an isolated worktree, on a container backend, with write approval on, and keep the provenance records described here so you can answer questions afterwards.

Fix it without waiting for upstream

A self-improving agent installs packages, writes scripts and authors skills that become part of your codebase, so every agent change can add open source components that will one day receive CVEs. The agent harness itself, its plugins and its skill scripts are open source components too, and they need the same patching discipline as anything else you ship.

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

Safeguard is not a Hermes Agent integration and does not monitor or govern Hermes specifically. Its fit is with what any agent leaves behind in your repositories. SBOM generation and analysis in SPDX and CycloneDX, attestation and signing with SLSA, Sigstore and in-toto, and policies and gates are generally available, so the SBOM diff, attestation and merge gate steps above can run on the same platform you use for human-written code. AI-BOM discovery, which inventories AI assets such as SDK imports and produces CycloneDX ML-BOM and SPDX AI profile output, is in early access. Safeguard does not detect whether code was written by a model, and it does not replace the commit signing and trailer discipline described here.

The Claude demo shows Claude connected to the Safeguard MCP server with OAuth, choosing which of its hundreds of MCP tools Claude may use. You add it from Claude's connector directory rather than writing a custom MCP configuration. For this topic, that is how an assistant can pull an SBOM or check a dependency an agent just added, with each tool call scoped by the permissions you chose.

Click through it at your own pace, or open it full screen.

The CLI demo is a real terminal session: the Safeguard CLI is asked to fix a lodash CVE, reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, prints a summary and pushes nothing. It is the shape of a well-governed agent change: every command approved, a reviewable diff, nothing pushed without a person. The same session ends with /scan ., the check you would run on any agent-made change before it merges.

Click through it at your own pace, or open it full screen.

The local runner demo sets up a runner on your own machine, approves it with a device code, then scans from the terminal. If your agent runs on a dedicated host, this is how scans of its worktrees can run on that host without the code leaving it.

Click through it at your own pace, or open it full screen.

To try it on your own repositories, download Safeguard and follow the CLI documentation.

Quick check

An agent commit carries a valid signature and a Co-Authored-By trailer. What does that prove about which model wrote it?

Nothing. The signature proves which key or identity created the commit object, and the trailer is a typed label. To bind the change to a model, record the model identifier in a runner-signed in-toto attestation, ideally with a SLSA provenance predicate, generated by something the agent cannot edit.

In SPDX 3.0.1, which relationship records that a reviewer handed a task to an agent?

delegatedTo. It runs from the delegating Agent (the reviewer) to an invokedBy relationship, which itself records that the Build was invoked by the SoftwareAgent.

Sources

Last reviewed: 23 October 2026. Hermes Agent ships several releases a week; check the linked docs for current defaults and config keys before you copy them.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.