- Go 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Release / changelog (push) Successful in 3s
Release / test (push) Successful in 1m10s
Release / ci (push) Successful in 0s
Release / plan (push) Successful in 4s
Release / image (push) Successful in 1m13s
Release / packages (push) Successful in 1m13s
Release / release (push) Successful in 3s
The agents' bao shell wrapper renewed the token file by mtime every seven days; with this it takes the token from baox, which renews by the TTL OpenBao reports. |
||
| .forgejo/workflows | ||
| internal | ||
| packaging | ||
| .gitignore | ||
| .ko.yaml | ||
| CHANGELOG.md | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| README.md | ||
baox
Run commands with credentials from OpenBao in their environment, act as a credential helper for kubectl, git, AWS and
an MCP headersHelper, and seal archives with transit keys.
Install
brew install mikluko/tap/baox
go install sarak.sh/mikluko/baox@latest
Releases also ship a container image, sarak.sh/mikluko/baox, and .deb packages for amd64 and arm64 in the
sarak.sh Debian registry (distribution trixie, component main).
OpenBao login
Every command gets its token the same way, first match wins:
BAO_TOKENin the environment.openbao.token_file: a token renewed when less than half its period (or creation TTL) remains.openbao.kubernetes: login through the kubernetes auth method with the pod's service account token.
The address is BAO_ADDR, else openbao.address. The other BAO_* client variables (BAO_CACERT and so on) apply.
baox token prints that token, renewed first when due, for the bao CLI and anything else that wants one:
BAO_TOKEN="$(baox token)" bao kv list -mount=kv k8s
Config file
--config, else $BAOX_CONFIG, else $XDG_CONFIG_HOME/baox/config.yaml or ~/.config/baox/config.yaml. A missing
file is an empty config. Unknown keys are errors.
openbao:
address: https://secrets.sarak.sh
token_file: ~/.config/openbao/agent-token
# kubernetes:
# mount: kubernetes/saraksh # default kubernetes
# role: agent
# jwt_file: ... # default the projected service account token
accounts:
mikluko:
kind: github
host: github.com # default github.com
owners: [mikluko]
mount: github
permission_set: agent-mikluko # token from <mount>/token/<permission_set>
sarak:
kind: forgejo
host: sarak.sh
owners: [mikluko]
role: forgejo # identity/oidc/token/<role>
prod:
kind: aws
account_id: "123456789012"
role: aws # identity token role
region: eu-central-1 # default us-east-1
clusters:
saraksh:
mount: k8s-saraksh
namespace: openbao
cluster_role_binding: true
ttl: 1h
seal:
mount: transit # default transit
encryption_key: agent-sessions
signing_key: agent-sessions-sign
github and forgejo accounts are picked by host and owner; a host and owner matching no account, or several, is an error.
Commands
run
baox run --env-file .env -- ./server
The child inherits baox's environment, overridden by each --env-file in turn. Any value, inherited or from a file,
of the form bao://<mount>/<path>#<key> is replaced by that field of the KV v2 secret. baox prints no value and exits
with the child's status. Env files take KEY=VALUE lines, # comments, an optional export prefix, and single or
double quotes.
kube
A kubeconfig user:
users:
- name: saraksh-view
user:
exec:
apiVersion: client.authentication.k8s.io/v1
command: baox
args: [kube, saraksh, view]
interactiveMode: Never
baox kube <cluster> <role> writes <mount>/creds/<role> and prints an ExecCredential whose expiry falls 10% of the
lease (at most 5 minutes) before the lease ends. Each call creates a new lease.
github and forgejo
git config --global credential.https://github.com.helper '!baox github git-credential'
git config --global credential.https://github.com.useHttpPath true
git config --global credential.https://sarak.sh.helper '!baox forgejo git-credential'
git config --global credential.https://sarak.sh.useHttpPath true
run sets GH_TOKEN (github) or FORGEJO_TOKEN (forgejo) for the child, choosing the account by --account, else
by the origin remote of the current directory:
baox github run -- gh pr list
baox forgejo run --account sarak -- fj issue list
The forgejo credential is an OpenBao identity token that Forgejo accepts through an Authorized Integration.
baox forgejo header <account> prints {"Authorization":"Bearer <token>"} for Claude Code's headersHelper:
{
"mcpServers": {
"forgejo": {
"type": "http",
"url": "https://example/mcp",
"headersHelper": "baox forgejo header sarak"
}
}
}
aws
[profile prod-admin]
credential_process = baox aws prod admin
baox aws <account> <role> exchanges an identity token for credentials of arn:aws:iam::<account_id>:role/<role>
through STS AssumeRoleWithWebIdentity.
seal and unseal
tar -c work | baox seal -o work.tar.sealed
baox unseal work.tar.sealed | tar -x
seal encrypts with a data key from transit and signs with an ed25519 transit key; the format is described in the
internal/seal package doc. unseal takes a file, not a pipe: it verifies the signature over the whole file before
transit decrypts the data key, and writes nothing if verification fails. Both stream in constant memory.
Prior art
- 1Password CLI:
op runresolvesop://references in the environment and env files the same waybaox runresolvesbao://ones.
Development
go test ./... runs a dev-mode OpenBao from the bao binary on PATH; without one those tests skip, unless
BAOX_REQUIRE_BAO is set.
Publishing the image and packages exchanges the workflow's OIDC token through an Authorized Integration of the owner's
with scope write:package; REGISTRY_AUDIENCE in release.yaml holds that integration's audience.