Replace bearer tokens with cryptographic proof-of-possession. Signet provides tools for signing commits, files, and HTTP requests using ephemeral certificates with algorithm agility (Ed25519 + ML-DSA-44 post-quantum).
Security Note:
- Not audited - use for development only
- Core cryptography is unit-tested (13k+ LOC) and partially fuzzed (3 fuzz functions covering critical parsing paths)
- Native Go implementation via
go-cms(no external review, passes OpenSSL interop tests) - Platform: Built for macOS, should work on Linux (minimal testing)
- Key storage: Defaults to OS keyring; falls back to plaintext
~/.signet/master.keywhen the keyring is unavailable or initialized with--insecure - TouchID integration (macOS) requires CGO; Linux/CI builds are Pure Go (Static)
- See SECURITY.md for security limitations and best practices
Replace GPG with modern Ed25519 signatures:
# Build and install
make install
# Initialize
signet-git init
# Configure Git
git config --global gpg.format x509
git config --global gpg.x509.program signet-git
git config --global user.signingKey $(signet-git export-key-id)
# Sign commits
git commit -S -m "Signed with Signet"Features:
- 5-minute ephemeral certificates from local CA
- OpenSSL-compatible CMS/PKCS#7 signatures
- Sub-millisecond performance (~0.12ms)
- Completely offline
Sign any file with the same primitives:
# Initialize with Ed25519 (default, shares keys with git signing)
./signet sign --init
# Or initialize with ML-DSA-44 (post-quantum)
./signet sign --init --algorithm ml-dsa-44
# Sign files
./signet sign document.pdf
# Creates document.pdf.sig
# Verify with OpenSSL (Ed25519 signatures)
openssl cms -verify -binary -in document.pdf.sig -inform PEMTwo-step verification middleware for Go HTTP servers:
import "github.com/agentic-research/signet/pkg/http/middleware"
auth, err := middleware.SignetMiddleware(
middleware.WithMasterKey(masterPubKey),
middleware.WithClockSkew(30*time.Second),
)
if err != nil {
log.Fatal(err)
}
handler := auth(yourHandler)Features:
- Ephemeral proof verification (master→ephemeral→request)
- Replay attack prevention
- Pluggable token/nonce stores (memory, Redis with
redisbuild tag) - Clock skew tolerance
- Token revocation via SPIRE-model CA bundle rotation
See pkg/http/middleware/README.md for details.
Mint X.509 client certificates from OIDC login (Fulcio-style).
The hosted authority at auth.notme.bot handles this automatically — no self-hosting required. For self-hosted deployments, see the notme/worker repo (CF Worker) or run the Go authority server directly:
./signet authority --config config.jsonSee cmd/signet/authority.go for Go server configuration details.
One-command onboarding for MCP endpoints with mTLS. The CLI will prompt for your Dashboard and MCP URLs on first run and save them to ~/.signet/config.json.
# Interactive (prompts for URLs on first run, then opens browser)
signet auth login
# Headless (prompts for URLs if not configured, then registers)
signet auth register --github-token $GITHUB_TOKEN
# Check certificate status
signet auth statusFeatures:
- Zero Configuration: Prompts for setup if defaults are not defined.
- OAuth2 + PKCE browser flow with localhost callback (
signet auth login) - GitHub token one-shot registration for headless/CI agents (
signet auth register) - ECDSA P-256 keypair generated locally (private key never leaves machine)
- For browser login: refresh token stored for automatic cert renewal
- For register: no refresh token; re-registers when cert is near expiry
- Auto-configures Claude Code (
claude mcp add) - Idempotent: re-running reuses an existing valid cert or renews when needed
Sign artifacts in GitHub Actions using ambient OIDC credentials (no secrets needed).
The language-neutral Signet Action works for Rust, Go, JavaScript, Python, or any repository that produces files:
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- run: cargo build --release
- uses: agentic-research/signet/.github/actions/sign-artifact@<immutable-commit-sha>
with:
artifacts: |
target/release/my-rust-binary
checksums-sha256.txt
authority-url: https://<cloister-host>/identityThe Action exchanges a GHA OIDC token for a five-minute Notme certificate,
holds the matching Ed25519 key only in memory, signs through the standard
signet://session KMS URI, and verifies every bundle before returning. Point
authority-url at a Cloister cluster's /identity route to use its
cluster-local Notme CA.
Features:
- Zero secrets stored in repo (uses GHA ambient OIDC identity)
- Language-neutral artifact paths
- Standard cosign bundles and immediate verification
- Bridge certificates with capability X.509 extensions
- Policy-based authorization (repo, workflow, ref filtering)
Use Signet keys with the Sigstore ecosystem (cosign, gitsign):
# Build and install the plugin
go build -o sigstore-kms-signet ./cmd/sigstore-kms-signet
mv sigstore-kms-signet /usr/local/bin/
# Sign artifacts with cosign using your Signet key (uploads to Rekor by default)
cosign sign-blob --key signet://default --tlog-upload=true artifact.bin > artifact.sigThe --tlog-upload and Rekor verification flags are handled entirely by cosign; the sigstore-kms-signet plugin only signs the digest it receives over the KMS plugin protocol.
See docs/sigstore-integration.md for full setup, including a "Verifying via Rekor" section with copy-pasteable cosign verify-blob commands.
Authenticate multiple clients via Signet proofs, forwarding requests with a shared upstream token:
go build -o signet-proxy ./cmd/signet-proxy
./signet-proxy --upstream-token $GITHUB_TOKEN --listen :8080See cmd/signet-proxy/README.md for details.
SPIRE-model revocation using CA bundle rotation and epoch-based invalidation:
Features:
- Instant revocation of compromised keys via CA rotation
- Rollback protection with monotonic sequence numbers
- Fail-closed design - infrastructure failures deny access
- Grace periods for smooth key rotation
- Offline-first - no CRL/OCSP dependency
How it works:
// Configure revocation checker
fetcher := cabundle.NewHTTPSFetcher(bundleServerURL, nil)
storage := cabundle.NewMemoryStorage()
cache := cabundle.NewBundleCache(30 * time.Second)
checker := revocation.NewCABundleChecker(fetcher, storage, cache, bundleSigningPublicKey)
// Add to middleware
auth, err := middleware.SignetMiddleware(
middleware.WithRevocationChecker(checker),
)
if err != nil {
log.Fatal(err)
}
handler := auth(yourHandler)Tokens are automatically checked against the CA bundle for:
- Epoch validity (instant revocation of old epochs)
- Key ID matching (CA rotation detection)
- Sequence number monotonicity (rollback attack prevention)
See docs/design/006-revocation.md for architecture details.
Signet's primitives are designed to be used independently:
| Package | Purpose | Status |
|---|---|---|
github.com/agentic-research/go-cms |
Ed25519 CMS/PKCS#7 (standalone library) | |
pkg/crypto/algorithm |
Algorithm registry (Ed25519, ML-DSA-44) with pluggable ops | Internal† |
pkg/crypto/epr |
Ephemeral proof generation/verification | Internal† |
pkg/crypto/cose |
COSE Sign1 for compact wire format | Internal† |
pkg/crypto/keys |
Algorithm-agile signer with zeroization + pluggable backends (PKCS#11, TouchID) | Internal† |
pkg/attest/x509 |
Local CA for short-lived certificates | Internal† |
pkg/signet |
CBOR token structure + SIG1 wire format | Internal† |
pkg/http/middleware |
HTTP authentication middleware (server + client) | Internal† |
pkg/revocation |
SPIRE-model token revocation system | Internal† |
pkg/lifecycle |
Loan-pattern memory zeroization for sensitive data | Internal† |
† Internal = Developed in-house, no independent security audit yet
Release binaries are signed in CI with the cosign-compatible Signet KMS Action. Each ships a Sigstore bundle and a five-minute Notme certificate. Verify before running — pinning the Notme/Cloister CA is what distinguishes a key certified for this workload from one an artifact-replacement attacker created.
VERSION=vX.Y.Z # the release you are installing
PLATFORM=linux-amd64 # or darwin-arm64, linux-arm64
BASE=https://github.com/agentic-research/signet/releases/download/$VERSION
curl -fLO $BASE/signet-$PLATFORM
curl -fLO $BASE/signet-$PLATFORM.sigstore.json
curl -fLO $BASE/signet-$PLATFORM.signet.crt.pem
# Obtain this CA from the configured authority's trust channel, not from the
# release assets you are trying to authenticate. The public-authority default is:
curl -fsS https://auth.notme.bot/.well-known/ca-bundle.pem \
-o trusted-notme-ca.pem
openssl verify \
-CAfile trusted-notme-ca.pem \
signet-$PLATFORM.signet.crt.pem
openssl x509 \
-in signet-$PLATFORM.signet.crt.pem \
-noout -ext subjectAltName
# Require URI:wimse://notme.bot/gha/agentic-research/signet (or the
# cluster's configured equivalent).
AUTHORITY=https://auth.notme.bot
IDENTITY=wimse://notme.bot/gha/agentic-research/signet
cosign trusted-root create \
--with-default-services \
--no-default-ctfe \
--fulcio="url=$AUTHORITY,certificate-chain=trusted-notme-ca.pem" \
--out trusted-signet-root.json
cosign verify-blob \
--trusted-root trusted-signet-root.json \
--certificate-identity "$IDENTITY" \
--certificate-oidc-issuer-regexp '^$' \
--insecure-ignore-sct \
--bundle signet-$PLATFORM.sigstore.json \
signet-$PLATFORM
chmod +x signet-$PLATFORM && mv signet-$PLATFORM /usr/local/bin/signetIf SIGNET_AUTHORITY_URL points at a Cloister cluster, provision that cluster's
pinned Notme CA instead of the public-authority URL above. Do not trust the
.signet.ca.pem copied beside a release merely because it is present there;
an attacker who can replace every release asset could replace that file too.
--insecure-ignore-sct is required because Notme bridge certificates are not
Fulcio CT certificates; it does not disable the Rekor inclusion proof checked
from the bundle.
checksums-sha256.txt is published and signed too, but it is not a substitute:
checksums tell you a download is intact, not who produced it.
git clone https://github.com/agentic-research/signet.git
cd signet
make buildProduces ./signet and ./signet-git binaries.
- Go 1.25+
- OpenSSL (for verification)
Signet is a set of cryptographic primitives with tools built on top:
block-beta
columns 1
block:tools["Tools"]
columns 6
signetgit["signet-git"]
signetsign["signet sign"]
signetauth["signet auth"]
signetauthority["signet authority"]
signetproxy["signet-proxy"]
sigstorекмс["sigstore-kms"]
end
block:middleware["Middleware / Protocol"]
columns 4
httpmw["HTTP middleware"]
revocation["Revocation"]
oidc["OIDC providers"]
policy["Policy eval"]
end
block:core["Core Primitives (pkg/)"]
columns 6
cms["CMS (go-cms)"]
cose["COSE"]
epr["EPR"]
tokens["Tokens"]
localca["LocalCA"]
bridge["Bridge certs"]
end
block:algo["Algorithm Registry"]
columns 2
ed25519["Ed25519 (default)"]
mldsa["ML-DSA-44 (post-quantum)"]
end
tools --> middleware --> core --> algo
All tools share the same master key, certificate authority, and keystore.
# Run tests
make test
# Run integration tests
make integration-test
# Format and lint
make fmt lint- Architecture - Design decisions and technical rationale
- Agent Provenance Standard (APAS) - Research and specifications for AI agent identity
- Design Docs - ADRs and design documents
- Contributing - How to contribute effectively
- Performance - Benchmarks and analysis
- Sigstore Integration - Using Signet keys with cosign/gitsign
- CMS Implementation - Ed25519 CMS/PKCS#7 details (go-cms repo)
Signet is in alpha (v0.2.0).
Recent (v0.2.0):
- ✅ GHA OIDC end-to-end signing flow (ambient identity, bridge certs, policy)
- ✅ Cloudflare Access OIDC provider
- ✅ MCP client authentication (
signet auth login/register) - ✅ Post-merge re-signing workflow
- ✅ Security audit: 7 findings fixed (JTI race, serial collision, rate limiter hardening)
- ✅ Edge identity authority at
auth.notme.bot(CF Worker, zero secrets) - ✅ Cap'n Proto schemas for cross-language type parity (
notme/schema)
Current focus: Go workspace refactor, OAuth port to edge.
Gaps before v1.0:
- Security audit (required before production use)
- Go workspace structure (
go.workwithauthority/as importable module) - Port remaining OAuth flows to CF Worker (eliminate Fly dependency)
- Signature verification CLI (
signet verify)
We welcome contributions! See CONTRIBUTING.md for development setup and guidelines.
High-impact areas:
- Core protocol completion (CBOR, COSE, wire format)
- Language SDKs (Python, JavaScript, Rust)
- Security review and testing
- Documentation and examples
Questions? Open a GitHub Discussion
Problem: Bearer tokens (API keys, JWTs, OAuth tokens) are "steal-and-use" credentials. If an attacker gets your token, they are you. Every attempted fix leaves the core vulnerability intact:
graph TD
classDef rootProblem fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#000
classDef bandAid fill:#fff3e0,stroke:#ef6c00,stroke-width:1px,color:#000
classDef current fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#000
classDef signet fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#000
classDef feature fill:#f1f8e9,stroke:#689f38,stroke-width:1px,color:#000
classDef bridge fill:#fce4ec,stroke:#ad1457,stroke-width:1px,color:#000
Root["The Bearer Token Problem<br/>(Possession = Identity)"]:::rootProblem
subgraph StatusQuo ["Attempted Fixes"]
TR["Token Rotation"]:::bandAid
mTLS["mTLS"]:::bandAid
ZT["Zero Trust"]:::bandAid
SPIFFE["SPIFFE / SPIRE"]:::bandAid
end
subgraph Current ["Current Best Practice"]
OIDC["OIDC Federation<br/>(ambient creds)"]:::current
end
Root --> TR & mTLS & ZT & SPIFFE
Root --> OIDC
TR -->|"Shorter window, same flaw"| Vuln
mTLS -->|"Secures the pipe, not the token"| Vuln
ZT -->|"Validates stolen tokens more frequently"| Vuln
SPIFFE -->|"Workload identity, not agent identity"| Vuln
OIDC -->|"No static secrets...<br/>but output is still bearer"| Vuln
Vuln(("Still Yields a Bearer Asset<br/>(Steal one, become the user)")):::rootProblem
Vuln --> Bridge["Requires moving from Possession to Proof"]:::bridge
Bridge --> Signet
Signet{"Signet: Proof-of-Possession"}:::signet
Signet --> F1["Agent Identity Model<br/>(separate from human)"]:::feature
Signet --> F2["5-minute Ephemeral Certs<br/>(no renewal, just expire)"]:::feature
Signet --> F3["Offline-first Local CA<br/>(epoch-based rotation, no CRL/OCSP)"]:::feature
F1 & F2 & F3 --> End((Stolen Cert = Useless Without Key<br/>Stolen Key = 5 Minutes Max)):::signet
Solution: Cryptographic proof-of-possession. Every request proves knowledge of a private key without revealing it. Tokens can't be stolen and replayed.
Unique features:
- One of the first Go libraries with Ed25519 CMS/PKCS#7 support (via go-cms, not yet security reviewed)
- Post-quantum ready via ML-DSA-44 (FIPS 204) using cloudflare/circl
- Offline-first design (no network dependencies)
- Ephemeral certificates (5-minute lifetime)
- Sub-millisecond verification
- OpenSSL-compatible output
agentic-research/notme— edge identity authority (CF Worker) + Cap'n Proto schemas + reusable GHA workflowsauth.notme.bot— live authority (zero secrets, SigningAuthority DO)notme.bot— the standard (APAS predicate URIs, research)agentic-research/go-cms— Ed25519 CMS/PKCS#7
Apache 2.0 - See LICENSE
Inspired by Sigstore for supply chain security. Signet extends the concept to general-purpose authentication with offline-first design.
Questions? Open an issue Ready to contribute? See CONTRIBUTING.md