mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
The credential helper stored accounts keyed by handle, which broke on handle renames and let the Docker `store` path (username+secret, no DID) overwrite a good account with a DID-less one — how evan.jarrett.net on buoy.cr ended up active with a blank DID. Re-key everything by the stable DID, treating handle as a mutable display label. DID is recovered client-side via standard AT-proto handle resolution (DNS TXT _atproto.<handle> + HTTPS .well-known/atproto-did) — no server change, no JWT, no auth, and no indigo pulled into the helper. - resolve.go: stdlib handle->DID resolver - config.go: v3 DID-keyed schema; find/activeAccount/upsert/rekey helpers; upsert never blanks a known DID; migrateV2toV3 re-keys existing files in place (no login lost; DID-less accounts stay provisional and self-heal) - protocol.go: store resolves+preserves DID; get lazily backfills+re-keys; list reports the active account's handle (was arbitrary map iteration) - status/switch/logout: display handle, key/compare by DID - config_test.go: migration, upsert-never-blanks-DID, rekey, find, list, resolver parse helpers Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
66 lines
1.3 KiB
Go
66 lines
1.3 KiB
Go
package credhelper
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func newStatusCmd() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "status",
|
|
Short: "Show all configured accounts",
|
|
RunE: runStatus,
|
|
}
|
|
}
|
|
|
|
func runStatus(cmd *cobra.Command, args []string) error {
|
|
sc, err := loadConfig()
|
|
if err != nil {
|
|
return fmt.Errorf("loading config: %w", err)
|
|
}
|
|
|
|
if len(sc.Registries) == 0 {
|
|
fmt.Fprintf(os.Stderr, "No accounts configured.\n")
|
|
fmt.Fprintf(os.Stderr, "Run: %s login\n", cfg.BinaryName)
|
|
return nil
|
|
}
|
|
|
|
// Sort registry URLs for stable output
|
|
var urls []string
|
|
for url := range sc.Registries {
|
|
urls = append(urls, url)
|
|
}
|
|
sort.Strings(urls)
|
|
|
|
for _, url := range urls {
|
|
reg := sc.Registries[url]
|
|
fmt.Printf("%s\n", url)
|
|
|
|
// Sort accounts by handle for stable output (the map is keyed by DID).
|
|
accts := make([]*Account, 0, len(reg.Accounts))
|
|
for _, acct := range reg.Accounts {
|
|
accts = append(accts, acct)
|
|
}
|
|
sort.Slice(accts, func(i, j int) bool { return accts[i].Handle < accts[j].Handle })
|
|
|
|
active := reg.activeAccount()
|
|
for _, acct := range accts {
|
|
marker := " "
|
|
if acct == active {
|
|
marker = "* "
|
|
}
|
|
did := ""
|
|
if acct.DID != "" {
|
|
did = fmt.Sprintf(" (%s)", acct.DID)
|
|
}
|
|
fmt.Printf(" %s%s%s\n", marker, acct.Handle, did)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
|
|
return nil
|
|
}
|