Skip to content

Make Credential Graph return typed objects instead of plain maps #704

@jakobmoellerdev

Description

@jakobmoellerdev

Description

This issue covers Phase 1 (Foundation) of the typed credentials work described in ADR 0017 — Typed Credentials and Consumer Identity Types.

The goal is to lay the foundation so that bindings can gradually migrate to typed credentials without any downstream breakage.

Problem

Most credentials can be fairly complex. OCIRegistry for example has several fields. These fields in OCM today are undocumented. Because they are undocumented they are hard to use.

Credentials are map[string]string — key names like username, password, accessToken are scattered string constants with no compile-time guarantees. A real bug exists where OCI resource downloads used access_token (snake_case) while docker config resolution used accessToken (camelCase), causing silent auth failures (ocm-project#985).

Consumer identity types are scattered strings — "OCIRegistry", "HelmChartRepository", "RSA/v1alpha1" defined independently per binding with no central registry and no way to validate identity ↔ credential compatibility.

Scope — Phase 1: Foundation

Add the foundational types and interfaces that enable typed credentials. No existing behavior changes, no downstream breakage — all existing code continues to work.

Specifically:

  1. Add ResolveTyped method to the existing Resolver interface — returns runtime.Typed instead of map[string]string. Adding a method to an interface breaks implementors (all in our codebase), not consumers. Each binding migrates from Resolve to ResolveTyped independently in later phases.

  2. Add CredentialTypeSchemeProvider and IdentityTypeSchemeProvider interfaces to the credential graph — two optional scheme providers passed via graph configuration (Options). The graph stores credentials as runtime.Typed internally and resolves typed credentials from config when a provider is configured. Both providers are nil-safe — the graph degrades to DirectCredentials behavior when no provider is configured.

type CredentialTypeSchemeProvider interface {
    Scheme() *runtime.Scheme
}

type IdentityTypeSchemeProvider interface {
    Scheme() *runtime.Scheme
}
  1. Add CredentialAcceptor interface to runtime — typed identity structs declare which credential types they accept. The graph validates during ingestion that configured credential types are compatible (warnings, not errors — plugins loaded after ingestion may introduce unknown types).
type CredentialAcceptor interface {
    AcceptedCredentialTypes() []runtime.Type
}
  1. Register DirectCredentials/v1 as the universal fallback — ensures .ocmconfig files with Credentials/v1 and properties continue to work unchanged.

Where before the flow was:

sequenceDiagram
    participant Runtime
    participant Graph
    participant Plugin

    Runtime->>Plugin: getConsumerIdentity()
    Plugin->>Runtime: runtime.Identity

    Runtime->>Graph: Resolve(runtime.Identity)
    Graph->>Runtime: map[string]string

    Runtime->>Plugin: Function(e.g. Get CV): function(..., credentials map[string]string)
Loading

after Phase 1 the graph additionally supports:

sequenceDiagram
    participant C as Consumer (e.g. Helm downloader)
    participant I as Typed Identity (HelmChartRepositoryIdentity)
    participant G as Credential Graph
    participant S as CredentialTypeSchemeProvider

    C->>I: construct typed identity
    I-->>C: .Identity() → runtime.Identity
    C->>G: ResolveTyped(identity)
    G->>G: match identity in DAG
    G->>G: resolve raw credential from config
    G->>S: Scheme() → convert to runtime.Typed
    S-->>G: typed credential
    G-->>C: HelmHTTPCredentials
    C->>C: type-assert fields (CertFile, KeyFile, Keyring)
Loading

Note that previous credentials of type Properties still work — DirectCredentials/v1 is the fallback. Consumers only switch to ResolveTyped when they are ready (Phase 2+).

Out of Scope

  • Binding migration to typed credentials (Phase 2 — separate issues per binding)
  • Plugin interface changes (Phase 3)
  • Repository interface changes (Phase 4)
  • Consumer migration (Phase 5)
  • Legacy cleanup / deprecation of Resolve (Phase 6)

Done Criteria

  • ResolveTyped method added to Resolver interface
  • CredentialTypeSchemeProvider and IdentityTypeSchemeProvider interfaces accepted by credential graph via Options
  • CredentialAcceptor interface added to runtime
  • DirectCredentials/v1 registered as universal fallback
  • Graph degrades gracefully when no scheme providers are configured (nil-safe)
  • Existing .ocmconfig files continue to work unchanged
  • Code has been reviewed by other team members
  • Analysis of existing tests (Unit and Integration)
  • Unit Tests created for new code or existing Unit Tests updated
  • Integration Test Suite updated (includes deletion of existing unnecessary Integration Test and/or creation of new ones if required)
  • Enduser Documentation updated (if applicable)
  • Internal technical Documentation created/updated (if applicable)
  • Successful demonstration in Review

Metadata

Metadata

Assignees

Labels

area/ipceiImportant Project of Common European Interestkind/tasksmall task, normally part of feature or epic

Type

No fields configured for Task.

Projects

Status
🍺 Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions