This document provides a comprehensive specification for implementing SchemaPin in Go, ensuring feature parity with the existing Python and JavaScript implementations while following idiomatic Go patterns. SchemaPin is a cryptographic protocol for ensuring the integrity and authenticity of tool schemas used by AI agents, preventing "MCP Rug Pull" attacks through digital signatures and Trust-On-First-Use (TOFU) key pinning.
- Protocol Compatibility: 100% interoperability with Python and JavaScript implementations
- Idiomatic Go: Natural Go patterns, error handling, and project structure
- Zero Dependencies: Pure Go implementation with single binary deployment
- Professional CLI: Rich command-line interface using industry-standard tools
- Comprehensive Testing: Full test coverage with cross-language compatibility validation
graph TB
subgraph "Go Project Structure"
A[go/] --> B[cmd/]
A --> C[pkg/]
A --> D[internal/]
A --> E[examples/]
A --> F[tests/]
end
subgraph "CLI Applications (cmd/)"
B --> G[schemapin-keygen/]
B --> H[schemapin-sign/]
B --> I[schemapin-verify/]
end
subgraph "Public API (pkg/)"
C --> J[core/]
C --> K[crypto/]
C --> L[discovery/]
C --> M[pinning/]
C --> N[interactive/]
C --> O[utils/]
end
subgraph "Core Dependencies"
P[BoltDB] --> M
Q[Cobra] --> B
R[crypto/ecdsa] --> K
S[encoding/json] --> J
T[net/http] --> L
end
- Standard Go Layout:
pkg/for public APIs,cmd/for CLI tools - BoltDB Storage: Pure Go key-value store for key pinning (no CGo)
- Cobra CLI Framework: Professional command-line interface
- Protocol-First Compatibility: Strict adherence to cryptographic specifications
- Idiomatic Error Handling: Go-style error patterns and wrapped errors
go/
├── go.mod # Go module definition
├── go.sum # Dependency checksums
├── README.md # Go-specific documentation
├── LICENSE # MIT License
├── Makefile # Build automation
├── .gitignore # Git ignore patterns
├── .golangci.yml # Linting configuration
│
├── cmd/ # CLI applications
│ ├── schemapin-keygen/
│ │ └── main.go # Key generation tool
│ ├── schemapin-sign/
│ │ └── main.go # Schema signing tool
│ └── schemapin-verify/
│ └── main.go # Schema verification tool
│
├── pkg/ # Public API packages
│ ├── core/
│ │ ├── core.go # Schema canonicalization
│ │ └── core_test.go # Core tests
│ ├── crypto/
│ │ ├── crypto.go # ECDSA operations
│ │ └── crypto_test.go # Crypto tests
│ ├── discovery/
│ │ ├── discovery.go # .well-known discovery
│ │ └── discovery_test.go # Discovery tests
│ ├── pinning/
│ │ ├── pinning.go # Key pinning with BoltDB
│ │ └── pinning_test.go # Pinning tests
│ ├── interactive/
│ │ ├── interactive.go # User interaction
│ │ └── interactive_test.go # Interactive tests
│ └── utils/
│ ├── utils.go # High-level workflows
│ └── utils_test.go # Utils tests
│
├── internal/ # Private packages
│ ├── testutil/
│ │ └── testutil.go # Test utilities
│ └── version/
│ └── version.go # Version information
│
├── examples/ # Usage examples
│ ├── developer/
│ │ └── main.go # Tool developer example
│ ├── client/
│ │ └── main.go # Client verification example
│ └── interactive/
│ └── main.go # Interactive pinning demo
│
└── tests/ # Integration tests
├── integration/
│ ├── cross_compatibility_test.go # Cross-language tests
│ └── cli_integration_test.go # CLI integration tests
└── fixtures/
├── test_keys/ # Test key pairs
├── test_schemas/ # Test schemas
└── test_well_known/ # Test .well-known files
package core
// SchemaPinCore provides schema canonicalization and hashing
type SchemaPinCore struct{}
func (s *SchemaPinCore) CanonicalizeSchema(schema map[string]interface{}) (string, error)
func (s *SchemaPinCore) HashCanonical(canonical string) []byte
func (s *SchemaPinCore) CanonicalizeAndHash(schema map[string]interface{}) ([]byte, error)package crypto
// KeyManager handles ECDSA key operations
type KeyManager struct{}
func (k *KeyManager) GenerateKeypair() (*ecdsa.PrivateKey, error)
func (k *KeyManager) ExportPrivateKeyPEM(key *ecdsa.PrivateKey) (string, error)
func (k *KeyManager) ExportPublicKeyPEM(key *ecdsa.PublicKey) (string, error)
func (k *KeyManager) LoadPrivateKeyPEM(pemData string) (*ecdsa.PrivateKey, error)
func (k *KeyManager) LoadPublicKeyPEM(pemData string) (*ecdsa.PublicKey, error)
func (k *KeyManager) CalculateKeyFingerprint(key *ecdsa.PublicKey) (string, error)
// SignatureManager handles signature operations
type SignatureManager struct{}
func (s *SignatureManager) SignHash(hashBytes []byte, privateKey *ecdsa.PrivateKey) (string, error)
func (s *SignatureManager) VerifySignature(hashBytes []byte, signatureB64 string, publicKey *ecdsa.PublicKey) bool
func (s *SignatureManager) SignSchemaHash(schemaHash []byte, privateKey *ecdsa.PrivateKey) (string, error)
func (s *SignatureManager) VerifySchemaSignature(schemaHash []byte, signatureB64 string, publicKey *ecdsa.PublicKey) boolpackage discovery
// WellKnownResponse represents .well-known/schemapin.json structure
type WellKnownResponse struct {
SchemaVersion string `json:"schema_version"`
DeveloperName string `json:"developer_name"`
PublicKeyPEM string `json:"public_key_pem"`
Contact string `json:"contact,omitempty"`
RevokedKeys []string `json:"revoked_keys,omitempty"`
}
// PublicKeyDiscovery handles .well-known endpoint discovery
type PublicKeyDiscovery struct{}
func NewPublicKeyDiscovery() *PublicKeyDiscovery
func (p *PublicKeyDiscovery) ConstructWellKnownURL(domain string) string
func (p *PublicKeyDiscovery) FetchWellKnown(ctx context.Context, domain string) (*WellKnownResponse, error)
func (p *PublicKeyDiscovery) GetPublicKeyPEM(ctx context.Context, domain string) (string, error)
func (p *PublicKeyDiscovery) ValidateKeyNotRevoked(ctx context.Context, publicKeyPEM, domain string) (bool, error)
func (p *PublicKeyDiscovery) GetDeveloperInfo(ctx context.Context, domain string) (map[string]string, error)package pinning
// PinningMode and PinningPolicy enums
type PinningMode string
type PinningPolicy string
// PinnedKeyInfo represents stored key information
type PinnedKeyInfo struct {
ToolID string `json:"tool_id"`
PublicKeyPEM string `json:"public_key_pem"`
Domain string `json:"domain"`
DeveloperName string `json:"developer_name,omitempty"`
PinnedAt time.Time `json:"pinned_at"`
LastVerified time.Time `json:"last_verified,omitempty"`
}
// KeyPinning manages TOFU key storage with BoltDB
type KeyPinning struct{}
func NewKeyPinning(dbPath string, mode PinningMode, handler interactive.InteractiveHandler) (*KeyPinning, error)
func (k *KeyPinning) Close() error
func (k *KeyPinning) PinKey(toolID, publicKeyPEM, domain, developerName string) error
func (k *KeyPinning) GetPinnedKey(toolID string) (string, error)
func (k *KeyPinning) IsKeyPinned(toolID string) bool
func (k *KeyPinning) UpdateLastVerified(toolID string) error
func (k *KeyPinning) SetDomainPolicy(domain string, policy PinningPolicy) error
func (k *KeyPinning) GetDomainPolicy(domain string) PinningPolicy
func (k *KeyPinning) InteractivePinKey(ctx context.Context, toolID, publicKeyPEM, domain, developerName string) (bool, error)package interactive
// PromptType and UserDecision enums
type PromptType string
type UserDecision string
// KeyInfo represents key information for display
type KeyInfo struct {
Fingerprint string
PEMData string
Domain string
DeveloperName string
PinnedAt time.Time
LastVerified time.Time
IsRevoked bool
}
// PromptContext provides context for interactive prompts
type PromptContext struct {
PromptType PromptType
ToolID string
Domain string
CurrentKey *KeyInfo
NewKey *KeyInfo
DeveloperInfo map[string]string
SecurityWarning string
}
// InteractiveHandler interface for user interaction
type InteractiveHandler interface {
PromptUser(context *PromptContext) (UserDecision, error)
DisplayKeyInfo(keyInfo *KeyInfo) string
DisplaySecurityWarning(warning string)
}
// ConsoleInteractiveHandler implements console-based interaction
type ConsoleInteractiveHandler struct{}
// CallbackInteractiveHandler implements callback-based interaction
type CallbackInteractiveHandler struct{}
// InteractivePinningManager manages interactive key pinning
type InteractivePinningManager struct{}
func NewInteractivePinningManager(handler InteractiveHandler) *InteractivePinningManager
func (i *InteractivePinningManager) PromptFirstTimeKey(toolID, domain, publicKeyPEM string, developerInfo map[string]string) (UserDecision, error)
func (i *InteractivePinningManager) PromptKeyChange(toolID, domain, currentKeyPEM, newKeyPEM string, currentKeyInfo map[string]interface{}, developerInfo map[string]string) (UserDecision, error)
func (i *InteractivePinningManager) PromptRevokedKey(toolID, domain, revokedKeyPEM string, keyInfo map[string]string) (UserDecision, error)package utils
// SchemaSigningWorkflow provides high-level signing operations
type SchemaSigningWorkflow struct{}
func NewSchemaSigningWorkflow(privateKeyPEM string) (*SchemaSigningWorkflow, error)
func (s *SchemaSigningWorkflow) SignSchema(schema map[string]interface{}) (string, error)
// SchemaVerificationWorkflow provides high-level verification operations
type SchemaVerificationWorkflow struct{}
func NewSchemaVerificationWorkflow(pinningDBPath string) (*SchemaVerificationWorkflow, error)
func (s *SchemaVerificationWorkflow) VerifySchema(ctx context.Context, schema map[string]interface{}, signatureB64, toolID, domain string, autoPin bool) (map[string]interface{}, error)
func (s *SchemaVerificationWorkflow) PinKeyForTool(ctx context.Context, toolID, domain, developerName string) error
// CreateWellKnownResponse creates .well-known response structure
func CreateWellKnownResponse(publicKeyPEM, developerName, contact string, revokedKeys []string, schemaVersion string) map[string]interface{}# Generate ECDSA key pair with .well-known template
schemapin-keygen --type ecdsa --output-dir ./keys --developer "Alice Corp" --well-known
# Generate RSA key pair with custom size
schemapin-keygen --type rsa --key-size 4096 --output-dir ./keys
# Generate with contact information
schemapin-keygen --type ecdsa --developer "Bob Inc" --contact "security@bob.com" --well-knownFlags:
--type: Key type (ecdsa, rsa) [default: ecdsa]--key-size: RSA key size (2048, 3072, 4096) [default: 2048]--output-dir: Output directory [default: current directory]--prefix: Filename prefix [default: schemapin]--developer: Developer name for .well-known template--contact: Contact information--schema-version: Schema version [default: 1.1]--well-known: Generate .well-known template--verbose, -v: Verbose output--quiet, -q: Quiet output--json: JSON output format
# Sign single schema
schemapin-sign --key private.pem --schema schema.json --output signed_schema.json
# Sign with metadata
schemapin-sign --key private.pem --schema schema.json --developer "Alice Corp" --version "1.0"
# Batch signing
schemapin-sign --key private.pem --batch schemas/ --output-dir signed/
# Sign from stdin
echo '{"type": "object"}' | schemapin-sign --key private.pem --stdinFlags:
--key: Private key file (PEM format) [required]--schema: Input schema file--batch: Directory for batch processing--stdin: Read from stdin--output: Output file--output-dir: Output directory for batch--developer: Developer name metadata--version: Schema version metadata--description: Schema description--metadata: JSON metadata file--pattern: File pattern for batch [default: *.json]--suffix: Output suffix [default: _signed]--no-validate: Skip schema validation--verbose, -v: Verbose output--quiet, -q: Quiet output--json: JSON output format
# Verify with public key
schemapin-verify --schema signed_schema.json --public-key public.pem
# Verify with discovery
schemapin-verify --schema signed_schema.json --domain example.com --tool-id my-tool
# Interactive verification
schemapin-verify --schema signed_schema.json --domain example.com --tool-id my-tool --interactive
# Batch verification
schemapin-verify --batch schemas/ --domain example.com --auto-pinFlags:
--schema: Signed schema file--batch: Directory for batch processing--stdin: Read from stdin--public-key: Public key file (PEM format)--domain: Domain for discovery--tool-id: Tool identifier for pinning--pinning-db: Pinning database path--interactive: Interactive pinning mode--auto-pin: Auto-pin keys on first use--pattern: File pattern for batch [default: *.json]--verbose, -v: Verbose output--quiet, -q: Quiet output--json: JSON output format--exit-code: Exit with error code on failure
- Error Handling: Use wrapped errors with
fmt.Errorfand%wverb - Context Propagation: Pass
context.Contextfor cancellation and timeouts - Interface Design: Small, focused interfaces following Go conventions
- Package Organization: Clear separation of concerns with minimal dependencies
- Testing: Table-driven tests with comprehensive coverage
- Documentation: Godoc comments for all exported functions and types
// go.mod
module github.com/thirdkey/schemapin
go 1.21
require (
github.com/spf13/cobra v1.8.0
go.etcd.io/bbolt v1.3.8
)
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.4.0 // indirect
)# Makefile
.PHONY: build test lint clean install
BINARY_NAME=schemapin
VERSION=$(shell git describe --tags --always --dirty)
LDFLAGS=-ldflags "-X github.com/thirdkey/schemapin/internal/version.Version=$(VERSION)"
build:
go build $(LDFLAGS) -o bin/schemapin-keygen ./cmd/schemapin-keygen
go build $(LDFLAGS) -o bin/schemapin-sign ./cmd/schemapin-sign
go build $(LDFLAGS) -o bin/schemapin-verify ./cmd/schemapin-verify
test:
go test -v -race -coverprofile=coverage.out ./...
lint:
golangci-lint run
clean:
rm -rf bin/ coverage.out
install:
go install $(LDFLAGS) ./cmd/...- Coverage Target: >90% for all packages
- Test Structure: Table-driven tests with subtests
- Mocking: Interface-based mocking for external dependencies
- Test Data: Fixtures in
tests/fixtures/directory
- Cross-Language Compatibility: Verify interoperability with Python/JavaScript
- CLI Integration: End-to-end testing of command-line tools
- Database Integration: BoltDB operations and migrations
- Network Integration: HTTP client behavior and timeouts
- Cryptographic Validation: ECDSA signature compatibility
- Key Format Validation: PEM encoding/decoding correctness
- Protocol Compliance: .well-known endpoint format validation
- Error Handling: Security-sensitive error scenarios
- Benchmarks: Core operations (canonicalization, signing, verification)
- Memory Usage: Profiling for memory leaks and efficiency
- Concurrency: Thread safety and concurrent access patterns
- JSON Canonicalization: Identical key sorting and serialization
- ECDSA Signatures: Compatible ASN.1 DER encoding
- Key Fingerprints: SHA-256 of DER-encoded public keys
- Well-Known Format: Exact JSON structure matching
- Database Schema: Interoperable key storage format
// Cross-compatibility test example
func TestCrossLanguageCompatibility(t *testing.T) {
tests := []struct {
name string
pythonSigned string
jsSigned string
expectedValid bool
}{
{
name: "python_signed_schema",
pythonSigned: "fixtures/python_signed.json",
expectedValid: true,
},
{
name: "js_signed_schema",
jsSigned: "fixtures/js_signed.json",
expectedValid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test Go can verify Python/JS signatures
// Test Python/JS can verify Go signatures
})
}
}- Project structure setup with Go modules
- Core module: JSON canonicalization and hashing
- Crypto module: ECDSA key management and signatures
- Basic unit tests with >80% coverage
- Discovery module: .well-known endpoint handling
- Pinning module: BoltDB-based key storage
- Integration tests for HTTP and database operations
- Interactive module: User prompt system
- Console and callback handlers
- Interactive pinning workflows
- schemapin-keygen: Key generation tool
- schemapin-sign: Schema signing tool
- schemapin-verify: Schema verification tool
- CLI integration tests
- Cross-compatibility test suite
- Python/JavaScript interoperability validation
- Performance benchmarking
- Documentation and examples
- Security audit and testing
- Error handling improvements
- Documentation completion
- Release preparation
- Functional Parity: All features from Python/JavaScript implementations
- Protocol Compatibility: 100% interoperability across languages
- Test Coverage: >90% unit test coverage, comprehensive integration tests
- Performance: Comparable or better performance than existing implementations
- Usability: Intuitive CLI tools with comprehensive help and examples
- Documentation: Complete API documentation and usage guides
- Security: Cryptographic operations validated and secure by default
This specification provides a comprehensive roadmap for implementing SchemaPin in Go while maintaining strict compatibility with existing implementations and following Go best practices.