A Go implementation of the SchemaPin cryptographic protocol for ensuring the integrity and authenticity of tool schemas used by AI agents.
SchemaPin prevents "MCP Rug Pull" attacks through digital signatures and Trust-On-First-Use (TOFU) key pinning. This Go implementation provides 100% protocol compatibility with the Python and JavaScript versions while following idiomatic Go patterns.
- Cryptographic Security: ECDSA P-256 digital signatures for schema integrity
- Trust-On-First-Use: Automatic key pinning with interactive user prompts
- Key Discovery: Automatic public key discovery via
.well-known/schemapin.json - Key Revocation: Support for revoked key lists and security warnings
- CLI Tools: Professional command-line tools for key generation, signing, and verification
- Zero Dependencies: Pure Go implementation with single binary deployment
- Cross-Language Compatible: Full interoperability with Python and JavaScript implementations
- Interactive Pinning: User prompts for key decisions with domain policies
- Performance Optimized: Fast ECDSA operations with efficient key storage
git clone https://github.com/ThirdKeyAi/schemapin.git
cd schemapin/go
make installgo install github.com/ThirdKeyAi/schemapin/go/cmd/...@latestDownload pre-built binaries from the releases page.
# Generate ECDSA P-256 key pair
schemapin-keygen --type ecdsa --developer "Your Company" --contact "[email protected]" --well-known
# Output:
# ✓ Generated private key: private_key.pem
# ✓ Generated public key: public_key.pem
# ✓ Generated .well-known response: well_known.json# Sign a JSON schema file
schemapin-sign --key private_key.pem --schema schema.json --output signed_schema.json
# Or sign inline
echo '{"name": "test", "type": "object"}' | schemapin-sign --key private_key.pem# Verify with automatic key discovery
schemapin-verify --schema signed_schema.json --domain example.com --tool-id my-tool
# Verify with explicit public key
schemapin-verify --schema signed_schema.json --public-key public_key.pem
# Output:
# ✅ Schema signature is VALID
# 🔑 Key pinned for future use
# 📋 Developer: Your CompanyGenerate ECDSA key pairs and .well-known responses.
schemapin-keygen [OPTIONS]
Options:
--type string Key type (ecdsa) (default "ecdsa")
--developer string Developer name for .well-known response
--contact string Contact email for .well-known response
--output-dir string Output directory (default ".")
--private-key string Private key filename (default "private_key.pem")
--public-key string Public key filename (default "public_key.pem")
--well-known Generate .well-known/schemapin.json response
--revoked-keys string Comma-separated list of revoked key files
--schema-version string Schema version (default "1.1")Sign JSON schemas with private keys.
schemapin-sign [OPTIONS]
Options:
--key string Private key file (required)
--schema string Schema file to sign (or use stdin)
--output string Output file (default stdout)
--format string Output format: json, compact (default "json")Verify signed schemas with automatic key discovery.
schemapin-verify [OPTIONS]
Options:
--schema string Signed schema file (required)
--domain string Domain for key discovery
--tool-id string Tool identifier for key pinning
--public-key string Explicit public key file (skips discovery)
--db-path string Key pinning database path (default "~/.schemapin/keys.db")
--auto-pin Automatically pin keys on first use
--interactive Enable interactive key pinning prompts
--timeout duration Discovery timeout (default 10s)ECDSA key management and signature operations.
// Generate key pair
keyManager := crypto.NewKeyManager()
privateKey, err := keyManager.GenerateKeypair()
// Export to PEM format
privateKeyPEM, err := keyManager.ExportPrivateKeyPEM(privateKey)
publicKeyPEM, err := keyManager.ExportPublicKeyPEM(&privateKey.PublicKey)
// Sign and verify
signatureManager := crypto.NewSignatureManager()
signature, err := signatureManager.SignHash(hash, privateKey)
valid := signatureManager.VerifySignature(hash, signature, &privateKey.PublicKey)Schema canonicalization and hashing.
// Canonicalize and hash schema
core := core.NewSchemaPinCore()
canonical, err := core.CanonicalizeSchema(schema)
hash := core.HashCanonical(canonical)
// Combined operation
hash, err := core.CanonicalizeAndHash(schema)High-level workflows for signing and verification.
// Signing workflow
signingWorkflow, err := utils.NewSchemaSigningWorkflow(privateKeyPEM)
signature, err := signingWorkflow.SignSchema(schema)
// Verification workflow
verificationWorkflow, err := utils.NewSchemaVerificationWorkflow(dbPath)
result, err := verificationWorkflow.VerifySchema(ctx, schema, signature, toolID, domain, autoPin)Key pinning with BoltDB storage.
// Initialize key pinning
keyPinning, err := pinning.NewKeyPinning(dbPath, pinning.PinningModeInteractive, handler)
// Pin and verify keys
err = keyPinning.PinKey(toolID, publicKeyPEM, domain, developerName)
isPinned := keyPinning.IsKeyPinned(toolID)
pinnedKeys, err := keyPinning.ListPinnedKeys()Interactive user prompts for key decisions.
// Console handler
handler := interactive.NewConsoleInteractiveHandler()
// Callback handler
handler := interactive.NewCallbackInteractiveHandler(
promptCallback,
displayCallback,
warningCallback,
)
// Prompt for key decisions
decision, err := handler.PromptUser(context)Automatic public key discovery via .well-known endpoints.
// Initialize discovery
discovery := discovery.NewPublicKeyDiscovery()
// Discover keys
publicKeyPEM, err := discovery.GetPublicKeyPEM(ctx, domain)
developerInfo, err := discovery.GetDeveloperInfo(ctx, domain)
isValid, err := discovery.ValidateKeyNotRevoked(ctx, publicKeyPEM, domain)See examples/developer/main.go for a complete tool developer example:
cd examples/developer
go run main.goThis demonstrates:
- Key pair generation
- Schema creation and signing
- .well-known response generation
- File output for distribution
See examples/client/main.go for client verification:
cd examples/client
go run main.goThis demonstrates:
- Loading signed schemas
- Key discovery simulation
- TOFU key pinning
- Signature verification
- Invalid signature detection
See examples/interactive-demo/main.go:
cd examples/interactive-demo
go run main.goThis demonstrates:
- Console interactive prompts
- Callback-based handlers
- Domain policies
- Key change scenarios
See examples/cross-language-demo/main.go:
cd examples/cross-language-demo
go run main.goThis demonstrates:
- Verifying Python-generated signatures
- Verifying JavaScript-generated signatures
- Generating signatures for other languages
go/
├── cmd/ # CLI applications
│ ├── schemapin-keygen/ # Key generation tool
│ ├── schemapin-sign/ # Schema signing tool
│ └── schemapin-verify/ # Schema verification tool
├── pkg/ # Public API packages
│ ├── core/ # Schema canonicalization
│ ├── crypto/ # ECDSA operations
│ ├── discovery/ # .well-known discovery
│ ├── pinning/ # Key pinning with BoltDB
│ ├── interactive/ # User interaction
│ └── utils/ # High-level workflows
├── internal/ # Private packages
│ └── version/ # Version information
├── examples/ # Usage examples
│ ├── developer/ # Tool developer workflow
│ ├── client/ # Client verification
│ ├── interactive-demo/ # Interactive pinning
│ └── cross-language-demo/ # Cross-language compatibility
├── tests/ # Integration tests
└── docs/ # Additional documentation
- Go 1.19 or later
- Make (optional, for convenience)
# Build all CLI tools
make build
# Build specific tool
go build -o bin/schemapin-keygen ./cmd/schemapin-keygen# Run all tests
make test
# Run with coverage
go test -v -race -coverprofile=coverage.out ./...
# Run integration tests
go test -v ./tests/
# Run specific package tests
go test -v ./pkg/crypto/# Install golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Run linter
make lint
# or
golangci-lint run# Run benchmarks
go test -bench=. ./pkg/crypto/
go test -bench=. ./pkg/core/
# Example output:
# BenchmarkSignature-8 5000 234567 ns/op
# BenchmarkVerification-8 10000 123456 ns/opPerformance characteristics on modern hardware:
| Operation | Time | Throughput |
|---|---|---|
| Key Generation | ~2ms | 500 keys/sec |
| Schema Signing | ~1ms | 1000 sigs/sec |
| Signature Verification | ~0.5ms | 2000 verifs/sec |
| Schema Canonicalization | ~0.1ms | 10000 schemas/sec |
- Private keys are stored in PEM format with 0600 permissions
- Key pinning database uses BoltDB with file-level locking
- No keys are stored in memory longer than necessary
- Algorithm: ECDSA with P-256 curve (secp256r1)
- Hash Function: SHA-256
- Signature Format: ASN.1 DER encoding for cross-language compatibility
- Key Format: PKCS#8 for private keys, PKIX for public keys
- TOFU (Trust On First Use): Keys are pinned on first encounter
- Domain Policies: Configure trust levels per domain
- Key Revocation: Support for revoked key lists in .well-known responses
- Interactive Prompts: User confirmation for key changes
# Check if .well-known endpoint is accessible
curl https://yourdomain.com/.well-known/schemapin.json
# Verify domain in tool ID matches discovery domain
schemapin-verify --schema schema.json --domain yourdomain.com --tool-id yourdomain.com/tool# Check schema format and canonicalization
schemapin-verify --schema schema.json --public-key public_key.pem --verbose
# Verify key format
openssl ec -in private_key.pem -text -noout# Check database directory permissions
ls -la ~/.schemapin/
# Reset database if corrupted
rm ~/.schemapin/keys.dbEnable debug logging:
export SCHEMAPIN_DEBUG=1
schemapin-verify --schema schema.json --domain example.comIf signatures don't verify across languages:
-
Check schema canonicalization:
# Compare canonical forms echo '{"b":2,"a":1}' | schemapin-sign --key key.pem --format compact
-
Verify key formats:
# Keys should be identical across implementations diff go_public_key.pem python_public_key.pem -
Test with known good signatures:
# Use cross-language demo cd examples/cross-language-demo go run main.go
- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Make changes and add tests
- Run tests:
make test - Run linter:
make lint - Commit changes:
git commit -m "Add feature" - Push to branch:
git push origin feature-name - Create a Pull Request
- Follow standard Go conventions
- Use
gofmtfor formatting - Add godoc comments for public APIs
- Include tests for new functionality
- Update documentation for user-facing changes
MIT License - see LICENSE file for details.
- Python Implementation - Reference implementation
- JavaScript Implementation - Browser and Node.js support
- Integration Demo - Cross-language testing
- Production Server - .well-known endpoint server
- GitHub Issues - Bug reports and feature requests
- Discussions - Questions and community support
- Security - Security vulnerability reports