From 24a57c1dff623e8c6cc31c015f2312146dfc7127 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Fri, 28 Aug 2026 22:30:14 -0500 Subject: [PATCH] Add keyproxy CLI with serve and get commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire the config loader, backends, and HTTP server into an executable. `serve` runs the resolve hop until SIGINT/SIGTERM (graceful shutdown); `get` resolves one ref to stdout as a bare value with no trailing newline for $(...) exec-style plumbing. Exit codes: 0 ok, 1 usage or config error, 2 resolution failure. 💘 Generated with Crush Assisted-by: Crush:glm-5.2 --- cmd/keyproxy/main.go | 142 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 cmd/keyproxy/main.go diff --git a/cmd/keyproxy/main.go b/cmd/keyproxy/main.go new file mode 100644 index 0000000..4636724 --- /dev/null +++ b/cmd/keyproxy/main.go @@ -0,0 +1,142 @@ +// Command keyproxy resolves opaque mpk- refs to key material at the +// wire. v0 backends: file (0600 env files) and env (process env); +// bitwarden and vault ship as explicit not-implemented stubs until +// phase 3. Material is memory-only: never persisted, never logged. +package main + +import ( + "context" + "flag" + "fmt" + "os" + "os/signal" + "syscall" + + "git.knownelement.com/ukrrs/mopac-keyproxy/internal/backend" + "git.knownelement.com/ukrrs/mopac-keyproxy/internal/config" + "git.knownelement.com/ukrrs/mopac-keyproxy/internal/server" +) + +const usage = `keyproxy: resolve mpk- placeholder refs to key material + +Usage: + keyproxy serve [-config PATH] [-listen ADDR] run the resolve HTTP hop + keyproxy get REF [-config PATH] resolve one ref to stdout + keyproxy help print this usage + +Backends (v0): file (0600 env files), env (process env); bitwarden and +vault are explicit not-implemented stubs until phase 3. Material is +memory-only: never persisted, never logged. + +Exit codes: 0 ok, 1 usage/config error, 2 resolution failure. +` + +func main() { + os.Exit(run(os.Args[1:])) +} + +func run(args []string) int { + if len(args) == 0 { + fmt.Fprint(os.Stderr, usage) + return 1 + } + switch args[0] { + case "help", "-h", "--help": + fmt.Print(usage) + return 0 + case "serve": + return cmdServe(args[1:]) + case "get": + return cmdGet(args[1:]) + default: + fmt.Fprintf(os.Stderr, "keyproxy: unknown command %q\n\n%s", args[0], usage) + return 1 + } +} + +// newRegistry wires every backend behind one interface; phase-3 +// connectors replace the stubs in this single place. +func newRegistry() *backend.Registry { + return backend.NewRegistry( + backend.NewFile(), + backend.NewEnv(), + backend.NewBitwarden(), + backend.NewVault(), + ) +} + +func loadConfig(path string) (*config.Config, error) { + if path == "" { + path = os.Getenv("KEYPROXY_CONFIG") + } + if path == "" { + path = "keyproxy.toml" + } + return config.Load(path) +} + +func cmdServe(args []string) int { + fs := flag.NewFlagSet("serve", flag.ContinueOnError) + configPath := fs.String("config", "", "config file (default $KEYPROXY_CONFIG, then ./keyproxy.toml)") + listen := fs.String("listen", "", "bind address (overrides config listen)") + if err := fs.Parse(args); err != nil { + return 1 + } + cfg, err := loadConfig(*configPath) + if err != nil { + fmt.Fprintf(os.Stderr, "keyproxy: %v\n", err) + return 1 + } + if *listen != "" { + cfg.Listen = *listen + } + srv, err := server.New(cfg, newRegistry(), os.Stdout) + if err != nil { + fmt.Fprintf(os.Stderr, "keyproxy: %v\n", err) + return 1 + } + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + if err := srv.ListenAndServe(ctx); err != nil { + fmt.Fprintf(os.Stderr, "keyproxy: %v\n", err) + return 1 + } + return 0 +} + +func cmdGet(args []string) int { + fs := flag.NewFlagSet("get", flag.ContinueOnError) + configPath := fs.String("config", "", "config file (default $KEYPROXY_CONFIG, then ./keyproxy.toml)") + if err := fs.Parse(args); err != nil { + return 1 + } + if fs.NArg() != 1 { + fmt.Fprintln(os.Stderr, "keyproxy get: exactly one ref required (mpk-)") + return 1 + } + name := fs.Arg(0) + cfg, err := loadConfig(*configPath) + if err != nil { + fmt.Fprintf(os.Stderr, "keyproxy: %v\n", err) + return 1 + } + ref, ok := cfg.Refs[name] + if !ok { + fmt.Fprintf(os.Stderr, "keyproxy: ref %s: unknown_ref\n", name) + return 2 + } + b, ok := newRegistry().Get(ref.Backend) + if !ok { + fmt.Fprintf(os.Stderr, "keyproxy: ref %s: backend %s not registered\n", name, ref.Backend) + return 2 + } + value, err := b.Resolve(context.Background(), ref) + if err != nil { + fmt.Fprintf(os.Stderr, "keyproxy: %v\n", backend.AsResolveError(err, ref, ref.Backend)) + return 2 + } + // No trailing newline: exec-style plumbing ($(keyproxy get mpk-x)) + // wants the bare value. + fmt.Print(value) + return 0 +}