@@ -4,8 +4,10 @@ package main
44
55import (
66 "encoding/json"
7+ "fmt"
78 "os"
89
10+ "github.com/pilot-protocol/common/badgeverify"
911 "github.com/pilot-protocol/common/crypto"
1012 "github.com/pilot-protocol/common/protocol"
1113 registry "github.com/pilot-protocol/common/registry/client"
@@ -36,9 +38,15 @@ func nodeArgToID(s string) uint32 {
3638// command hands them to the daemon, which proves ownership with the node key
3739// and submits to the registry.
3840//
41+ // pilotctl verify # show your own verification status
42+ // pilotctl verify status # same
3943// pilotctl verify --badge <badge> --badge-sig <sig>
4044// pilotctl verify --from cred.json # {"badge":..,"badge_sig":..}
4145func cmdVerify (args []string ) {
46+ if len (args ) >= 1 && args [0 ] == "status" {
47+ cmdVerifyStatus (args [1 :])
48+ return
49+ }
4250 flags , _ := parseFlags (args )
4351 badge := flagString (flags , "badge" , "" )
4452 badgeSig := flagString (flags , "badge-sig" , "" )
@@ -55,9 +63,11 @@ func cmdVerify(args []string) {
5563 badgeSig = m .BadgeSig
5664 }
5765 }
66+ // Bare `pilotctl verify` with nothing to submit is a status check — show
67+ // the user whether they are verified and, if not, how to become verified.
5868 if badge == "" || badgeSig == "" {
59- fatalCode ( "invalid_argument" ,
60- "usage: pilotctl verify --badge <badge> --badge-sig <sig> (or --from cred.json)" )
69+ cmdVerifyStatus ( args )
70+ return
6171 }
6272 d := connectDriver ()
6373 defer d .Close ()
@@ -68,6 +78,103 @@ func cmdVerify(args []string) {
6878 output (resp )
6979}
7080
81+ // cmdVerifyStatus shows whether THIS node carries a verified-address badge.
82+ // The badge is read from the registry (untrusted transport) and then verified
83+ // OFFLINE against the pinned issuer key — we never take the registry's word for
84+ // it. When unverified, it prints how to get verified.
85+ //
86+ // pilotctl verify status [--node <addr|id>]
87+ func cmdVerifyStatus (args []string ) {
88+ flags , _ := parseFlags (args )
89+
90+ var nodeID uint32
91+ var address string
92+ if n := flagString (flags , "node" , "" ); n != "" {
93+ nodeID = nodeArgToID (n )
94+ } else {
95+ d := connectDriver ()
96+ info , err := d .Info ()
97+ d .Close ()
98+ if err != nil {
99+ fatalCode ("connection_failed" ,
100+ "verify status: cannot reach the daemon (is it running?): %v" , err )
101+ }
102+ if v , ok := info ["node_id" ].(float64 ); ok {
103+ nodeID = uint32 (v )
104+ }
105+ address , _ = info ["address" ].(string )
106+ }
107+
108+ rc := connectRegistry ()
109+ defer rc .Close ()
110+ resp , err := rc .Lookup (nodeID )
111+ if err != nil {
112+ fatalCode ("connection_failed" , "verify status: registry lookup: %v" , err )
113+ }
114+
115+ badge , _ := resp ["badge" ].(string )
116+ badgeSig , _ := resp ["badge_sig" ].(string )
117+ provider , _ := resp ["verification_provider" ].(string )
118+ verifiedAt , _ := resp ["verified_at" ].(string )
119+
120+ out := map [string ]interface {}{"node_id" : nodeID }
121+ if address != "" {
122+ out ["address" ] = address
123+ }
124+
125+ status := "not_verified"
126+ verified := false
127+ var detail string
128+ if badge != "" {
129+ if _ , verr := badgeverify .VerifyForNode (badge , badgeSig , nodeID ); verr == nil {
130+ status , verified = "verified" , true
131+ } else {
132+ status , detail = "badge_present_unvalidated" , verr .Error ()
133+ }
134+ }
135+ out ["verified" ] = verified
136+ out ["status" ] = status
137+ if provider != "" {
138+ out ["provider" ] = provider
139+ }
140+ if verifiedAt != "" {
141+ out ["verified_at" ] = verifiedAt
142+ }
143+ if status == "not_verified" {
144+ out ["how_to_verify" ] = []string {
145+ fmt .Sprintf ("1. pilot-verify verify --provider github --node-id %d --github-client-id <client-id>" , nodeID ),
146+ " (authorize in your browser; it prints a badge + signature)" ,
147+ "2. pilotctl verify --badge <badge> --badge-sig <sig>" ,
148+ }
149+ }
150+ if detail != "" {
151+ out ["detail" ] = detail
152+ }
153+
154+ if jsonOutput {
155+ output (out )
156+ return
157+ }
158+ switch status {
159+ case "verified" :
160+ line := fmt .Sprintf ("✓ Verified via %s" , provider )
161+ if verifiedAt != "" {
162+ line += fmt .Sprintf (" (since %s)" , verifiedAt )
163+ }
164+ fmt .Println (line )
165+ case "badge_present_unvalidated" :
166+ fmt .Printf ("A %s badge is on file but could not be validated by this build.\n " , provider )
167+ fmt .Println ("(the issuer key may not be pinned yet)" )
168+ default :
169+ fmt .Println ("Not verified." )
170+ fmt .Println ()
171+ fmt .Println ("To get a verified badge:" )
172+ fmt .Printf (" 1. pilot-verify verify --provider github --node-id %d --github-client-id <client-id>\n " , nodeID )
173+ fmt .Println (" (authorize in your browser; it prints a badge + signature)" )
174+ fmt .Println (" 2. pilotctl verify --badge <badge> --badge-sig <sig>" )
175+ }
176+ }
177+
71178// cmdRecovery dispatches the recovery subcommands.
72179func cmdRecovery (args []string ) {
73180 if len (args ) < 1 {
0 commit comments