Signing Dossiers: Practical Guide
Last Updated: 2026-06-14 Status: Active
Overview
Signing is what turns a skill into a trusted skill — the single biggest thing a dossier adds over a plain SKILL.md. A plain skill has no way to prove who wrote it or that it hasn’t been tampered with; a signed dossier does, and ai-dossier install-skill / run verify that signature before the agent executes anything.
This guide covers the practical steps for signing dossiers locally and in CI/CD. It complements the Key Management documentation with hands-on procedures and troubleshooting.
Prerequisites
- Node.js installed (for signing tools)
- AWS credentials configured (for AWS KMS signing)
- Access to the dossier repository
Local Signing (Development)
Two Signing Methods
Dossier supports two signing methods:
- AWS KMS - For official imboard-ai team dossiers (requires AWS credentials)
- Ed25519 - For community contributors (no special access needed)
Using Ed25519 (Community Contributors) âś… RECOMMENDED
Community contributors should use Ed25519 signing, which uses Node.js built-in crypto (no external dependencies).
Step 1: Generate Your Key Pair
The CLI does this for you, writing ~/.dossier/<name>.pem (private, 0600) and
~/.dossier/<name>.pub (public), and printing the public key in its canonical
raw base64 form:
ai-dossier keys generate --name my-name-2025
Or generate the pair by hand
# Generate Ed25519 key pair
node -e "
const { generateKeyPairSync } = require('crypto');
const fs = require('fs');
const { privateKey, publicKey } = generateKeyPairSync('ed25519', {
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
publicKeyEncoding: { type: 'spki', format: 'pem' }
});
// Save keys
fs.writeFileSync('my-signing-key.pem', privateKey, { mode: 0o600 });
fs.writeFileSync('my-public-key.pem', publicKey);
console.log('âś… Keys generated:');
console.log(' Private key: my-signing-key.pem (keep this secret!)');
console.log(' Public key: my-public-key.pem (share this)');
"
The steps below use the hand-generated my-signing-key.pem / my-public-key.pem
names; if you used keys generate, substitute ~/.dossier/my-name-2025.pem and
~/.dossier/my-name-2025.pub.
Step 2: Sign a Dossier
# Sign with your Ed25519 key
ai-dossier sign --method ed25519 path/to/your-dossier.ds.md \
--key my-signing-key.pem \
--key-id "my-name-2025" \
--signed-by "Your Name <your.email@example.com>"
Step 3: Verify the Signature
# Verify locally (requires adding your key to trusted keys).
# The `--` is required: a PEM starts with "-", which the option parser would
# otherwise read as a flag. The key is stored in its canonical raw base64 form.
ai-dossier keys add -- "$(cat my-public-key.pem)" "my-name-2025"
ai-dossier verify path/to/your-dossier.ds.md
Step 4: Publish Your Public Key
Add your public key to your repository so others can verify your signatures.
Publish the canonical raw base64 form, not the PEM. It is a single line, so
readers can copy it straight into keys add without the -- escape, and it is
exactly what trusted-keys.txt stores:
# Canonical form = the last 32 bytes of the SPKI DER, base64-encoded.
# (`ai-dossier keys generate` prints this directly as "Public key (base64)".)
KEY=$(openssl pkey -pubin -in my-public-key.pem -outform DER | tail -c 32 | base64)
# Write it to KEYS.txt (unquoted heredoc, so the variables expand)
cat > KEYS.txt << EOF
# Dossier Author Public Keys
## Your Name (your.email@example.com)
- **Key ID**: my-name-2025
- **Algorithm**: Ed25519
- **Created**: 2025-11-24
- **Public Key**: \`${KEY}\`
EOF
Readers then trust you with one line — no PEM, no --:
ai-dossier keys add "<the base64 string from KEYS.txt>" "my-name-2025"
Using AWS KMS (Official Dossiers)
AWS KMS signing requires AWS credentials with appropriate permissions.
Step 1: Verify AWS Credentials
# Check if AWS credentials are configured
aws sts get-caller-identity
# Expected output shows your AWS user/role:
# {
# "UserId": "AIDA...",
# "Account": "942039714848",
# "Arn": "arn:aws:iam::942039714848:user/yourname"
# }
Step 2: Sign a Dossier
# Basic signing
ai-dossier sign path/to/your-dossier.ds.md
# With signed_by identity (recommended)
ai-dossier sign path/to/your-dossier.ds.md \
--signed-by "Your Name <your.email@example.com>"
# Specify KMS key (if not using default)
ai-dossier sign path/to/your-dossier.ds.md \
--key-id alias/dossier-official-prod \
--region us-east-1 \
--signed-by "Your Name <your.email@example.com>"
Step 3: Verify the Signature
# Verify locally
ai-dossier verify path/to/your-dossier.ds.md
# Should show:
# âś… PASSED: Checksum and signature valid
What the Signing Command Does
When signing with AWS KMS, ai-dossier sign performs these steps:
- Reads the dossier file and parses frontmatter + body
- Calculates checksum (SHA256 of body only, not frontmatter)
- Signs with AWS KMS:
- Hashes the body content:
SHA256(body) - Calls AWS KMS Sign API with
MessageType: 'DIGEST' - Receives ECDSA signature from KMS
- Hashes the body content:
- Updates frontmatter with:
checksum: The SHA256 hashsignature: Object containing signature, public key, key ID, timestamps
- Writes the file back with updated frontmatter
Important Notes
Checksum Calculation:
- âś… Only the body (content after
---) is hashed - ❌ Frontmatter is NOT included in checksum
- This allows updating metadata without invalidating signatures
Message Type:
- Signing uses
MessageType: 'DIGEST'(signs the hash, not raw content) - Verification MUST also use
DIGESTmode (fixed in v1.0.2)
signed_by Field:
- Always include
--signed-byparameter - Format:
"Name <email@domain.com>" - This field is used for trust verification
GitHub CI/CD Signing
Overview
GitHub Actions can sign dossiers automatically using AWS KMS with OIDC authentication (no long-lived credentials).
Workflow: Manual Signing
Location: .github/workflows/sign.yml
Current Workflow
name: sign
on:
workflow_dispatch: {} # Manual trigger only
permissions:
id-token: write # Required for OIDC
contents: read
env:
AWS_REGION: us-east-1
ROLE_ARN: arn:aws:iam::942039714848:role/github-dossier-oidc
KMS_KEY_ALIAS: alias/dossier-official-prod
jobs:
sign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS creds via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Sign dossiers
run: |
# Sign all unsigned dossiers in examples/
for file in examples/**/*.ds.md; do
if ! grep -q '"signature"' "$file"; then
echo "Signing: $file"
npx @ai-dossier/cli sign "$file" \
--signed-by "Dossier Team <team@dossier.ai>"
fi
done
- name: Commit signed dossiers
run: |
git config user.name "GitHub Actions Bot"
git config user.email "actions@github.com"
git add examples/
git commit -m "chore: Sign dossiers with AWS KMS" || echo "No changes"
git push
How to Use
- Navigate to Actions tab on GitHub
- Select “sign” workflow
- Click “Run workflow”
- Select branch (usually
main) - Click “Run workflow” button
The workflow will:
- Checkout the code
- Authenticate with AWS via OIDC (no stored credentials)
- Sign all unsigned
.ds.mdfiles - Commit and push the signed versions
OIDC Authentication
GitHub Actions uses OpenID Connect (OIDC) to get temporary AWS credentials:
Benefits:
- âś… No long-lived AWS credentials in GitHub Secrets
- âś… Automatic credential rotation
- âś… Scoped permissions (only what the role allows)
- âś… Audit trail in CloudTrail
How it Works:
- GitHub generates an OIDC token for the workflow
- Token includes claims: repository, branch, workflow
- AWS validates the token against configured trust policy
- AWS issues temporary credentials (valid ~1 hour)
- Workflow uses credentials to call KMS
IAM Role Trust Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::942039714848:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:imboard-ai/ai-dossier:*",
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}
]
}
Verification Process
How Verification Works
The ai-dossier verify command runs an integrity stage followed by a risk assessment:
Stage 1: Integrity Check (checksum + signature)
- Parse dossier file (separate frontmatter and body)
- Calculate checksum:
SHA256(body)and compare withchecksum.hashin frontmatter - If a signature is present, verify it (Ed25519 / AWS KMS) and check the signer against your trusted keys (
~/.dossier/trusted-keys.txt)
Risk assessment
- Evaluate
risk_level,risk_factors, anddestructive_operationsfrom frontmatter - Determine whether the dossier is safe to execute or requires approval
A valid signature from a key that isn’t in your trusted list is reported as “valid but untrusted” — it is not auto-trusted. Add the key with
ai-dossier keys addto trust it.
AWS KMS Signature Verification
Critical Implementation Detail: Verification must match signing process.
Signing Process (ai-dossier sign, KMS method)
// 1. Hash the body
const hash = crypto.createHash('sha256').update(body, 'utf8').digest();
// 2. Sign with KMS using DIGEST mode
const signCommand = new SignCommand({
KeyId: keyId,
Message: hash, // Pass the hash
MessageType: 'DIGEST', // Important: DIGEST mode
SigningAlgorithm: 'ECDSA_SHA_256'
});
Verification Process (packages/core/src/signature.ts)
// 1. Hash the body (MUST match signing)
const hash = createHash('sha256').update(content, 'utf8').digest();
// 2. Verify with KMS using DIGEST mode
const command = new VerifyCommand({
KeyId: keyId,
Message: hash, // Pass the hash
MessageType: 'DIGEST', // MUST match signing!
Signature: signatureBuffer,
SigningAlgorithm: SigningAlgorithmSpec.ECDSA_SHA_256,
});
Bug Fixed in v1.0.2 (2025-11-24):
- ❌ Before: Verification used
MessageType: 'RAW'(default) - âś… After: Verification uses
MessageType: 'DIGEST'(matches signing) - Impact: All AWS KMS signatures now verify correctly locally
Testing Verification
# Test on the meta-dossier
ai-dossier verify examples/authoring/create-dossier.ds.md
# Test from GitHub URL
ai-dossier verify https://raw.githubusercontent.com/imboard-ai/ai-dossier/main/examples/authoring/create-dossier.ds.md
# Verbose output
ai-dossier verify examples/authoring/create-dossier.ds.md --verbose
Troubleshooting
Signature Verification Fails Locally
Symptom: ⚠️ Signature verification FAILED
Possible Causes:
-
AWS Credentials Not Configured
# Check credentials aws sts get-caller-identity # If error, configure AWS CLI: aws configure # Or set environment variables: export AWS_ACCESS_KEY_ID=... export AWS_SECRET_ACCESS_KEY=... export AWS_REGION=us-east-1 -
Insufficient KMS Permissions
# Test KMS access aws kms describe-key --key-id alias/dossier-official-prod # Required permissions: # - kms:Verify # - kms:GetPublicKey # - kms:DescribeKey -
Wrong MessageType (fixed in v1.0.2)
- Update to latest version of
@ai-dossier/core - Rebuild:
cd packages/core && npm run build
- Update to latest version of
-
Content Modified After Signing
- Checksum will also fail if body changed
- Re-sign the dossier
Signing Tool Errors
Error: “AWS SDK not found”
# Install AWS SDK
npm install @aws-sdk/client-kms
Error: “File not found”
# Use absolute or correct relative path
ai-dossier sign $(pwd)/examples/my-dossier.ds.md
Error: “Access Denied” from KMS
# Check your IAM permissions
aws kms describe-key --key-id alias/dossier-official-prod
# Need these actions:
# - kms:Sign
# - kms:GetPublicKey
# - kms:DescribeKey
GitHub Actions Signing Fails
Error: “Could not assume role”
- Check OIDC provider is configured in AWS IAM
- Verify role trust policy allows your repository
- Ensure workflow has
id-token: writepermission
Error: “KMS operation denied”
- Check IAM role has KMS permissions
- Verify KMS key policy allows the role
- Check role session duration (default 1 hour)
Dossiers Not Signed
- Check workflow ran successfully (Actions tab)
- Verify glob pattern matches your files:
examples/**/*.ds.md - Check if dossiers already have signatures (skipped)
Best Practices
For Dossier Authors
-
Always include
--signed-byai-dossier sign file.ds.md \ --signed-by "Your Name <email@domain.com>" -
Verify after signing
ai-dossier verify file.ds.md -
Sign before committing
- Never commit unsigned high-risk dossiers
- Use pre-commit hook to check signatures
-
Document your signing key
- Add to repository KEYS.txt
- Include fingerprint and expiry
For Repository Maintainers
-
Use OIDC for CI/CD
- Never store AWS credentials in GitHub Secrets
- Configure OIDC provider in AWS
- Use short-lived credentials
-
Automate signing in CI
- Sign on merge to main
- Or manual workflow_dispatch
- Never sign on every PR (security risk)
-
Audit signing operations
- Monitor CloudTrail for KMS operations
- Alert on unusual patterns
- Regular access review
-
Keep signing tools updated
- Watch for security patches
- Test in staging before production
- Document tool versions used
Security Checklist
- AWS credentials secured (not in code)
- KMS key policy restricts access appropriately
- OIDC configured for GitHub Actions
- CloudTrail logging enabled for KMS
- Signing tool version documented
- Emergency key rotation procedure tested
- Backup of public keys maintained
- Trust policy reviewed quarterly
Examples
Sign Multiple Dossiers
# Sign all dossiers in a directory
for file in examples/devops/*.ds.md; do
echo "Signing: $file"
ai-dossier sign "$file" \
--signed-by "DevOps Team <devops@example.com>"
done
Re-sign After Updates
# After editing a dossier, re-sign it
vim examples/my-dossier.ds.md
ai-dossier sign examples/my-dossier.ds.md \
--signed-by "Your Name <email@domain.com>"
Verify Before Push
# Pre-push hook script
#!/bin/bash
for file in $(git diff --cached --name-only | grep '\.ds\.md$'); do
if ! ai-dossier verify "$file"; then
echo "❌ Verification failed: $file"
exit 1
fi
done
echo "âś… All dossiers verified"
Related Documentation
- Key Management - Comprehensive key lifecycle
- Security Architecture - Overall security design
- AWS KMS Choice Decision - Why AWS KMS
- Dual Signature System - KMS + Minisign
Changelog
2025-11-24
- Initial guide created
- Documented local and CI/CD signing processes
- Added troubleshooting section
- Documented MessageType: DIGEST bug fix
Questions or Issues?
- GitHub Discussions: https://github.com/imboard-ai/ai-dossier/discussions
- Security Issues: security@imboard.ai
Rendered from docs/guides/signing-dossiers.md in the repository. Edit it there.