Skip to content

nolabs-ai/agent-sign

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nono Attest

Cryptographic provenance for AI agent instructions and packages
Sign SKILLS.md, CLAUDE.md and related artifacts with Sigstore and Nono keyless attestation, or publish full nono packages to the registry with the same GitHub Actions identity.

Quick Start | How It Works | Package Publishing | Inputs | Verification | nono CLI


Why?

AI agents read instruction files to determine what they can do. If those files are tampered with, the agent follows malicious instructions. nono packages raise the stakes further by distributing profiles, hooks, trust policy, and project instructions together. This action creates a cryptographic chain of trust for both cases: files can be signed in CI, and full nono packages can be signed and published with the same workflow identity.

The result is a Sigstore bundle containing a DSSE envelope with an in-toto statement, a Fulcio certificate (binding GitHub Actions OIDC identity to the signature), and a Rekor transparency log inclusion proof. No private keys involved — identity is derived from the CI workflow itself.

Quick Start

name: Sign files
on:
  push:
    branches: [main]
    paths:
    - 'SKILL.md'
    - 'scripts/some-script.py'

permissions:
  id-token: write
  contents: write

jobs:
  sign:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: always-further/agent-sign@v0.0.2

That's it. This signs all files matching the trust policy's includes patterns, commits the .bundle sidecars, and verifies the signatures as a smoke test.

Package Publishing

agent-sign also supports publishing full nono packages to the registry. In publish mode it:

  1. Installs the nono CLI
  2. Signs each package artifact with a per-file .bundle
  3. Exchanges the GitHub OIDC token for a short-lived registry upload token
  4. Uploads the artifacts and matching bundle sidecars

Example:

name: Publish nono package

on:
  push:
    tags:
      - "v*"

permissions:
  contents: read
  id-token: write

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: always-further/agent-sign@main
        with:
          publish: "true"
          package-name: claude-code
          package-version: ${{ github.ref_name }}
          nono-version: latest
          package-path: .
          files: |
            package.json
            claude-code.profile.json
            CLAUDE.md
            hooks/nono-hook.sh
            groups.json
            trust-policy.json

If files is omitted in publish mode, the action recursively discovers non-hidden files under package-path, excluding README.md and existing *.bundle sidecars.

How It Works

  1. Installs the nono CLI from GitHub releases
  2. Signs files using nono trust sign --keyless
  3. GitHub Actions OIDC provides the identity token automatically
  4. Fulcio issues a short-lived certificate binding the OIDC claims (repository, workflow, ref) to an ephemeral signing key
  5. The signature is submitted to Rekor for transparency logging
  6. The resulting bundle contains everything needed for offline verification

Multi-Subject vs Per-File Bundles

By default, all specified files are signed together into a single .nono-trust.bundle. One signature covers the entire set — modify any file and the whole bundle is invalidated. This is the recommended approach for related instruction files.

Mode Bundle Output Use Case
Multi-subject (default) .nono-trust.bundle Sign related files together. Atomic verification. Any change invalidates the bundle.
Per-file (per-file: true) <file>.bundle for each Independent signatures. Files can be updated and verified separately.

Examples

Upload bundles as workflow artifacts

- uses: always-further/agent-sign@v0.0.2
  with:
    commit: "false"
    upload-artifacts: "true"

Sign specific files together

- uses: always-further/agent-sign@v0.0.2
  with:
    files: "SKILLS.md CLAUDE.md config/settings.json"

Sign files separately

- uses: always-further/agent-sign@v0.0.2
  with:
    files: "SKILLS.md CLAUDE.md"
    per-file: "true"

Pin a specific nono version

- uses: always-further/agent-sign@v0.0.2
  with:
    version: "v0.6.0-alpha.3"

Publish a package to a development registry

- uses: always-further/agent-sign@main
  with:
    publish: "true"
    package-name: claude-code
    package-version: ${{ github.ref_name }}
    nono-version: latest
    package-path: .
    registry-url: "http://localhost:3001/api/v1"

Custom trust policy for verification

- uses: always-further/agent-sign@v0.0.2
  with:
    trust-policy: "trust-policy.json"

Inputs

Input Default Description
nono-version latest nono CLI version to install
files (empty) Whitespace-separated list of files to sign. Empty = --all (matches trust policy includes)
per-file false Sign each file separately instead of a single multi-subject bundle
commit true Commit bundle files back to the repository
upload-artifacts false Upload bundle files as workflow artifacts
verify true Run verification after signing
trust-policy (empty) Path to trust-policy.json for verification
working-directory . Working directory for signing
commit-message chore: update attestation bundles [skip ci] Commit message
publish false Publish a nono package version to the registry
package-version (empty) Registry package version for publish mode
package-name (empty) Registry package name for publish mode
package-namespace (empty) Registry namespace for publish mode. Defaults to the owner of GITHUB_REPOSITORY
registry-url https://registry.nono.sh/api/v1 Registry API base URL for publish mode
package-path . Package directory for publish mode

Requirements

The workflow must have id-token: write permission for Sigstore keyless signing and package publishing. If commit: true (the default for instruction mode), it also needs contents: write.

permissions:
  id-token: write
  contents: write

For package publishing, contents: read is enough unless your workflow also commits changes:

permissions:
  id-token: write
  contents: read

Verification

Consumers verify bundles using a trust-policy.json that defines trusted publishers:

{
  "version": 1,
  "includes": ["SKILLS*", "CLAUDE*", "AGENT*", ".claude/**/*.md"],
  "publishers": [
    {
      "name": "my-org CI",
      "issuer": "https://token.actions.githubusercontent.com",
      "repository": "my-org/my-repo",
      "workflow": ".github/workflows/sign-instruction-files.yml",
      "ref_pattern": "refs/heads/main"
    }
  ],
  "blocklist": { "digests": [] },
  "enforcement": "deny"
}

Verify locally:

nono trust verify --policy trust-policy.json --all

Or enforce at runtime — nono's pre-exec scan verifies all files matching the trust policy before the agent can read them:

nono run --profile claude-code -- claude

For package publishing, consumers verify the downloaded artifacts during nono pull using the Sigstore bundle, Fulcio identity, Rekor inclusion proof, and namespace-bound publisher identity returned by the registry pull manifest.

Source to Runtime Attestation

This action is the source half of nono's attestation pipeline. The runtime half happens when an agent is launched through nono run. Together they form an unbroken cryptographic chain from CI to execution. For full technical details, see Signing and Attestation Internals.

The attestation stack

Each signed file produces a three-layer cryptographic artifact:

Attestation stack

For keyless signing (the default in CI), the signer identity is extracted from the GitHub Actions OIDC token and embedded in a short-lived Fulcio certificate. The certificate binds the OIDC claims -- repository, workflow file, branch ref -- to an ephemeral signing key. The signature is then logged in Rekor, providing a timestamp proof that the signature was created while the certificate was valid.

What happens at runtime

When an agent is launched through nono, the sandbox enforces attestation before the agent can read any file matching the trust policy:

Runtime verification flow

The agent process never observes unverified content. On Linux, the seccomp supervisor mediates every file open. On macOS, kernel-level Seatbelt rules prevent reads entirely unless the file was verified at startup.

Why this matters

Without runtime enforcement, signing is advisory -- an attacker who modifies a file after it was signed can still trick the agent. nono closes this gap:

  • No trust-on-first-use: A valid signature from a trusted publisher is required on first encounter. There is no grace period.
  • Blocklist precedence: Known-malicious file digests are rejected regardless of whether they have a valid signature.
  • TOCTOU protection: On Linux, the supervisor re-verifies the file descriptor's digest after opening, preventing file swaps between verification and read.
  • Policy self-protection: The trust policy document itself requires a signed attestation. An attacker cannot modify trust-policy.json to self-authorize without a valid signature from a trusted publisher.

Verification details

Keyless bundle verification confirms four things:

Check What it proves
Fulcio certificate chain The certificate was issued by Sigstore's CA
Rekor inclusion proof The signature was logged in the transparency log within the certificate's validity window (10-20 minutes)
ECDSA signature validity The DSSE envelope was signed by the certificate's key
OIDC claim matching The Fulcio certificate's X.509 extensions (issuer, repository, workflow, ref) match the trust policy's publisher definition

The relevant Fulcio certificate extensions:

OID Field
1.3.6.1.4.1.57264.1.1 OIDC issuer
1.3.6.1.4.1.57264.1.8 Source repository
1.3.6.1.4.1.57264.1.9 Repository ref
1.3.6.1.4.1.57264.1.11 Build config (workflow URI)

Bundle Output Options

Option Use Case
commit: true (default) Bundles live alongside files in version control. Consumers get them on git clone.
upload-artifacts: true Bundles available as downloadable workflow artifacts. Useful for release pipelines.
Both Commit for development, artifacts for release automation.

Companion Artifacts

Instruction files often reference companion artifacts (scripts, configs, data files). Use multi-subject signing to attest them together:

- uses: always-further/agent-sign@v0.0.2
  with:
    files: "SKILLS.md lib/helper.py config/settings.json"

This produces a single .nono-trust.bundle that attests all three files. If any file is modified, the entire bundle becomes invalid, ensuring the agent only receives a consistent, verified set of files.

License

Apache-2.0

About

Provide full security provenance and agent attestations from source to runtime

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages