Read the standard token sink, and tell a 403 from an outage

NewFromEnv now falls back to ~/.vault-token, the file `bao login` writes and
the bao and vault CLIs already read. An operator who has logged in once
should not have to re-export a secret, and a token that never has to be
typed is a token that never lands in shell history.

Preflight distinguishes the two things a 403 means. An expired token and a
token missing a policy look identical in the error, and the check that
separates them -- whether `bao token lookup` also fails -- is worth naming
where it is read rather than left to be rediscovered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172sgCZEEDJcnQmr4SGDvKa

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1361245@bnt-lap001
Assistant-Session: b3b428ef-f3e6-4688-b091-01f71461d66a
This commit is contained in:
tegwick 2026-09-04 22:27:01 +02:00
parent 7347bd6302
commit de7a7bdd2a
2 changed files with 28 additions and 3 deletions

View file

@ -76,7 +76,13 @@ func cmdPreflight(args []string) error {
appFields, found, err := store.Get(ctx, secrets.KeyOperatorApp)
switch {
case err != nil:
bad("openbao unreachable or token rejected: %v", err)
if strings.Contains(err.Error(), "403") {
bad("openbao rejected the token (403). If `bao token lookup` also fails,\n"+
" the token has expired -- run `bao login`. If lookup succeeds,\n"+
" the token lacks a policy for %s", store.Ref(secrets.KeyOperatorApp))
} else {
bad("openbao unreachable: %v", err)
}
case !found:
bad("no app credentials at %s\n write them with:\n"+
" bao kv put %s api_id=<n> api_hash=<hash>\n"+