Skip to content

credstore: forced file-only construction and a cancellable keyring probe #55

Description

@jeremy

Context: the basecamp-cli installer hang (basecamp/basecamp-cli#568, fixed CLI-side in basecamp/basecamp-cli#578).

credstore.NewStore probes OS keyring availability eagerly with a write (keyring.Set). On macOS that shells out to /usr/bin/security -i via zalando/go-keyring (keyring_darwin.go:76 in v0.2.8), whose Wait() is uncancellable. On a locked keychain with no TTY or GUI, the probe blocks forever and the child process cannot be reclaimed — abandoning the goroutine would leak both.

basecamp-cli now defers credstore.NewStore to first credential use, which fixes every credential-free command, but credential-touching commands still block unbounded on a locked headless keychain (escape hatch: the DisableEnvVar). Making that bounded needs support at two layers here, since the CLI cannot do it soundly:

  1. Explicit file-only constructionStoreOptions.ForceFile (or Disabled), a programmatic equivalent of the disable env var. Today the only way to force file mode is to mutate the process environment before construction, which isn't a real contract (Store fields are private, so the CLI can't build a file-only store directly either). This is what a caller would fall back to after a probe timeout.

  2. A cancellable/bounded probe — either a context-aware operation upstream in go-keyring (the exec.Command process handle lives there, so only it can kill the hung security child), or a platform-specific probe owned by credstore that manages its own child process and can actually reclaim it on timeout. A timeout wrapper outside these layers can only abandon the goroutine: it hides the hang and keeps the leaked security child, which is why basecamp-cli deliberately didn't ship one.

With both in place, callers can do: bounded probe → on timeout, construct file-only with a warning. basecamp-cli would adopt that via a dependency bump.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions