nmcd

Pure Go Namecoin library and daemon using btcd as dependencies (not forks).

Overview

nmcd is a library-first Namecoin implementation built using btcd libraries. It can be embedded directly into your Go applications for in-process name resolution and registration, or run as a standalone daemon for traditional RPC access.

Key Highlights:

Quick Start (Library Usage)

Installation

go get github.com/opd-ai/nmcd

Basic Example: Resolve a Name

package main

import (
    "context"
    "fmt"
    "log"
    
    "github.com/opd-ai/nmcd/client"
)

func main() {
    // Create embedded client (runs in-process)
    nc, err := client.NewClient(&client.Config{
        Mode:    client.ModeAuto,  // Auto-detect daemon or use embedded
        Network: "mainnet",
    })
    if err != nil {
        log.Fatal(err)
    }
    defer nc.Close()
    
    // Resolve a Namecoin name
    ctx := context.Background()
    record, err := nc.ResolveName(ctx, "d/example")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Name: %s\nValue: %s\nOwner: %s\n",
        record.Name, record.Value, record.Address)
}

Register or Update Names

// Register a new name
result, err := nc.RegisterName(ctx, "d/mysite", `{"ip":"1.2.3.4"}`, nil)
fmt.Printf("Registration TX: %s\n", result.TxHash)

// Update existing name
result, err = nc.UpdateName(ctx, "d/mysite", `{"ip":"5.6.7.8"}`, nil)
fmt.Printf("Update TX: %s\n", result.TxHash)

// List names with filters
names, err := nc.ListNames(ctx, &client.ListFilter{
    Namespace: "d/",
    Limit:     100,
})

πŸ“š See docs/EXAMPLES.md for detailed walkthroughs and patterns.

Library Features

Daemon Features

When run as a standalone daemon, nmcd provides:

Block Synchronization

The daemon automatically synchronizes with the Namecoin network using a headers-first sync protocol:

  1. Initial Block Download (IBD): Downloads block headers from peers, validates the chain, then fetches full blocks
  2. Ongoing Sync: Continuously monitors peers for new blocks and processes them as they arrive
  3. Peer Selection: Tracks peer reliability and latency to choose the best sync sources
  4. Health Monitoring: Use the /ready endpoint to check if sync is complete

The embedded client mode automatically connects to the network and syncs blocks when MaxPeers > 0 (default: 8). By default, it uses DNS seed discovery to find peers. To disable automatic network sync, set MaxPeers to 0 or provide custom BootstrapPeers.

Documentation

API Stability

Starting with v1.0.0, nmcd follows Semantic Versioning and provides strong backward compatibility guarantees:

See CHANGELOG.md for:

Current Status: v0.1.0 (development) β†’ Working towards v1.0.0 production release

Mode Selection

nmcd supports three operational modes:

Automatically detects if a daemon is running on localhost:8336 and uses it; otherwise runs in embedded mode.

nc, err := client.NewClient(&client.Config{
    Mode: client.ModeAuto,  // Default
})

Use When:

Embedded Mode

Runs the full blockchain, database, and network stack in-process. No external daemon required.

nc, err := client.NewEmbeddedClient(&client.Config{
    Mode:    client.ModeEmbedded,
    DataDir: "/path/to/data",
    Network: "mainnet",
})

Network Connectivity:

By default, embedded mode automatically connects to the Namecoin network using DNS seed discovery (MaxPeers defaults to 8). To customize network behavior:

// Disable automatic network sync (offline mode)
nc, err := client.NewEmbeddedClient(&client.Config{
    Mode:     client.ModeEmbedded,
    MaxPeers: 0,  // No peer connections
})

// Use custom bootstrap peers (skip DNS seeds)
nc, err := client.NewEmbeddedClient(&client.Config{
    Mode: client.ModeEmbedded,
    BootstrapPeers: []string{
        "peer1.example.com:8334",
        "peer2.example.com:8334",
    },
})

Use When:

Pros:

Cons:

Daemon Mode

Connects to an existing nmcd or Namecoin Core daemon via RPC.

nc, err := client.NewDaemonClient(&client.Config{
    Mode:        client.ModeDaemon,
    RPCAddr:     "http://localhost:8336",
    RPCUser:     "user",
    RPCPassword: "pass",
})

Use When:

Pros:

Cons:

πŸ“š See docs/MODES.md for detailed comparison and recommendations.

Architecture

Library Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Your Go Application                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                    nmcd Library (client/)                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚         NameClient Interface (Public API)              β”‚  β”‚
β”‚  β”‚  β€’ ResolveName(name) β†’ NameRecord                      β”‚  β”‚
β”‚  β”‚  β€’ RegisterName(name, value, opts) β†’ TxHash            β”‚  β”‚
β”‚  β”‚  β€’ UpdateName(name, value, opts) β†’ TxHash              β”‚  β”‚
β”‚  β”‚  β€’ ListNames(filter) β†’ []NameRecord                    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚            β–²                              β–²                    β”‚
β”‚            β”‚                              β”‚                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚  EmbeddedClient   β”‚        β”‚   DaemonClient      β”‚        β”‚
β”‚  β”‚  (in-process)     β”‚        β”‚   (RPC to daemon)   β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β”‚            β”‚                                                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”‚
β”‚  β”‚  Embedded Components               β”‚                       β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”β”‚                       β”‚
β”‚  β”‚  β”‚ chain/ β”‚ β”‚namedb/ β”‚ β”‚network/β”‚β”‚                       β”‚
β”‚  β”‚  β”‚validateβ”‚ β”‚bbolt DBβ”‚ β”‚ peer   β”‚β”‚                       β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚                       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Daemon Component Architecture

  1. namedb: Name database with bbolt storage
    • Stores name records with expiration tracking
    • Historical operation tracking
    • Thread-safe with RWMutex
  2. chain: Blockchain wrapper
    • Embeds btcd’s blockchain.BlockChain
    • Extends validation with name operation hooks
    • Manages name expiration (36000 blocks ~250 days)
  3. network: P2P networking
    • Uses btcd/peer for peer management
    • Interface-based connections (net.Conn)
    • Handles block/tx propagation
  4. rpc: JSON-RPC server
    • Standard library net/http
    • Name-specific RPC methods
    • Thread-safe access
  5. config: Configuration management
    • Network selection (mainnet/testnet/regtest)
    • Data directory management

Using as a Daemon

While nmcd is primarily a library, it can also run as a standalone daemon for traditional RPC access.

Building the Daemon

go build -v ./cmd/nmcd

Running the Daemon

# Run with defaults (auto-discovers peers via DNS seeds)
./nmcd

# Custom data directory
./nmcd -datadir=/path/to/data

# Testnet
./nmcd -network=testnet

# Custom RPC port
./nmcd -rpcaddr=127.0.0.1:18336

# Connect to specific peer (bypasses DNS seed discovery)
./nmcd -addpeer=peer.example.com:8334

# Enable RPC authentication (recommended for security)
./nmcd -rpcuser=myuser -rpcpassword=mypassword

Note: When using nmcd as a library, you don’t need to run the daemon unless you want to share blockchain state across multiple applications.

Peer Discovery (Daemon Mode)

nmcd supports automatic peer discovery via DNS seeds. When started without the -addpeer flag, the node will:

  1. Query official Namecoin DNS seed servers
  2. Resolve IP addresses of active network nodes
  3. Connect to discovered peers automatically

Mainnet DNS Seeds:

Testnet DNS Seeds:

To bypass DNS seed discovery and connect to specific peers, use the -addpeer flag.

RPC API (Daemon Mode)

When running nmcd as a daemon, it exposes a JSON-RPC interface for external access. If you’re using nmcd as a library, use the Go API instead of RPC for better performance and type safety.

The RPC server supports HTTP Basic Authentication. When both -rpcuser and -rpcpassword flags are set, all RPC requests must include valid credentials.

Security Considerations:

# With authentication enabled
curl -X POST http://127.0.0.1:8336 \
  -u myuser:mypassword \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"getinfo","params":[],"id":1}'

Standard Methods

Name Methods

Wallet Methods

Wallet Encryption

Security Warning: By default, nmcd wallets store private keys unencrypted in wallet.json with file permissions 0600. For production use, always encrypt your wallet.

Health and Readiness Endpoints

nmcd provides HTTP health check endpoints suitable for Kubernetes liveness and readiness probes, load balancers, and monitoring systems.

Health Endpoint (/health):

curl http://127.0.0.1:8336/health

Returns HTTP 200 OK when the daemon is running and initialized, or 503 Service Unavailable when initializing.

Response:

{
  "status": "healthy",
  "block_height": 500000,
  "peers": 8,
  "syncing": true
}

The syncing field is optional (omitempty) and appears when the node is initializing or syncing blocks.

Use this endpoint for liveness probes to detect if the process is alive and responsive.

Readiness Endpoint (/ready):

curl http://127.0.0.1:8336/ready

Returns HTTP 200 OK when the daemon is ready to serve requests (sync complete), or 503 Service Unavailable when syncing or initializing.

Response:

{
  "status": "ready",
  "block_height": 500000,
  "peers": 8,
  "syncing": false
}

Use this endpoint for readiness probes to detect if the node is ready to handle traffic.

Kubernetes Example:

livenessProbe:
  httpGet:
    path: /health
    port: 8336
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 8336
  initialDelaySeconds: 5
  periodSeconds: 5

Security Note: These endpoints do not require authentication and are intended for health checking systems. Bind the RPC server to localhost (default) or use network-level controls to restrict access.

Prometheus Metrics Endpoint

nmcd can optionally expose metrics in Prometheus format for monitoring and observability. The metrics are served on a separate HTTP endpoint from the RPC server.

Enable Prometheus metrics:

# Enable on default port 9100
./nmcd -prometheusaddr=127.0.0.1:9100

# Or use custom port
./nmcd -prometheusaddr=127.0.0.1:9999

Once enabled, metrics are available at http://<address>/metrics in Prometheus text format.

Security Considerations:

The Prometheus metrics endpoint does not implement authentication and exposes operational data about the node (e.g., block processing statistics and name operation counts). For production deployments, you should:

If you bind the metrics endpoint to a non-localhost network interface, use network-level controls (firewall rules, VPN, or restricted subnets) to allow access only from trusted monitoring systems.

Available Metrics:

Block Processing:

Name Operations:

Network:

Transactions:

Validation Errors:

Database Performance (New):

RPC Performance (New):

Error Breakdown (New):

Go Runtime (New):

Example Prometheus Query:

curl http://127.0.0.1:9100/metrics

Prometheus Configuration:

scrape_configs:
  - job_name: 'nmcd'
    static_configs:
      - targets: ['localhost:9100']

Grafana Dashboard:

The metrics can be visualized in Grafana. Key panels to consider:

Example Queries:

# Top 5 slowest RPC methods (with method label aggregation)
topk(5, avg by (method) (nmcd_rpc_duration_seconds))

# Error rate by category (per second, grouped by type)
sum by (type) (rate(nmcd_errors_total[5m]))

# Database read/write latency comparison
nmcd_namedb_read_latency_seconds
nmcd_namedb_write_latency_seconds

# RPC request rate by method
sum by (method) (rate(nmcd_rpc_requests_total[5m]))

Examples

The examples/ directory contains several working examples demonstrating library usage:

Run an example:

go run ./examples/simple_resolve d/example
go run ./examples/list_names --namespace=d/

πŸ“š For detailed walkthroughs, see docs/EXAMPLES.md


Wallet

nmcd includes basic wallet functionality for managing name operations. The wallet stores private keys in wallet.json within the data directory.

Security Note: The wallet file contains unencrypted private keys. Ensure proper file permissions (0600) and secure the data directory.

Library Usage: When using nmcd as a library, the wallet is automatically initialized in the data directory. Use DisableWallet: true in the config to disable wallet functionality if you only need name resolution.


Use Cases

Library Mode (Embedded)

Best For:

Example Applications:

Daemon Mode

Best For:

Example Setups:


Dependencies

Design Principles

  1. Composition over Reimplementation: Use btcd libraries directly
  2. Standard Library: net/http for RPC, no web frameworks
  3. Interface Types: net.Conn not concrete *net.TCPConn
  4. Thread Safety: Mutex protection for all shared state
  5. Minimal Code: Focus on name-specific functionality only

Name Operations

The implementation supports three name operations:

  1. NAME_NEW: Pre-register a name (prevents front-running)
  2. NAME_FIRSTUPDATE: First registration of a name
  3. NAME_UPDATE: Update existing name value

Names expire after 36000 blocks (~250 days) and must be renewed.

Name Deletion

Namecoin does not have a separate NAME_DELETE operation. To delete a name, use NAME_UPDATE with an empty value:

// Delete a name by setting empty value
result, err := nc.UpdateName(ctx, "d/mysite", "", nil)

This effectively removes the name’s data while maintaining ownership on the blockchain. The name will still expire after 36,000 blocks unless renewed.

Project Structure

nmcd/
β”œβ”€β”€ client/          # πŸ”§ Library public API (import this!)
β”‚   β”œβ”€β”€ types.go           # NameClient interface and types
β”‚   β”œβ”€β”€ embedded.go        # In-process embedded client
β”‚   β”œβ”€β”€ daemon.go          # RPC daemon client
β”‚   └── *_test.go          # Comprehensive test suite
β”‚
β”œβ”€β”€ cmd/nmcd/        # Daemon binary entry point
β”‚   └── main.go            # CLI flags and daemon setup
β”‚
β”œβ”€β”€ cmd/permamail/   # πŸ“§ Permamail CLI tool
β”‚   └── main.go            # Email forwarding management
β”‚
β”œβ”€β”€ internal/        # Internal packages (not importable)
β”‚   └── server/            # Daemon server implementation
β”‚
β”œβ”€β”€ bridge/          # Email forwarding bridge adapter
β”œβ”€β”€ mail/            # SMTP routing and relay
β”œβ”€β”€ namedb/          # Name database (bbolt)
β”œβ”€β”€ chain/           # Blockchain wrapper (btcd integration)
β”œβ”€β”€ network/         # P2P networking (btcd/peer)
β”œβ”€β”€ rpc/             # JSON-RPC server (daemon mode)
β”œβ”€β”€ wallet/          # Basic wallet functionality
β”œβ”€β”€ config/          # Network configuration
β”‚
β”œβ”€β”€ docs/            # πŸ“š Documentation
β”‚   β”œβ”€β”€ API.md             # Complete API reference
β”‚   β”œβ”€β”€ EMBEDDING.md       # Integration guide
β”‚   β”œβ”€β”€ EXAMPLES.md        # Example walkthroughs
β”‚   β”œβ”€β”€ MODES.md           # Mode comparison
β”‚   └── PERFORMANCE.md     # Optimization tips
β”‚
β”œβ”€β”€ examples/        # 🎯 Working code examples
β”‚   β”œβ”€β”€ simple_resolve/    # Basic name resolution
β”‚   β”œβ”€β”€ embedded_client/   # Embedded mode demo
β”‚   β”œβ”€β”€ register_name/     # Name registration
β”‚   β”œβ”€β”€ update_name/       # Name updates
β”‚   β”œβ”€β”€ list_names/        # Name filtering
β”‚   β”œβ”€β”€ bridge_adapter/    # Email bridge usage
β”‚   β”œβ”€β”€ mail_router/       # Email routing example
β”‚   β”œβ”€β”€ smtp_relay/        # SMTP relay deployment
β”‚   └── namedb/            # Direct database access
β”‚
└── README.md        # This file

Import Paths:

// Primary library interface
import "github.com/opd-ai/nmcd/client"

// For advanced usage (typically not needed)
import "github.com/opd-ai/nmcd/namedb"
import "github.com/opd-ai/nmcd/config"

Permamail: Decentralized Email Forwarding

nmcd includes permamail, a command-line tool for managing decentralized email forwarding via Namecoin. It allows you to register .bit email addresses that forward to your real email address.

Installation

# Build both nmcd and permamail
make build

# Or build permamail only
go build -o permamail ./cmd/permamail

Usage

# Register a new .bit email address
permamail register alice --forward user@gmail.com

# Update forwarding configuration with backup
permamail update alice --forward newemail@proton.me --backup backup@proton.me

# Look up current configuration
permamail lookup alice

# Start SMTP relay server
# Note: Avoid passing SMTP passwords directly on the command line, as they
# may be visible to other local users via process listings (ps, /proc).
# Consider using environment variables or a config file with restricted permissions.
permamail serve --upstream smtp.sendgrid.net --upstreamport 587 \
                --smtpuser apikey --smtppass <api-key>

Architecture

Permamail consists of three main components:

  1. Bridge Adapter (bridge/): Translates Namecoin name records to email forwarding configuration
  2. Mail Router (mail/router.go): Routes .bit addresses to real email addresses with caching
  3. SMTP Relay (mail/smtp.go): Accepts mail to .bit addresses and forwards to real inboxes

The permamail CLI integrates these components into a single tool for managing email forwarding.

Email Configuration Format

Email forwarding is stored in Namecoin name values as JSON:

{
  "email": "user@gmail.com",
  "backup": ["backup@proton.me"],
  "pubkey": "base64_encoded_public_key"
}

Running an SMTP Relay

To run a production SMTP relay that forwards .bit emails:

# Start relay on port 2525, forwarding via Gmail
permamail serve --listen :2525 \
                --upstream smtp.gmail.com \
                --upstreamport 587 \
                --smtpuser your.email@gmail.com \
                --smtppass your-app-password

# Users can now send email to alice@mail.bit
# The relay will resolve alice -> user@gmail.com and forward

Note: Currently, name registration and updates require a running nmcd node with wallet enabled. The register and update commands prepare the configuration and display instructions for completing the operation via nmcd RPC. Full integrated wallet support is planned for future releases.

Examples

See examples/smtp_relay/ for a complete production-ready SMTP relay deployment with systemd integration.


Security

RPC Security Features

Authentication:

Rate Limiting:

Request Size Limits:

Security Headers:

Example RPC Server Configuration:

server, err := rpc.NewServer(&rpc.Config{
    Blockchain:     blockchain,
    PeerMgr:        peerMgr,
    Wallet:         wallet,
    ListenAddr:     "127.0.0.1:8336",
    RPCUser:        "myuser",
    RPCPassword:    "mypassword",
    RateLimit:      100,        // requests per minute (0 = use default)
    MaxRequestSize: 1024 * 1024, // 1MB (0 = use default)
})

Name Operation Security


Getting Help


Testing

nmcd has comprehensive test coverage across all packages:

Running Tests

# Run all tests
go test ./...

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

# Run tests with race detector
go test -race ./...

# Generate HTML coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html

Test Coverage Status

Critical Packages:

Other Packages:

Note: Command-line entry points (cmd/*) and integration components have lower coverage by design, as they primarily wire together well-tested components.

πŸ“Š Detailed Coverage Analysis: See docs/development/COVERAGE.md for comprehensive coverage analysis and improvement roadmap.


Building and Releases

Building from Source

To build nmcd from source, you need Go 1.24 or later:

# Clone the repository
git clone https://github.com/opd-ai/nmcd
cd nmcd

# Build the binary
make build

# Or build with custom version
go build -ldflags="-X main.version=1.0.0" ./cmd/nmcd

Official Releases

nmcd provides automated binary releases for multiple platforms. Releases are triggered when a version tag is pushed:

Supported Platforms:

Download Options:

  1. Pre-built Binaries: Download from GitHub Releases
    • All binaries include SHA256 checksums for verification
    • Extract and run directly (no installation required)
  2. Docker Images: Multi-architecture images available on GitHub Container Registry
    # Pull latest version
    docker pull ghcr.io/opd-ai/nmcd:latest
       
    # Run nmcd in Docker, exposing RPC only on localhost
    docker run -p 127.0.0.1:8336:8336 -v nmcd-data:/data ghcr.io/opd-ai/nmcd:latest
       
    # Pull specific version
    docker pull ghcr.io/opd-ai/nmcd:1.0.0
    

    Docker images are optimized with multi-stage builds and compressed to <100MB.

    Security note: The JSON-RPC interface is sensitive. Before exposing port 8336 beyond localhost (for example by using -p 8336:8336 or binding to a non-loopback address), configure strong RPC credentials via -rpcuser and -rpcpassword (or enforce equivalent network access controls such as a firewall, VPN, or TLS-terminating reverse proxy) to prevent unauthorized access.

Verifying Downloads:

# Verify SHA256 checksum (Linux/macOS)
sha256sum -c nmcd-linux-amd64.sha256

# Or manually compare
sha256sum nmcd-linux-amd64
cat nmcd-linux-amd64.sha256

Contributing

Contributions are welcome! Areas of interest:

Please ensure:

  1. Code follows Go best practices (go fmt, go vet)
  2. All tests pass (make test)
  3. Documentation is updated for API changes
  4. Examples are updated if public API changes

License

See LICENSE file for details.