Sonr Common Package

Go Version License

The common package provides high-level, simplified interfaces for IPFS and WebAuthn functionality, designed for easy integration into external libraries and applications within the Sonr network.

Features

  • IPFS Integration: Simple, high-level API for storing and retrieving data from IPFS
  • WebAuthn Support: Comprehensive WebAuthn credential management and verification
  • Helper Functions: Convenient wrapper functions that abstract complexity
  • Well-Tested: Comprehensive test coverage with unit and integration tests
  • Production-Ready: Battle-tested implementations with proper error handling

Table of Contents

Installation

go get github.com/sonr-io/common

Quick Start

IPFS Example

package main

import (
    "fmt"
    "github.com/sonr-io/common"
)

func main() {
    // Check if IPFS daemon is running
    if !common.IsIPFSDaemonRunning() {
        panic("IPFS daemon is not running")
    }

    // Store data
    data := []byte("Hello, IPFS!")
    cid, err := common.StoreData(data)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Stored data with CID: %s\n", cid)

    // Retrieve data
    retrieved, err := common.RetrieveData(cid)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Retrieved: %s\n", string(retrieved))
}

WebAuthn Example

package main

import (
    "fmt"
    "github.com/sonr-io/common"
)

func main() {
    // Generate a challenge for WebAuthn ceremony
    challenge, err := common.NewChallenge()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Challenge: %s\n", challenge)

    // Verify origin
    origin := "https://example.com"
    allowedOrigins := []string{"https://example.com", "https://app.example.com"}
    if err := common.VerifyOrigin(origin, allowedOrigins); err != nil {
        panic(err)
    }
    fmt.Println("Origin verified successfully!")
}

IPFS Module

The IPFS module (ipfs/) provides a high-level interface for interacting with IPFS nodes.

Core Functions

Client Management

// Create a new IPFS client
client, err := common.NewIPFSClient()

// Or use the panic version for critical scenarios
client := common.MustGetIPFSClient()

// Check if daemon is running
if common.IsIPFSDaemonRunning() {
    // Daemon is available
}

Data Storage

// Store raw bytes
cid, err := common.StoreData([]byte("data"))

// Store a file with metadata
cid, err := common.StoreFile("document.txt", fileData)

// Store multiple files as a folder
files := map[string][]byte{
    "file1.txt": []byte("content 1"),
    "file2.txt": []byte("content 2"),
}
cid, err := common.StoreFolder(files)

Data Retrieval

// Retrieve data by CID
data, err := common.RetrieveData("QmXxx...")

// Using client directly for advanced operations
client, _ := common.NewIPFSClient()
exists, err := client.Exists("QmXxx...")
entries, err := client.Ls("QmXxx...")

Advanced IPFS Usage

import "github.com/sonr-io/common/ipfs"

client, _ := ipfs.GetClient()

// Pin content
err := client.Pin("QmXxx...", "my-important-data")

// Unpin content
err := client.Unpin("QmXxx...")

// Check if pinned
pinned, err := client.IsPinned("ipns-name")

// Get node status
status, err := client.NodeStatus()
fmt.Printf("Peer ID: %s\n", status.PeerID)
fmt.Printf("Connected Peers: %d\n", status.ConnectedPeers)

WebAuthn Module

The WebAuthn module (webauthn/) provides comprehensive support for WebAuthn authentication.

Challenge Generation

// Generate a cryptographic challenge (32 bytes, base64url encoded)
challenge, err := common.NewChallenge()

// Get the standard challenge length
length := common.ChallengeLength() // Returns 32

Origin Verification

// Verify origin against allowed list
allowedOrigins := []string{
    "https://example.com",
    "https://app.example.com:8080",
}

err := common.VerifyOrigin("https://example.com", allowedOrigins)

Base64 URL Encoding

// Encode data to URL-safe base64 (no padding)
encoded := common.EncodeBase64URL([]byte("data"))

// Decode URL-safe base64
decoded, err := common.DecodeBase64URL(encoded)

Credential Management

Credential Creation (Registration)

// Unmarshal credential creation response from client
credData := []byte(`{"id":"...","type":"public-key",...}`)
credResponse, err := common.UnmarshalCredentialCreation(credData)

// Parse and validate credential creation
parsedCred, err := common.ParseCredentialCreation(credData)

// Marshal credential for storage
jsonData, err := common.MarshalCredentialCreation(credResponse)

Credential Assertion (Authentication)

// Unmarshal assertion response from client
assertionData := []byte(`{"id":"...","type":"public-key",...}`)
assertionResponse, err := common.UnmarshalCredentialAssertion(assertionData)

// Parse and validate assertion
parsedAssertion, err := common.ParseCredentialAssertion(assertionData)

// Marshal assertion for storage
jsonData, err := common.MarshalCredentialAssertion(assertionResponse)

Advanced WebAuthn Usage

import "github.com/sonr-io/common/webauthn"

// Create registration options
options := &webauthn.PublicKeyCredentialCreationOptions{
    RelyingParty: webauthn.RelyingPartyEntity{
        Name: "Example Corp",
        ID:   "example.com",
    },
    User: webauthn.UserEntity{
        ID:          []byte("user-id"),
        Name:        "user@example.com",
        DisplayName: "User Name",
    },
    Challenge: challenge,
    Parameters: []webauthn.CredentialParameter{
        {Type: webauthn.PublicKeyCredentialType, Algorithm: -7}, // ES256
    },
}

// Verify credential creation
clientDataHash, err := parsedCred.Verify(
    storedChallenge,
    verifyUser,
    verifyUserPresence,
    relyingPartyID,
    rpOrigins,
    rpTopOrigins,
    rpTopOriginsVerify,
    metadataProvider,
    credParams,
)

// Verify credential assertion
err = parsedAssertion.Verify(
    storedChallenge,
    relyingPartyID,
    rpOrigins,
    rpTopOrigins,
    rpTopOriginsVerify,
    appID,
    verifyUser,
    verifyUserPresence,
    credentialPublicKey,
)

Helper Functions

IPFS Helpers

Function Description
NewIPFSClient() Create IPFS client with detailed error messages
MustGetIPFSClient() Panic version for critical initialization
StoreData(data) Store raw bytes and return CID
RetrieveData(cid) Retrieve content by CID
IsIPFSDaemonRunning() Check if IPFS daemon is accessible
StoreFile(name, data) Store file with metadata
StoreFolder(files) Store multiple files as a folder

WebAuthn Helpers

Function Description
NewChallenge() Generate 32-byte cryptographic challenge
ChallengeLength() Get standard challenge length (32)
VerifyOrigin(origin, allowed) Verify origin against allowed list
EncodeBase64URL(data) Encode to URL-safe base64
DecodeBase64URL(encoded) Decode URL-safe base64
UnmarshalCredentialCreation(data) Parse credential creation response
MarshalCredentialCreation(ccr) Serialize credential creation
ParseCredentialCreation(data) Parse and validate creation response
UnmarshalCredentialAssertion(data) Parse assertion response
MarshalCredentialAssertion(car) Serialize assertion response
ParseCredentialAssertion(data) Parse and validate assertion

Testing

The package includes a comprehensive Makefile for testing and development tasks.

Available Make Targets

# Run all tests
make test

# Run tests for specific modules
make test-ipfs
make test-webauthn

# Generate coverage reports
make coverage              # Show coverage summary
make coverage-ipfs         # IPFS module coverage
make coverage-webauthn     # WebAuthn module coverage
make coverage-all          # HTML coverage report

# Code quality
make fmt                   # Format code
make vet                   # Run go vet
make lint                  # Run golangci-lint

# Performance
make bench                 # Run benchmarks
make test-race             # Run tests with race detector

# Maintenance
make clean                 # Clean test cache and coverage files
make deps                  # Install dependencies

# Quick check
make check                 # Run fmt, vet, and test

Running Tests Manually

# Run all tests
go test ./...

# Run tests with coverage
go test -cover ./...

# Run specific tests
go test -v -run TestNewChallenge

# Run benchmarks
go test -bench=. -benchmem ./...

Test Coverage

Current test coverage:

  • Common package: 49.4%
  • WebAuthn module: Comprehensive (50+ tests)
  • IPFS module: Core functionality tested

Examples

Complete IPFS Example

package main

import (
    "fmt"
    "log"
    "github.com/sonr-io/common"
    "github.com/sonr-io/common/ipfs"
)

func main() {
    // Check daemon availability
    if !common.IsIPFSDaemonRunning() {
        log.Fatal("IPFS daemon is not running. Start it with: ipfs daemon")
    }

    // Get client for advanced operations
    client, err := common.NewIPFSClient()
    if err != nil {
        log.Fatal(err)
    }

    // Store a file
    fileData := []byte("This is my important document")
    cid, err := common.StoreFile("document.txt", fileData)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Stored file with CID: %s\n", cid)

    // Pin the content
    if err := client.Pin(cid, "important-document"); err != nil {
        log.Fatal(err)
    }
    fmt.Println("Content pinned successfully")

    // Check if content exists
    exists, err := client.Exists(cid)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Content exists: %v\n", exists)

    // Get node status
    status, err := client.NodeStatus()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Node Status:\n")
    fmt.Printf("  Peer ID: %s\n", status.PeerID)
    fmt.Printf("  Connected Peers: %d\n", status.ConnectedPeers)
    fmt.Printf("  Version: %s\n", status.Version)

    // Store multiple files as a folder
    folder := map[string][]byte{
        "readme.md":  []byte("# Project\nThis is a test project"),
        "main.go":    []byte("package main\n\nfunc main() {}"),
        "config.yml": []byte("port: 8080\nhost: localhost"),
    }
    folderCID, err := common.StoreFolder(folder)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Stored folder with CID: %s\n", folderCID)

    // List folder contents
    entries, err := client.Ls(folderCID)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Folder contents:")
    for _, entry := range entries {
        fmt.Printf("  - %s\n", entry)
    }
}

Complete WebAuthn Example

package main

import (
    "fmt"
    "log"
    "github.com/sonr-io/common"
    "github.com/sonr-io/common/webauthn"
)

func main() {
    // Generate challenge for registration
    challenge, err := common.NewChallenge()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Registration Challenge: %s\n", challenge)

    // Verify request origin
    origin := "https://example.com"
    allowedOrigins := []string{
        "https://example.com",
        "https://app.example.com",
    }

    if err := common.VerifyOrigin(origin, allowedOrigins); err != nil {
        log.Fatalf("Origin verification failed: %v", err)
    }
    fmt.Println("Origin verified!")

    // Simulate receiving credential creation response from client
    // In a real scenario, this would come from the browser
    credentialJSON := []byte(`{
        "id": "base64-credential-id",
        "type": "public-key",
        "rawId": "base64-raw-id",
        "response": {
            "clientDataJSON": "base64-client-data",
            "attestationObject": "base64-attestation"
        }
    }`)

    // Parse credential creation response
    credResponse, err := common.UnmarshalCredentialCreation(credentialJSON)
    if err != nil {
        log.Printf("Parse error (expected in demo): %v", err)
    }

    // For authentication, generate a new challenge
    authChallenge, err := common.NewChallenge()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Authentication Challenge: %s\n", authChallenge)

    // Encode/decode example
    secretData := []byte("secret-session-data")
    encoded := common.EncodeBase64URL(secretData)
    fmt.Printf("Encoded: %s\n", encoded)

    decoded, err := common.DecodeBase64URL(encoded)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Decoded: %s\n", string(decoded))

    // Challenge length validation
    expectedLength := common.ChallengeLength()
    fmt.Printf("Expected challenge length: %d bytes\n", expectedLength)
}

Module Structure

common/
 common.go              # Helper functions and simplified API
 common_test.go         # Comprehensive test suite
 Makefile               # Testing and development tasks
 README.md              # This file
 go.mod                 # Go module definition
 ipfs/                  # IPFS module
    client.go          # IPFS client implementation
    file.go            # File operations
    folder.go          # Folder operations
 webauthn/              # WebAuthn module
     client.go          # WebAuthn client operations
     credential.go      # Credential types
     assertion.go       # Authentication assertions
     attestation.go     # Registration attestation
     challenge.go       # Challenge generation
     base64.go          # Base64 URL encoding
     decoder.go         # JSON decoding utilities
     ...                # Additional WebAuthn components

Requirements

  • Go 1.25 or higher
  • IPFS daemon running (for IPFS operations)
  • Dependencies managed via go.mod

Contributing

We welcome contributions! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Test your changes (make test)
  4. Format your code (make fmt)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Development Workflow

# Install dependencies
make deps

# Run tests during development
make test

# Check code quality
make check

# Generate coverage report
make coverage-all

# Run benchmarks
make bench

License

This project is part of the Sonr network. See the LICENSE file for details.

Support

For issues, questions, or contributions, please visit:

Acknowledgments

Description
No description provided
Readme 235 KiB
Languages
Go 99.3%
Makefile 0.7%