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