Skip to content

credential: manage keystore credentials from the CLI #1218

Description

@lukehinds

Credential injection (nono run --credential <service> -- ...) reads secrets from the system keystore, but nono gives users no way to put secrets there. To set this up, a user currently has to use platform-specific tooling and figuring out the nono json policy schema:

  • macOS: security add-generic-password -s nono -a openai -w …
  • Linux: secret-tool store --label=nono service nono account openai

This issue is for more new users, then enterprise - who want to get going, yet could easily get things wrong. The most common failure is a service/account mismatch: the account you write with security/secret-tool has to be exactly the account the proxy reads for that service at run time, and nothing tells you what that account is. A built-in service might read from an env var or an external manager (1Password, Bitwarden) ather than the keystore at all - so a hand-written keystore entry is silently ignored, with no feedback about why injection "isn't working."

There's also no way to inspect the situation: which services exist, where each one's credential comes from, and whether it's actually available.

Matters would be helped with a nono credential command for storing and inspecting keystore credentials directly, so setup is:

nono credential set openai            # paste the key once (no echo)
nono run --credential openai -- claude

Subcommands:

  • set <service> — prompt securely (terminal echo disabled) and store the
    secret under the account the proxy actually reads for that service. Falls back
    to plain line-reading when stdin isn't a TTY, so it stays scriptable.
  • list — show built-in and (with --profile) profile custom services,
    their source (keystore / env / external manager / oauth2), and
    availability. --json supported.
  • rm <service> — remove a stored credential (with a confirmation prompt;
    -y to skip).
  • check <service> — verify a service's credential loads, without ever
    printing the value (exits non-zero if it can't).

Resolution: service names resolve against the same built-in network policy and profile custom credentials that --credential <service> uses, so the account written by set is the account injected at run time. Non-keystore sources are handled explicitly:

  • An env-backed service (e.g. anthropicenv://ANTHROPIC_API_KEY) declines set with guidance to export the variable, rather than writing an unused keystore entry. `--account forces a keystore entry when needed.
  • External-manager-backed services (1Password/Bitwarden/Apple Passwords/file) are surfaced with a redacted display and similarly declined.
  • OAuth2 client-credential services point you at storing each half under its own account.

Library/CLI boundary: the nono library gains the keystore primitives
(store_secret / delete_secret / secret_exists, gated on the system-keyring feature, alongside the existing load_secret_by_ref); all policy and UX live in the CLI.

Example list output:

SERVICE    ORIGIN    SOURCE                  STATUS
anthropic  built-in  env: ANTHROPIC_API_KEY  ✗ not set
gemini     built-in  keystore: gemini        ✓ available
github     built-in  env: GITHUB_TOKEN       ✓ available
openai     built-in  keystore: openai        ✗ not set

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions