Files
unkinben 123faf8bbf
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline failed
ci/woodpecker/pr/pre-commit Pipeline was successful
Add vctl and vctx Vault token CLIs
Introduces the vault-tools monorepo: two Go CLIs that share a config file
(~/.config/vault) and token cache (~/.cache/vault) for working with multiple
Vault instances (contexts).

- add shared/ library: config parsing (vctl.yaml/config.yaml, per-context
  overrides, slash contexts), token cache (0600/0700, atomic writes, path-
  traversal guards), and a small hand-rolled Vault HTTP client (login/renew)
- add vctl: login/renew (single or --all), list, --method/--user overrides,
  no-echo password/token prompts, dynamic context completion
- add vctx: resolve a context, set VAULT_ADDR/VAULT_TOKEN/VAULT_NAMESPACE and
  exec the vault CLI, passing remaining args through untouched
- add unit tests across shared/, vctl and vctx command layers (config
  resolution, cache paths, vault client, --all iteration + error aggregation,
  vctx arg pass-through and env construction via fakeable exec/prompt seams)
- add Makefile (build/test/completions/rpm, patch|minor|major version bumps),
  nfpm RPM packaging bundling bash/zsh/fish completions for both binaries
- add Woodpecker pipelines: build/test/pre-commit on PRs, and a tag release
  that cross-compiles, builds+uploads the RPM to artifactapi, and cuts a Gitea
  release (serviceAccountName default, k8s resources on every step)
- add README, per-command docs (docs/vctl.md, docs/vctx.md), AGENTS.md and an
  example config

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-26 23:28:05 +10:00

141 lines
4.3 KiB
Markdown

# vctl
`vctl` manages Vault tokens for multiple vault instances ("contexts"). It logs
in to (or renews tokens for) one or all configured contexts and caches each
resulting token under `~/.cache/vault/<context>` for use by
[`vctx`](vctx.md) and other tooling.
## Synopsis
```
vctl login <context> # log in to one context
vctl login --all # log in to every configured context
vctl renew <context> # renew one context's cached token
vctl renew --all # renew every context that has a cached token
vctl list # show contexts + cached-token status
vctl version
```
## Flags
Both `login` and `renew` accept:
| Flag | Default | Description |
| -------------- | -------------------------------- | -------------------------------------------------- |
| `--all` | `false` | Operate on every configured context. |
| `--method <m>` | context/config, else `ldap` | Override the auth method (`ldap`, `userpass`, `okta`, `radius`, `token`). |
| `--user <u>` | context/config, else `$USER` | Override the login username. |
A context name or `--all` is required (but not both).
## Configuration
`vctl` reads the first of these files that exists:
1. `~/.config/vault/vctl.yaml`
2. `~/.config/vault/config.yaml`
(`$XDG_CONFIG_HOME` is honoured in place of `~/.config`.)
```yaml
# ~/.config/vault/vctl.yaml
defaults:
method: ldap # applied to any context that doesn't set its own
user: ben
namespace: ""
contexts:
sydney:
address: https://vault.syd1.au.unkin.net
staging/sydney: # slashes are allowed; they nest on disk
address: https://vault-staging.syd1.au.unkin.net
namespace: staging
user: svc-ben # per-context override
legacy:
address: https://vault-legacy.example.net
method: userpass
path: userpass2 # override the auth mount path (default = method)
```
### Field resolution
For each context, every field is resolved with the chain
**context value → file `defaults` → built-in default**:
- `method` — built-in default `ldap`.
- `user` — built-in default `$USER`.
- `namespace` — no built-in default (unset means the root namespace).
- `path` — the auth mount path; defaults to the resolved `method`.
`--method` / `--user` on the command line override the resolved values. When
`--method` changes the method and the context did not pin an explicit `path`,
the auth path follows the new method.
## Auth methods
- **Password methods** (`ldap`, `userpass`, `okta`, `radius`): `vctl` prompts
for a password (no echo) and POSTs to `auth/<path>/login/<user>`.
- **`token`**: `vctl` prompts for a raw Vault token (no echo), verifies it with
`auth/token/lookup-self`, and caches its details.
## Token cache
Tokens are written to `~/.cache/vault/<context>` (honouring `$XDG_CACHE_HOME`)
as JSON. Slash contexts nest: `staging/sydney`
`~/.cache/vault/staging/sydney`. Parent directories are created `0700` and token
files are written `0600`.
Each file stores enough to inspect and later revoke the token:
```json
{
"context": "staging/sydney",
"address": "https://vault-staging.syd1.au.unkin.net",
"namespace": "staging",
"token": "s....",
"accessor": "hmac-...",
"policies": ["default", "kv-read"],
"renewable": true,
"lease_duration_seconds": 3600,
"issued_at": "2026-07-26T12:00:00Z",
"expires_at": "2026-07-26T13:00:00Z"
}
```
The `accessor` lets you revoke the token later without exposing the secret
(`vault token revoke -accessor <accessor>`).
## Examples
```bash
# Log in to one context (prompts for password)
vctl login sydney
# Log in everywhere at once
vctl login --all
# Log in to a context overriding the method + user
vctl login sydney --method okta --user someone.else
# Renew a slash-named context
vctl renew staging/sydney
# Renew everything that currently has a cached token
vctl renew --all
# See what is configured and which tokens are still valid
vctl list
```
## Shell completion
```bash
vctl completion bash > /etc/bash_completion.d/vctl
vctl completion zsh > ~/.zsh/completions/_vctl
```
Context names complete dynamically from the config file (the RPM installs these
completions automatically).