this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(toolbox): add secrets config generation and vault store layer

+587
+29
toolbox/go.mod
··· 3 3 go 1.25.5 4 4 5 5 require ( 6 + github.com/charmbracelet/huh v0.8.0 6 7 github.com/charmbracelet/log v0.4.2 7 8 github.com/hashicorp/vault/api v1.22.0 8 9 github.com/spf13/cobra v1.10.2 9 10 golang.org/x/crypto v0.47.0 11 + gopkg.in/yaml.v3 v3.0.1 10 12 ) 11 13 12 14 require ( 15 + github.com/atotto/clipboard v0.1.4 // indirect 16 + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 17 + github.com/catppuccin/go v0.3.0 // indirect 13 18 github.com/cenkalti/backoff/v4 v4.3.0 // indirect 19 + github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect 20 + github.com/charmbracelet/bubbletea v1.3.6 // indirect 21 + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect 22 + github.com/charmbracelet/lipgloss v1.1.0 // indirect 23 + github.com/charmbracelet/x/ansi v0.9.3 // indirect 24 + github.com/charmbracelet/x/cellbuf v0.0.13 // indirect 25 + github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect 26 + github.com/charmbracelet/x/term v0.2.1 // indirect 27 + github.com/dustin/go-humanize v1.0.1 // indirect 28 + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect 14 29 github.com/go-jose/go-jose/v4 v4.1.1 // indirect 15 30 github.com/go-logfmt/logfmt v0.6.0 // indirect 16 31 github.com/hashicorp/errwrap v1.1.0 // indirect ··· 22 37 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect 23 38 github.com/hashicorp/go-sockaddr v1.0.7 // indirect 24 39 github.com/hashicorp/hcl v1.0.1-vault-7 // indirect 40 + github.com/inconshreveable/mousetrap v1.1.0 // indirect 41 + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 42 + github.com/mattn/go-isatty v0.0.20 // indirect 43 + github.com/mattn/go-localereader v0.0.1 // indirect 44 + github.com/mattn/go-runewidth v0.0.16 // indirect 25 45 github.com/mitchellh/go-homedir v1.1.0 // indirect 46 + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect 26 47 github.com/mitchellh/mapstructure v1.5.0 // indirect 48 + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect 49 + github.com/muesli/cancelreader v0.2.2 // indirect 50 + github.com/muesli/termenv v0.16.0 // indirect 51 + github.com/rivo/uniseg v0.4.7 // indirect 27 52 github.com/ryanuber/go-glob v1.0.0 // indirect 53 + github.com/spf13/pflag v1.0.9 // indirect 54 + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 55 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect 28 56 golang.org/x/net v0.48.0 // indirect 57 + golang.org/x/sync v0.19.0 // indirect 29 58 golang.org/x/sys v0.40.0 // indirect 30 59 golang.org/x/text v0.33.0 // indirect 31 60 golang.org/x/time v0.12.0 // indirect
+145
toolbox/go.sum
··· 1 + github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= 2 + github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= 3 + github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= 4 + github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 5 + github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 6 + github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 7 + github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= 8 + github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= 9 + github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= 10 + github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= 11 + github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= 12 + github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 13 + github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws= 14 + github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw= 15 + github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU= 16 + github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc= 17 + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= 18 + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= 19 + github.com/charmbracelet/huh v0.8.0 h1:Xz/Pm2h64cXQZn/Jvele4J3r7DDiqFCNIVteYukxDvY= 20 + github.com/charmbracelet/huh v0.8.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4= 21 + github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= 22 + github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= 23 + github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= 24 + github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= 25 + github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0= 26 + github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= 27 + github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= 28 + github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= 29 + github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= 30 + github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= 31 + github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= 32 + github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= 33 + github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= 34 + github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= 35 + github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4= 36 + github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= 37 + github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= 38 + github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= 39 + github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= 40 + github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= 41 + github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI= 42 + github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4= 43 + github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 44 + github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= 45 + github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= 46 + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 47 + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 48 + github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 49 + github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 50 + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= 51 + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= 52 + github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 53 + github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 54 + github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI= 55 + github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA= 56 + github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= 57 + github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 58 + github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= 59 + github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 60 + github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 61 + github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 62 + github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 63 + github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 64 + github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 65 + github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 66 + github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 67 + github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 68 + github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 69 + github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= 70 + github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= 71 + github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= 72 + github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= 73 + github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM= 74 + github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= 75 + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= 76 + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= 77 + github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= 78 + github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= 79 + github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= 80 + github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= 81 + github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0= 82 + github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= 83 + github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 84 + github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 85 + github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 86 + github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 87 + github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 88 + github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 89 + github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 90 + github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 91 + github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= 92 + github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= 93 + github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 94 + github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 95 + github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 96 + github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 97 + github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= 98 + github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= 99 + github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 100 + github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 101 + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= 102 + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= 103 + github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= 104 + github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= 105 + github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= 106 + github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= 107 + github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 108 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 109 + github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 110 + github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 111 + github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 112 + github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 113 + github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= 114 + github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= 115 + github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= 116 + github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 117 + github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= 118 + github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 119 + github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 120 + github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 121 + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 122 + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 123 + go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 124 + golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= 125 + golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= 126 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= 127 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= 128 + golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= 129 + golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= 130 + golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= 131 + golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 132 + golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 133 + golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 134 + golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= 135 + golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 136 + golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= 137 + golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= 138 + golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= 139 + golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= 140 + golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= 141 + golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 142 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 143 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 144 + gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 145 + gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+102
toolbox/internal/secrets/config.go
··· 1 + package secrets 2 + 3 + import ( 4 + "fmt" 5 + "os" 6 + "sort" 7 + 8 + "gopkg.in/yaml.v3" 9 + ) 10 + 11 + // Config is the root configuration structure. 12 + // Format: 13 + // 14 + // secrets: 15 + // secret/path: 16 + // KEY_NAME: 17 + // type: random|ssh|manual 18 + // ... 19 + type Config struct { 20 + Secrets map[string]map[string]SecretSettings `yaml:"secrets"` 21 + } 22 + 23 + type SecretSettings struct { 24 + Type string `yaml:"type"` 25 + Length int `yaml:"length,omitempty"` 26 + Algorithm string `yaml:"algorithm,omitempty"` 27 + PublicKey string `yaml:"public_key,omitempty"` 28 + Description string `yaml:"description,omitempty"` 29 + } 30 + 31 + type Entry struct { 32 + Path string 33 + DataKey string 34 + Settings SecretSettings 35 + } 36 + 37 + func LoadConfig(path string) (*Config, error) { 38 + data, err := os.ReadFile(path) 39 + if err != nil { 40 + return nil, fmt.Errorf("read file: %w", err) 41 + } 42 + 43 + var config Config 44 + if err := yaml.Unmarshal(data, &config); err != nil { 45 + return nil, fmt.Errorf("parse YAML: %w", err) 46 + } 47 + 48 + return &config, nil 49 + } 50 + 51 + func ParseAndValidate(config *Config) ([]Entry, error) { 52 + var entries []Entry 53 + 54 + paths := make([]string, 0, len(config.Secrets)) 55 + for path := range config.Secrets { 56 + paths = append(paths, path) 57 + } 58 + sort.Strings(paths) 59 + 60 + for _, path := range paths { 61 + keys := config.Secrets[path] 62 + 63 + dataKeys := make([]string, 0, len(keys)) 64 + for k := range keys { 65 + dataKeys = append(dataKeys, k) 66 + } 67 + sort.Strings(dataKeys) 68 + 69 + for _, dataKey := range dataKeys { 70 + settings := keys[dataKey] 71 + if err := validateSettings(path, dataKey, settings); err != nil { 72 + return nil, err 73 + } 74 + 75 + entries = append(entries, Entry{ 76 + Path: path, 77 + DataKey: dataKey, 78 + Settings: settings, 79 + }) 80 + } 81 + } 82 + 83 + return entries, nil 84 + } 85 + 86 + func validateSettings(path, dataKey string, settings SecretSettings) error { 87 + switch settings.Type { 88 + case "random": 89 + if settings.Length < 0 { 90 + return fmt.Errorf("%s#%s: length must be >= 0", path, dataKey) 91 + } 92 + case "ssh": 93 + // valid 94 + case "manual": 95 + // valid 96 + case "": 97 + return fmt.Errorf("%s#%s: type is required", path, dataKey) 98 + default: 99 + return fmt.Errorf("%s#%s: unknown type %q", path, dataKey, settings.Type) 100 + } 101 + return nil 102 + }
+156
toolbox/internal/secrets/generator.go
··· 1 + package secrets 2 + 3 + import ( 4 + "crypto/ed25519" 5 + "crypto/rand" 6 + "crypto/rsa" 7 + "crypto/x509" 8 + "encoding/pem" 9 + "fmt" 10 + "strings" 11 + 12 + "github.com/charmbracelet/log" 13 + "golang.org/x/crypto/ssh" 14 + ) 15 + 16 + const ( 17 + rsaKeyBits = 4096 18 + defaultKeyLength = 32 19 + ) 20 + 21 + type Prompter interface { 22 + PromptSecret(description string) (string, error) 23 + } 24 + 25 + type Generator struct { 26 + prompter Prompter 27 + } 28 + 29 + func NewGenerator(prompter Prompter) *Generator { 30 + return &Generator{prompter: prompter} 31 + } 32 + 33 + func (g *Generator) Generate(e Entry) (map[string]interface{}, error) { 34 + switch e.Settings.Type { 35 + case "random": 36 + length := e.Settings.Length 37 + if length == 0 { 38 + length = defaultKeyLength 39 + } 40 + value, err := generateRandomString(length) 41 + if err != nil { 42 + return nil, fmt.Errorf("generate random string: %w", err) 43 + } 44 + log.Info("generated random secret", "path", e.Path, "key", e.DataKey) 45 + return map[string]interface{}{e.DataKey: value}, nil 46 + 47 + case "ssh": 48 + algorithm := e.Settings.Algorithm 49 + if algorithm == "" { 50 + algorithm = "ed25519" 51 + } 52 + 53 + privateKey, publicKey, err := generateSSHKeypair(algorithm) 54 + if err != nil { 55 + return nil, fmt.Errorf("generate SSH keypair: %w", err) 56 + } 57 + 58 + pubKeyName := e.Settings.PublicKey 59 + if pubKeyName == "" { 60 + pubKeyName = e.DataKey + ".pub" 61 + } 62 + 63 + log.Info("generated SSH keypair", "algorithm", algorithm, "path", e.Path, "key", e.DataKey) 64 + return map[string]interface{}{ 65 + e.DataKey: privateKey, 66 + pubKeyName: publicKey, 67 + }, nil 68 + 69 + case "manual": 70 + if g.prompter == nil { 71 + return nil, fmt.Errorf("manual secret type requires a prompter") 72 + } 73 + 74 + description := e.Settings.Description 75 + if description == "" { 76 + description = fmt.Sprintf("Enter value for %s#%s", e.Path, e.DataKey) 77 + } 78 + value, err := g.prompter.PromptSecret(description) 79 + if err != nil { 80 + return nil, fmt.Errorf("prompt for secret: %w", err) 81 + } 82 + log.Info("stored manual secret", "path", e.Path, "key", e.DataKey) 83 + return map[string]interface{}{e.DataKey: value}, nil 84 + 85 + default: 86 + return nil, fmt.Errorf("unknown secret type: %s", e.Settings.Type) 87 + } 88 + } 89 + 90 + func generateRandomString(length int) (string, error) { 91 + const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 92 + if length <= 0 { 93 + return "", fmt.Errorf("length must be positive") 94 + } 95 + result := make([]byte, length) 96 + randomBytes := make([]byte, length) 97 + 98 + if _, err := rand.Read(randomBytes); err != nil { 99 + return "", err 100 + } 101 + 102 + for i, b := range randomBytes { 103 + result[i] = charset[int(b)%len(charset)] 104 + } 105 + 106 + return string(result), nil 107 + } 108 + 109 + func generateSSHKeypair(algorithm string) (privateKeyPEM, publicKeyOpenSSH string, err error) { 110 + switch algorithm { 111 + case "ed25519": 112 + pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) 113 + if err != nil { 114 + return "", "", fmt.Errorf("generate key: %w", err) 115 + } 116 + privKeyBytes, err := x509.MarshalPKCS8PrivateKey(privKey) 117 + if err != nil { 118 + return "", "", fmt.Errorf("marshal private key: %w", err) 119 + } 120 + privPEM := pem.EncodeToMemory(&pem.Block{ 121 + Type: "PRIVATE KEY", 122 + Bytes: privKeyBytes, 123 + }) 124 + pubKeyStr, err := marshalSSHPublicKey(pubKey) 125 + if err != nil { 126 + return "", "", err 127 + } 128 + return string(privPEM), pubKeyStr, nil 129 + 130 + case "rsa": 131 + privKey, err := rsa.GenerateKey(rand.Reader, rsaKeyBits) 132 + if err != nil { 133 + return "", "", fmt.Errorf("generate key: %w", err) 134 + } 135 + privPEM := pem.EncodeToMemory(&pem.Block{ 136 + Type: "RSA PRIVATE KEY", 137 + Bytes: x509.MarshalPKCS1PrivateKey(privKey), 138 + }) 139 + pubKeyStr, err := marshalSSHPublicKey(&privKey.PublicKey) 140 + if err != nil { 141 + return "", "", err 142 + } 143 + return string(privPEM), pubKeyStr, nil 144 + 145 + default: 146 + return "", "", fmt.Errorf("unsupported algorithm: %s", algorithm) 147 + } 148 + } 149 + 150 + func marshalSSHPublicKey(key interface{}) (string, error) { 151 + sshPubKey, err := ssh.NewPublicKey(key) 152 + if err != nil { 153 + return "", fmt.Errorf("convert to SSH public key: %w", err) 154 + } 155 + return strings.TrimSpace(string(ssh.MarshalAuthorizedKey(sshPubKey))), nil 156 + }
+28
toolbox/internal/secrets/prompt.go
··· 1 + package secrets 2 + 3 + import ( 4 + "fmt" 5 + 6 + "github.com/charmbracelet/huh" 7 + ) 8 + 9 + type HuhPrompter struct{} 10 + 11 + func (HuhPrompter) PromptSecret(description string) (string, error) { 12 + var value string 13 + 14 + err := huh.NewInput(). 15 + Title(description). 16 + EchoMode(huh.EchoModePassword). 17 + Value(&value). 18 + Run() 19 + if err != nil { 20 + return "", fmt.Errorf("prompt: %w", err) 21 + } 22 + 23 + if value == "" { 24 + return "", fmt.Errorf("no input provided") 25 + } 26 + 27 + return value, nil 28 + }
+45
toolbox/internal/secrets/service.go
··· 1 + package secrets 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + 7 + "github.com/hashicorp/vault/api" 8 + ) 9 + 10 + type Service struct { 11 + store *Store 12 + generator *Generator 13 + } 14 + 15 + func NewService(vault *api.Client, prompter Prompter) *Service { 16 + return &Service{ 17 + store: NewStore(vault), 18 + generator: NewGenerator(prompter), 19 + } 20 + } 21 + 22 + func (s *Service) Run(ctx context.Context, entries []Entry) error { 23 + var autoEntries, manualEntries []Entry 24 + for _, e := range entries { 25 + if e.Settings.Type == "manual" { 26 + manualEntries = append(manualEntries, e) 27 + } else { 28 + autoEntries = append(autoEntries, e) 29 + } 30 + } 31 + 32 + for _, e := range autoEntries { 33 + if err := s.store.Process(ctx, e, s.generator); err != nil { 34 + return fmt.Errorf("process secret %s#%s: %w", e.Path, e.DataKey, err) 35 + } 36 + } 37 + 38 + for _, e := range manualEntries { 39 + if err := s.store.Process(ctx, e, s.generator); err != nil { 40 + return fmt.Errorf("process secret %s#%s: %w", e.Path, e.DataKey, err) 41 + } 42 + } 43 + 44 + return nil 45 + }
+82
toolbox/internal/secrets/store.go
··· 1 + package secrets 2 + 3 + import ( 4 + "context" 5 + "errors" 6 + "fmt" 7 + "strings" 8 + 9 + "github.com/charmbracelet/log" 10 + "github.com/hashicorp/vault/api" 11 + ) 12 + 13 + type Store struct { 14 + vault *api.Client 15 + } 16 + 17 + func NewStore(vault *api.Client) *Store { 18 + return &Store{vault: vault} 19 + } 20 + 21 + func (s *Store) Process(ctx context.Context, e Entry, generator *Generator) error { 22 + mount, path, err := parsePath(e.Path) 23 + if err != nil { 24 + return err 25 + } 26 + 27 + existing, err := s.vault.KVv2(mount).Get(ctx, path) 28 + if err != nil && !errors.Is(err, api.ErrSecretNotFound) { 29 + return fmt.Errorf("read existing secret: %w", err) 30 + } 31 + 32 + data := make(map[string]interface{}) 33 + if existing != nil && existing.Data != nil { 34 + for k, v := range existing.Data { 35 + data[k] = v 36 + } 37 + } 38 + 39 + keysToCheck := []string{e.DataKey} 40 + if e.Settings.Type == "ssh" { 41 + pubKey := e.Settings.PublicKey 42 + if pubKey == "" { 43 + pubKey = e.DataKey + ".pub" 44 + } 45 + keysToCheck = append(keysToCheck, pubKey) 46 + } 47 + 48 + allExist := true 49 + for _, k := range keysToCheck { 50 + if _, exists := data[k]; !exists { 51 + allExist = false 52 + break 53 + } 54 + } 55 + if allExist { 56 + log.Info("secret already exists, skipping", "path", e.Path, "key", e.DataKey) 57 + return nil 58 + } 59 + 60 + newData, err := generator.Generate(e) 61 + if err != nil { 62 + return err 63 + } 64 + 65 + for k, v := range newData { 66 + data[k] = v 67 + } 68 + 69 + if _, err := s.vault.KVv2(mount).Put(ctx, path, data); err != nil { 70 + return fmt.Errorf("write to Vault: %w", err) 71 + } 72 + 73 + return nil 74 + } 75 + 76 + func parsePath(fullPath string) (mount, path string, err error) { 77 + parts := strings.SplitN(fullPath, "/", 2) 78 + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { 79 + return "", "", fmt.Errorf("invalid path %q: expected format mount/path", fullPath) 80 + } 81 + return parts[0], parts[1], nil 82 + }