feat: record error stacks at external client boundaries

MI
2d ago
+899-207

Errors from external clients carry no stack, so Sentry gives them the stack of the capture site (gRPC interceptor, workers) and groups unrelated failures together. This change uses pkg/errorsx where those errors enter Chainloop, so Sentry groups them by where they start. It follows #3736, which covered database errors.

  • gRPC clients: pkg/grpcconn adds stack-recording unary and stream client interceptors. They are enabled by default on every connection built with grpcconn.New and are exported for connections built elsewhere. io.EOF from streams stays unwrapped, because callers compare it with ==.
  • Plugin HTTP client: the SDK HTTP client gets a transport that records a stack on each round-trip error. This covers the Dependency-Track, Discord, Slack and webhook integrations. GUAC and SMTP wrap their client errors directly.
  • Storage and secrets: the OCI, S3, S3 access point and Azure Blob backends, and the Vault, AWS, GCP and Azure Key Vault secret managers.
  • Other clients: NATS (connection, audit publisher, KV cache), the OIDC provider (discovery, code exchange, token verification), the EJBCA and file CAs, the policy provider and the remote policy loaders.
  • Separate error types for non-2xx responses in EJBCA, the policy provider, the remote policy loader and Dependency-Track, so Sentry keeps them apart from transport and decoding failures.

Package-level sentinel errors stay errors.New. Wraps of expected not-found results do not record a stack. errors.Is, errors.As and status.Code keep working through the wrapped errors.

AI disclosure: this change was produced with the assistance of Claude Code.

Closes https://github.com/chainloop-dev/chainloop/issues/3735