main.go raw

   1  package main
   2  
   3  import (
   4  	"fmt"
   5  	"os"
   6  	"time"
   7  
   8  	"next.orly.dev/pkg/nostr/crypto/keys"
   9  	"next.orly.dev/pkg/nostr/encoders/hex"
  10  	"next.orly.dev/pkg/find"
  11  	"next.orly.dev/pkg/nostr/interfaces/signer"
  12  	"next.orly.dev/pkg/nostr/interfaces/signer/p8k"
  13  )
  14  
  15  func main() {
  16  	if len(os.Args) < 2 {
  17  		printUsage()
  18  		os.Exit(1)
  19  	}
  20  
  21  	command := os.Args[1]
  22  
  23  	switch command {
  24  	case "register":
  25  		handleRegister()
  26  	case "transfer":
  27  		handleTransfer()
  28  	case "verify-name":
  29  		handleVerifyName()
  30  	case "generate-key":
  31  		handleGenerateKey()
  32  	case "issue-cert":
  33  		handleIssueCert()
  34  	case "help":
  35  		printUsage()
  36  	default:
  37  		fmt.Printf("Unknown command: %s\n\n", command)
  38  		printUsage()
  39  		os.Exit(1)
  40  	}
  41  }
  42  
  43  func printUsage() {
  44  	fmt.Println("FIND - Free Internet Name Daemon")
  45  	fmt.Println("Usage: find <command> [options]")
  46  	fmt.Println()
  47  	fmt.Println("Commands:")
  48  	fmt.Println("  register <name>              Create a registration proposal for a name")
  49  	fmt.Println("  transfer <name> <new-owner>  Transfer a name to a new owner")
  50  	fmt.Println("  verify-name <name>           Validate a name format")
  51  	fmt.Println("  generate-key                 Generate a new key pair")
  52  	fmt.Println("  issue-cert <name>            Issue a certificate for a name")
  53  	fmt.Println("  help                         Show this help message")
  54  	fmt.Println()
  55  	fmt.Println("Examples:")
  56  	fmt.Println("  find verify-name example.com")
  57  	fmt.Println("  find register myname.nostr")
  58  	fmt.Println("  find generate-key")
  59  }
  60  
  61  func handleRegister() {
  62  	if len(os.Args) < 3 {
  63  		fmt.Println("Usage: find register <name>")
  64  		os.Exit(1)
  65  	}
  66  
  67  	name := os.Args[2]
  68  
  69  	// Validate the name
  70  	if err := find.ValidateName(name); err != nil {
  71  		fmt.Printf("Invalid name: %v\n", err)
  72  		os.Exit(1)
  73  	}
  74  
  75  	// Generate a key pair for this example
  76  	// In production, this would load from a secure keystore
  77  	signer, err := p8k.New()
  78  	if err != nil {
  79  		fmt.Printf("Failed to create signer: %v\n", err)
  80  		os.Exit(1)
  81  	}
  82  
  83  	if err := signer.Generate(); err != nil {
  84  		fmt.Printf("Failed to generate key: %v\n", err)
  85  		os.Exit(1)
  86  	}
  87  
  88  	// Create registration proposal
  89  	proposal, err := find.NewRegistrationProposal(name, find.ActionRegister, signer)
  90  	if err != nil {
  91  		fmt.Printf("Failed to create proposal: %v\n", err)
  92  		os.Exit(1)
  93  	}
  94  
  95  	fmt.Printf("Registration Proposal Created\n")
  96  	fmt.Printf("==============================\n")
  97  	fmt.Printf("Name:       %s\n", name)
  98  	fmt.Printf("Pubkey:     %s\n", hex.Enc(signer.Pub()))
  99  	fmt.Printf("Event ID:   %s\n", hex.Enc(proposal.GetIDBytes()))
 100  	fmt.Printf("Kind:       %d\n", proposal.Kind)
 101  	fmt.Printf("Created At: %s\n", time.Unix(proposal.CreatedAt, 0))
 102  	fmt.Printf("\nEvent JSON:\n")
 103  	json := proposal.Marshal(nil)
 104  	fmt.Println(string(json))
 105  }
 106  
 107  func handleTransfer() {
 108  	if len(os.Args) < 4 {
 109  		fmt.Println("Usage: find transfer <name> <new-owner-pubkey>")
 110  		os.Exit(1)
 111  	}
 112  
 113  	name := os.Args[2]
 114  	newOwnerPubkey := os.Args[3]
 115  
 116  	// Validate the name
 117  	if err := find.ValidateName(name); err != nil {
 118  		fmt.Printf("Invalid name: %v\n", err)
 119  		os.Exit(1)
 120  	}
 121  
 122  	// Generate current owner key (in production, load from keystore)
 123  	currentOwner, err := p8k.New()
 124  	if err != nil {
 125  		fmt.Printf("Failed to create current owner signer: %v\n", err)
 126  		os.Exit(1)
 127  	}
 128  
 129  	if err := currentOwner.Generate(); err != nil {
 130  		fmt.Printf("Failed to generate current owner key: %v\n", err)
 131  		os.Exit(1)
 132  	}
 133  
 134  	// Authorize the transfer
 135  	prevSig, timestamp, err := find.AuthorizeTransfer(name, newOwnerPubkey, currentOwner)
 136  	if err != nil {
 137  		fmt.Printf("Failed to authorize transfer: %v\n", err)
 138  		os.Exit(1)
 139  	}
 140  
 141  	fmt.Printf("Transfer Authorization Created\n")
 142  	fmt.Printf("===============================\n")
 143  	fmt.Printf("Name:           %s\n", name)
 144  	fmt.Printf("Current Owner:  %s\n", hex.Enc(currentOwner.Pub()))
 145  	fmt.Printf("New Owner:      %s\n", newOwnerPubkey)
 146  	fmt.Printf("Timestamp:      %s\n", timestamp)
 147  	fmt.Printf("Signature:      %s\n", prevSig)
 148  	fmt.Printf("\nTo complete the transfer, the new owner must create a proposal with:")
 149  	fmt.Printf("  prev_owner: %s\n", hex.Enc(currentOwner.Pub()))
 150  	fmt.Printf("  prev_sig: %s\n", prevSig)
 151  }
 152  
 153  func handleVerifyName() {
 154  	if len(os.Args) < 3 {
 155  		fmt.Println("Usage: find verify-name <name>")
 156  		os.Exit(1)
 157  	}
 158  
 159  	name := os.Args[2]
 160  
 161  	// Validate the name
 162  	if err := find.ValidateName(name); err != nil {
 163  		fmt.Printf("❌ Invalid name: %v\n", err)
 164  		os.Exit(1)
 165  	}
 166  
 167  	normalized := find.NormalizeName(name)
 168  	isTLD := find.IsTLD(normalized)
 169  	parent := find.GetParentDomain(normalized)
 170  
 171  	fmt.Printf("✓ Valid name\n")
 172  	fmt.Printf("==============\n")
 173  	fmt.Printf("Original:   %s\n", name)
 174  	fmt.Printf("Normalized: %s\n", normalized)
 175  	fmt.Printf("Is TLD:     %v\n", isTLD)
 176  	if parent != "" {
 177  		fmt.Printf("Parent:     %s\n", parent)
 178  	}
 179  }
 180  
 181  func handleGenerateKey() {
 182  	// Generate a new key pair
 183  	secKey, err := keys.GenerateSecretKey()
 184  	if err != nil {
 185  		fmt.Printf("Failed to generate secret key: %v\n", err)
 186  		os.Exit(1)
 187  	}
 188  
 189  	secKeyHex := hex.Enc(secKey)
 190  	pubKeyHex, err := keys.GetPublicKeyHex(secKeyHex)
 191  	if err != nil {
 192  		fmt.Printf("Failed to derive public key: %v\n", err)
 193  		os.Exit(1)
 194  	}
 195  
 196  	fmt.Println("New Key Pair Generated")
 197  	fmt.Println("======================")
 198  	fmt.Printf("Secret Key (keep safe!): %s\n", secKeyHex)
 199  	fmt.Printf("Public Key:              %s\n", pubKeyHex)
 200  	fmt.Println()
 201  	fmt.Println("⚠️  IMPORTANT: Store the secret key securely. Anyone with access to it can control your names.")
 202  }
 203  
 204  func handleIssueCert() {
 205  	if len(os.Args) < 3 {
 206  		fmt.Println("Usage: find issue-cert <name>")
 207  		os.Exit(1)
 208  	}
 209  
 210  	name := os.Args[2]
 211  
 212  	// Validate the name
 213  	if err := find.ValidateName(name); err != nil {
 214  		fmt.Printf("Invalid name: %v\n", err)
 215  		os.Exit(1)
 216  	}
 217  
 218  	// Generate name owner key
 219  	owner, err := p8k.New()
 220  	if err != nil {
 221  		fmt.Printf("Failed to create owner signer: %v\n", err)
 222  		os.Exit(1)
 223  	}
 224  
 225  	if err := owner.Generate(); err != nil {
 226  		fmt.Printf("Failed to generate owner key: %v\n", err)
 227  		os.Exit(1)
 228  	}
 229  
 230  	// Generate certificate key (different from name owner)
 231  	certSigner, err := p8k.New()
 232  	if err != nil {
 233  		fmt.Printf("Failed to create cert signer: %v\n", err)
 234  		os.Exit(1)
 235  	}
 236  
 237  	if err := certSigner.Generate(); err != nil {
 238  		fmt.Printf("Failed to generate cert key: %v\n", err)
 239  		os.Exit(1)
 240  	}
 241  
 242  	certPubkey := hex.Enc(certSigner.Pub())
 243  
 244  	// Generate 3 witness signers (in production, these would be separate services)
 245  	var witnesses []signer.I
 246  	for i := 0; i < 3; i++ {
 247  		witness, err := p8k.New()
 248  		if err != nil {
 249  			fmt.Printf("Failed to create witness %d: %v\n", i, err)
 250  			os.Exit(1)
 251  		}
 252  
 253  		if err := witness.Generate(); err != nil {
 254  			fmt.Printf("Failed to generate witness %d key: %v\n", i, err)
 255  			os.Exit(1)
 256  		}
 257  
 258  		witnesses = append(witnesses, witness)
 259  	}
 260  
 261  	// Issue certificate (90 day validity)
 262  	cert, err := find.IssueCertificate(name, certPubkey, find.CertificateValidity, owner, witnesses)
 263  	if err != nil {
 264  		fmt.Printf("Failed to issue certificate: %v\n", err)
 265  		os.Exit(1)
 266  	}
 267  
 268  	fmt.Printf("Certificate Issued\n")
 269  	fmt.Printf("==================\n")
 270  	fmt.Printf("Name:          %s\n", cert.Name)
 271  	fmt.Printf("Cert Pubkey:   %s\n", cert.CertPubkey)
 272  	fmt.Printf("Valid From:    %s\n", cert.ValidFrom)
 273  	fmt.Printf("Valid Until:   %s\n", cert.ValidUntil)
 274  	fmt.Printf("Challenge:     %s\n", cert.Challenge)
 275  	fmt.Printf("Witnesses:     %d\n", len(cert.Witnesses))
 276  	fmt.Printf("Algorithm:     %s\n", cert.Algorithm)
 277  	fmt.Printf("Usage:         %s\n", cert.Usage)
 278  
 279  	fmt.Printf("\nWitness Pubkeys:\n")
 280  	for i, w := range cert.Witnesses {
 281  		fmt.Printf("  %d: %s\n", i+1, w.Pubkey)
 282  	}
 283  }
 284