skill-sign
- Version: 1.0.3
- License: MIT
- Author: Evan Song · github.com/Songhonglei
- Repository: https://github.com/Songhonglei/build-better-skills/tree/main/skills/skill-sign
- Part of:
build-better-skillssuite — see Stages for the lifecycle map.
Cryptographically sign skill directories with Ed25519, so recipients can verify authenticity and detect tampering — even on machines that never met the author.
Why real signatures (not just hashes)
A plain SHA-256 of the skill detects accidental modification, but anyone can recompute the hash and re-publish under your name. Ed25519 signatures prove both integrity AND authorship — the verifier only needs your public key (safe to share), not your private one.
Dependencies
- Python ≥ 3.9 (stdlib only —
hashlib,json,base64,pathlib,argparse) - Vendored Ed25519 implementation:
scripts/_vendor/ed25519_rfc8032.py- Source: RFC 8032 Appendix A (public domain)
- This is NOT a pip package — it ships in this skill directory. No
pip installneeded.
- Environment variables read (all optional):
XDG_CONFIG_HOME,USER,USERNAME
Security model — read this first
| You can detect | Without --trust-key |
With --trust-key <author's pubkey> |
|---|---|---|
| Accidental file modification after signing | ✅ | ✅ |
| sign.key metadata tampering | ✅ | ✅ |
| Attacker re-signing with their own key | ❌ — anyone can self-sign | ✅ — public key mismatch fails |
The trust-key channel is your responsibility. Get the author's public key from a known-good source (their official GitHub README, a key fingerprint posted in a verified blog, a key file delivered in-person, etc.). Once you have it, every future verify with that key is solid.
This is the same trust model as git commit -S, PGP-signed releases, and
Sigstore — and it has the same caveat: you must establish trust in the
public key out-of-band, once, through a channel you trust.
First-time setup (one-time per machine)
python3 scripts/init.py
Generates an Ed25519 keypair at:
~/.config/skill-sign/private.key— KEEP SECRET (chmod 600)~/.config/skill-sign/public.key— share freely~/.config/skill-sign/public.key.txt— same key as base64 text for copy-paste
🛡️ Back up private.key to a safe place (encrypted USB, password manager).
If lost, you cannot re-sign with the same identity.
Sign a skill (publisher)
python3 scripts/sign.py /path/to/your-skill --version 1.0.0
Writes sign.key inside the skill directory. Share the skill + sign.key
together. Publish your public key (from running scripts/export_public.py)
in a place recipients can trust (your GitHub README, etc.).
Verify a skill (recipient)
Self-verify (proves nothing was changed since signing, but NOT who signed it):
python3 scripts/verify.py /path/to/skill
Trust-verify (proves both integrity AND author identity):
# Option A: trusted key as inline string
python3 scripts/verify.py /path/to/skill \
--trust-key-string ed25519:abc123...
# Option B: trusted key from a file
python3 scripts/verify.py /path/to/skill \
--trust-key ~/.config/skill-sign/trusted/alice.pub
Exit codes: 0 = PASS, 1 = FAIL (tampered / untrusted), 2 = error.
sign.key format
{
"skill_name": "glic-check",
"version": "1.0.1",
"signed_by": "alice",
"signed_at": "2026-06-21T22:00:00+08:00",
"algorithm": "ed25519",
"public_key": "ed25519:<base64 32 bytes>",
"content_hash": "sha256:<hex>",
"signature": "<base64 64 bytes ed25519 signature>"
}
The signature covers a canonical (sorted-key) JSON of all the above
metadata fields (except signature itself). Any modification — to file
content or metadata — invalidates the signature.
What gets hashed
Included: all files under the skill directory (recursive, sorted by path).
Excluded: sign.key itself, .git/, __pycache__/, node_modules/,
*.pyc, *.pyo, *.bak, *.swp, .DS_Store, macOS ._* resource forks.
Limitations (v1.0)
- No key rotation:
--rotate-keyis reserved; for now, manual workflow (init --force → re-sign each skill) is required. - No revocation: if your private key is compromised, you must publish the new public key and tell recipients to re-trust it. No central registry.
- Pure-Python Ed25519 is slow & not side-channel hardened: ~5ms per
sign/verify. Fine for skill publishing (a few times per release); not
for high-frequency or hostile environments. Swap in
cryptography.hazmatif you need that — sign.key format is interoperable.
Workflow for publishing on a skill hub
- Run
scripts/init.py(one-time) - Run
scripts/sign.py my-skill --version 1.0.0 - Commit
sign.keyalong with the skill, publish. - In your skill's README, include:
Signed with: ed25519:abc123...(fromexport_public.py --skill-md) - Recipients trust your key once (from your verified GitHub profile),
then
verify.py --trust-keyevery release.
Scripts
scripts/init.py— Generate Ed25519 keypairscripts/sign.py— Sign a skill directoryscripts/verify.py— Verify signed skillscripts/export_public.py— Print public key for sharingscripts/_lib.py— Shared helpers (file walking, hashing, encoding)scripts/_vendor/ed25519_rfc8032.py— RFC 8032 reference Ed25519 (public domain)