Files
mopac-keyproxy/cmd/keyproxy/main.go
T
mrcharles 24a57c1dff Add keyproxy CLI with serve and get commands
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
2026-08-28 22:30:50 -05:00

143 lines
3.8 KiB
Go

// 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-<name>)")
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
}