Skip to content

WIF-48: credential Provider in delegatedauth + DELA directive discovery - #55479

Draft
wynbennett wants to merge 16 commits into
mainfrom
wyn.bennett/wif-credential-provider-foundation
Draft

WIF-48: credential Provider in delegatedauth + DELA directive discovery#55479
wynbennett wants to merge 16 commits into
mainfrom
wyn.bennett/wif-credential-provider-foundation

Conversation

@wynbennett

@wynbennett wynbennett commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds the foundation for delegated authentication (WIF-48): a credential Provider interface on the delegatedauth component and a DELA directive discovery mechanism that scans additional_endpoints config keys for DELA(...) directives and registers a credential provider for each one.

What is a DELA directive?

A DELA directive is a string of the form DELA(<org_uuid>, aws) or DELA(<org_uuid>, aws, region=us-east-1, fallback=ENC[...]) that an operator places in an additional_endpoints api_key field instead of a static API key. The agent exchanges a cloud auth proof (e.g. an AWS STS token) for a Datadog API key at the target site, and hands the resolved credential to consumers through the Provider interface. This keeps the resolved key out of the config tree entirely — the directive stays in place and the credential flows through the provider registry.

Provider lifecycle

                 ┌──────────────────────────────────────────┐
start ──────────▶│ resolving        Authorize → false        │  caller buffers
                 └───────────┬──────────────────┬───────────┘
         exchange succeeded   │                  │  exchange failed
                             ▼                  ▼
        ┌────────────────────────────┐   ┌──────────────────────────────────┐
        │ resolved                   │   │ fallback configured?              │
        │ Authorize → true (real)    │   │  yes → Authorize → true (static)  │
        └────────────────────────────┘   │  no  → Authorize → false          │
                     ▲                   └──────────────┬───────────────────┘
                     └──────────────────────────────────┘
                            a later refresh succeeds

A fallback is only ever used after an attempt has actually failed. While the very first exchange is still in flight the provider reports "not yet", so callers hold their payloads instead of shipping them under a key the operator only meant as a safety net.

What this PR provides

  • Provider interface (def/component.go): Authorize(http.Header) bool — consumers call this on every request; returns false while resolving (buffer), true once a key is available (stamps the header).
  • instanceProvider (impl/provider.go): the concrete provider for one delegated-auth instance, managing the three-state lifecycle above using atomic swaps.
  • DELA directive discovery (pkg/config/setup/delegated_auth_endpoints.go): parses directives from both map-shape (additional_endpoints, apm_config.additional_endpoints) and list-shape (logs_config.additional_endpoints) config keys, resolves fallback=ENC[...] handles through the secrets backend, and registers an instance per directive with SkipConfigWriteback: true so the resolved key never enters the config tree.
  • Config model (pkg/config/model/delegated_auth.go): the DelaDirectivePrefix constant shared between setup and utils.
  • Refactoring: delegatedauth.go split — writeback.go and status.go extracted as separate files. Noop-impl, mock, and def updated for the new Provider interface.

Review findings addressed (commit 546adfc)

  1. (P1) Per-directive regions leak into shared initializationinitializeIfNeeded now always auto-detects; per-instance ProviderConfig applied separately by providerConfigForInstance.
  2. (P2) Provider replacement keeps old providerregisterProvider removes the old entry for the same directive before appending.
  3. (P2) Secret refresh does not update fallback key — documented as a known limitation with a TODO.

What this PR does NOT do

The three data-plane consumers (forwarder, trace writers, logs agent) are not yet wired to call ProvidersFor / ProviderForDirective on the send path. That wiring is in PR #55480.

Architecture doc

See Delegated Authentication in the Agent: Architecture and Path Forward for the full design, including the previous approach vs. the new Provider interface, locking, and future work.

Stacked PRs

PR1: #55479  foundation (this PR, base: main)
 │
 └──► PR2: #55480  consumers (base: this PR)
       │
       └──► PR3: #55481  otel wiring (base: PR2)

WIF-48

…covery

This PR adds a credential Provider interface to the delegatedauth component
and discovers DELA directives, handing them to consumers as providers.

Changes:
- Add credential Provider to delegatedauth (provider.go, provider_test.go)
- Extract writeback.go and status.go from delegatedauth.go
- Consolidate CredentialProvider redeclarations and document InstanceParams
- Add delegated_auth_endpoints.go for DELA directive discovery
- Wire delegated auth config in pkg/config/setup/config.go
- Add delegated_auth.go to pkg/config/model
- Update endpoints.go in pkg/config/utils
- Update noop-impl, mock, and def for new Provider interface

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 219c7a086f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Directive: directiveText,
TargetSite: destination,
FallbackAPIKey: resolveFallbackAPIKey(secretResolver, directive.params["fallback"], configKey),
SkipConfigWriteback: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wire the new providers into send paths

With SkipConfigWriteback: true, a successful exchange only updates the provider registry, but none of the three consumers registered above uses that registry: the default forwarder builds authorizers solely from APIKeys.Keys, trace appendEndpoints still copies the configured string into Endpoint.APIKey, and logs endpoints still use the configured api_key; a repository-wide search finds no production call to ProvidersFor or ProviderForDirective. Consequently, an additional_endpoints directive leaves the infrastructure endpoint with zero authorizers, while the APM and logs paths send the literal DELA(...) value, even after the real key resolves. Wire provider lookup into all three send paths before suppressing writeback, or retain the existing writeback delivery.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is by design — this PR is the foundation layer only. The three consumers (forwarder, trace writers, logs agent) are wired to call ProvidersFor / ProviderForDirective on the send path in PR #55480. SkipConfigWriteback: true is set only for additional_endpoints directives, where the resolved credential reaches the consumer through the Provider registry rather than the config tree. Without PR #55480 the providers are registered but not yet consumed.

Comment thread pkg/config/setup/delegated_auth_endpoints.go
@wynbennett
wynbennett marked this pull request as draft August 26, 2026 19:56

@datadog-prod-us1-6 datadog-prod-us1-6 Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datadog Autotest: FAIL

The change can use the wrong AWS region for one directive. It can also keep an old provider or an old fallback key.

Open Bits AI session

🤖 Datadog Autotest · Commit 219c7a0 · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

Comment thread pkg/config/setup/delegated_auth_endpoints.go
Comment thread comp/core/delegatedauth/impl/delegatedauth.go Outdated
if err != nil {
return "", err
}
resolved, err := secretResolver.Resolve(wrapped, origin, "", "", false)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Secret refresh does not update the fallback key

The Agent can keep an expired fallback key and stop sending data when delegated authentication is unavailable.

Assertion details
  • Input: A DELA directive uses fallback=ENC[...] and secret refresh changes that secret.
  • Expected: A secret refresh must update the instance fallback key and its credential provider.
  • Actual: The one-time resolution records the secret under a synthetic origin. The existing configuration subscriber ignores updates for that origin. The instance keeps the first fallback key after the secret changes.

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented as a known limitation with a TODO on resolveFallbackAPIKey in commit 546adfc. The resolved value is a snapshot at config-load time. A proper fix requires passing the raw ENC[...] handle and the secrets resolver to the component so it can re-resolve on secret refresh, or re-running discovery when the secrets subscriber fires. This is tracked for a follow-up.

@dd-octo-sts

dd-octo-sts Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Go Package Import Differences

Baseline: d725d90
Comparison: cfd8b79

binaryosarchchange
agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
agentlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
agentwindowsamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
agentdarwinamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
agentdarwinarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
agentaixppc64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
iot-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
iot-agentlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
heroku-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
cluster-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
cluster-agentlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
cluster-agent-cloudfoundrylinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
cluster-agent-cloudfoundrylinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
dogstatsdlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
dogstatsdlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
process-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
process-agentlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
process-agentwindowsamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
process-agentdarwinamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
process-agentdarwinarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
heroku-process-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
security-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
security-agentlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
security-agentwindowsamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
sbomgenlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
sbomgenlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
system-probelinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
system-probelinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
system-probewindowsamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
system-probedarwinamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
system-probedarwinarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
trace-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
trace-agentlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
trace-agentwindowsamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
trace-agentdarwinamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
trace-agentdarwinarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
trace-agentaixppc64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
heroku-trace-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
otel-agentlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
otel-agentlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
host-profilerlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
host-profilerlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
loaderlinuxamd64
+56, -0
+compress/flate
+compress/gzip
+crypto
+crypto/aes
+crypto/cipher
+crypto/des
+crypto/dsa
+crypto/ecdh
+crypto/ecdsa
+crypto/ed25519
+crypto/elliptic
+crypto/fips140
+crypto/hkdf
+crypto/hmac
+crypto/hpke
+crypto/md5
+crypto/mlkem
+crypto/rand
+crypto/rc4
+crypto/rsa
+crypto/sha1
+crypto/sha256
+crypto/sha3
+crypto/sha512
+crypto/subtle
+crypto/tls
+crypto/x509
+crypto/x509/pkix
+encoding/asn1
+encoding/hex
+encoding/pem
+github.com/DataDog/datadog-agent/pkg/credential
+hash/crc32
+math/big
+math/rand
+math/rand/v2
+mime
+mime/multipart
+mime/quotedprintable
+net/http
+net/http/httptrace
+net/http/internal
+net/textproto
+vendor/golang.org/x/crypto/chacha20
+vendor/golang.org/x/crypto/chacha20poly1305
+vendor/golang.org/x/crypto/cryptobyte
+vendor/golang.org/x/crypto/cryptobyte/asn1
+vendor/golang.org/x/net/http/httpguts
+vendor/golang.org/x/net/http/httpproxy
+vendor/golang.org/x/net/http2/hpack
+vendor/golang.org/x/net/idna
+vendor/golang.org/x/sys/cpu
+vendor/golang.org/x/text/secure/bidirule
+vendor/golang.org/x/text/transform
+vendor/golang.org/x/text/unicode/bidi
+vendor/golang.org/x/text/unicode/norm
loaderlinuxarm64
+55, -0
+compress/flate
+compress/gzip
+crypto
+crypto/aes
+crypto/cipher
+crypto/des
+crypto/dsa
+crypto/ecdh
+crypto/ecdsa
+crypto/ed25519
+crypto/elliptic
+crypto/fips140
+crypto/hkdf
+crypto/hmac
+crypto/hpke
+crypto/md5
+crypto/mlkem
+crypto/rand
+crypto/rc4
+crypto/rsa
+crypto/sha1
+crypto/sha256
+crypto/sha3
+crypto/sha512
+crypto/subtle
+crypto/tls
+crypto/x509
+crypto/x509/pkix
+encoding/asn1
+encoding/hex
+encoding/pem
+github.com/DataDog/datadog-agent/pkg/credential
+hash/crc32
+math/big
+math/rand
+math/rand/v2
+mime
+mime/multipart
+mime/quotedprintable
+net/http
+net/http/httptrace
+net/http/internal
+net/textproto
+vendor/golang.org/x/crypto/chacha20
+vendor/golang.org/x/crypto/chacha20poly1305
+vendor/golang.org/x/crypto/cryptobyte
+vendor/golang.org/x/crypto/cryptobyte/asn1
+vendor/golang.org/x/net/http/httpguts
+vendor/golang.org/x/net/http/httpproxy
+vendor/golang.org/x/net/http2/hpack
+vendor/golang.org/x/net/idna
+vendor/golang.org/x/text/secure/bidirule
+vendor/golang.org/x/text/transform
+vendor/golang.org/x/text/unicode/bidi
+vendor/golang.org/x/text/unicode/norm
loaderdarwinamd64
+56, -0
+compress/flate
+compress/gzip
+crypto
+crypto/aes
+crypto/cipher
+crypto/des
+crypto/dsa
+crypto/ecdh
+crypto/ecdsa
+crypto/ed25519
+crypto/elliptic
+crypto/fips140
+crypto/hkdf
+crypto/hmac
+crypto/hpke
+crypto/md5
+crypto/mlkem
+crypto/rand
+crypto/rc4
+crypto/rsa
+crypto/sha1
+crypto/sha256
+crypto/sha3
+crypto/sha512
+crypto/subtle
+crypto/tls
+crypto/x509
+crypto/x509/pkix
+encoding/asn1
+encoding/hex
+encoding/pem
+github.com/DataDog/datadog-agent/pkg/credential
+hash/crc32
+math/big
+math/rand
+math/rand/v2
+mime
+mime/multipart
+mime/quotedprintable
+net/http
+net/http/httptrace
+net/http/internal
+net/textproto
+vendor/golang.org/x/crypto/chacha20
+vendor/golang.org/x/crypto/chacha20poly1305
+vendor/golang.org/x/crypto/cryptobyte
+vendor/golang.org/x/crypto/cryptobyte/asn1
+vendor/golang.org/x/net/http/httpguts
+vendor/golang.org/x/net/http/httpproxy
+vendor/golang.org/x/net/http2/hpack
+vendor/golang.org/x/net/idna
+vendor/golang.org/x/sys/cpu
+vendor/golang.org/x/text/secure/bidirule
+vendor/golang.org/x/text/transform
+vendor/golang.org/x/text/unicode/bidi
+vendor/golang.org/x/text/unicode/norm
loaderdarwinarm64
+55, -0
+compress/flate
+compress/gzip
+crypto
+crypto/aes
+crypto/cipher
+crypto/des
+crypto/dsa
+crypto/ecdh
+crypto/ecdsa
+crypto/ed25519
+crypto/elliptic
+crypto/fips140
+crypto/hkdf
+crypto/hmac
+crypto/hpke
+crypto/md5
+crypto/mlkem
+crypto/rand
+crypto/rc4
+crypto/rsa
+crypto/sha1
+crypto/sha256
+crypto/sha3
+crypto/sha512
+crypto/subtle
+crypto/tls
+crypto/x509
+crypto/x509/pkix
+encoding/asn1
+encoding/hex
+encoding/pem
+github.com/DataDog/datadog-agent/pkg/credential
+hash/crc32
+math/big
+math/rand
+math/rand/v2
+mime
+mime/multipart
+mime/quotedprintable
+net/http
+net/http/httptrace
+net/http/internal
+net/textproto
+vendor/golang.org/x/crypto/chacha20
+vendor/golang.org/x/crypto/chacha20poly1305
+vendor/golang.org/x/crypto/cryptobyte
+vendor/golang.org/x/crypto/cryptobyte/asn1
+vendor/golang.org/x/net/http/httpguts
+vendor/golang.org/x/net/http/httpproxy
+vendor/golang.org/x/net/http2/hpack
+vendor/golang.org/x/net/idna
+vendor/golang.org/x/text/secure/bidirule
+vendor/golang.org/x/text/transform
+vendor/golang.org/x/text/unicode/bidi
+vendor/golang.org/x/text/unicode/norm
installerlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
installerlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
installerwindowsamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
privateactionrunnerlinuxamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
privateactionrunnerlinuxarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
privateactionrunnerwindowsamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
privateactionrunnerdarwinamd64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential
privateactionrunnerdarwinarm64
+1, -0
+github.com/DataDog/datadog-agent/pkg/credential

@datadog-prod-us1-6

datadog-prod-us1-6 Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🎯 Code Coverage (details)
Patch Coverage: 62.74%
Overall Coverage: 51.21% (-3.83%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: cfd8b79 | Docs | View more details | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Files inventory check summary

File checks results against ancestor c35c49a9:

Results for datadog-agent_7.84.0~devel.git.592.bb228e6.pipeline.134336743-1_amd64.deb:

Detected file changes:

1 Changed files:

  • opt/datadog-agent/embedded/bin/trace-loader:
    • Size changed: +24.26% (1.16 MiB) (4.77 MiB -> 5.93 MiB)

Results for datadog-iot-agent_7.84.0~devel.git.592.bb228e6.pipeline.134336743-1_amd64.deb:

No change detected

@dd-octo-sts

dd-octo-sts Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Static quality checks

❌ Please find below the results from static quality gates
Comparison made with ancestor d725d90
📊 Static Quality Gates Dashboard
🔗 SQG Job
SOME SIZE DELTAS ARE N/A (ANCESTOR METRICS NOT YET AVAILABLE). RETRY JOB

Error

Quality gate Change Size (prev → curr → max)
agent_rpm_arm64 (on disk) N/A N/A → 739.402 → 738.950
agent_rpm_arm64_fips (on disk) N/A N/A → 695.012 → 694.790
agent_suse_arm64 (on disk) N/A N/A → 739.402 → 738.950
agent_suse_arm64_fips (on disk) N/A N/A → 695.012 → 694.790
docker_agent_amd64 (on disk) N/A N/A → 822.719 → 822.100
docker_agent_arm64 (on disk) N/A N/A → 823.429 → 823.020
docker_agent_jmx_amd64 (on disk) N/A N/A → 1013.631 → 1013.020
docker_agent_jmx_arm64 (on disk) N/A N/A → 1003.121 → 1002.710
Gate failure full details
Quality gate Error type Error message
agent_rpm_arm64 AbsoluteLimitExceeded static_quality_gate_agent_rpm_arm64 failed!
Disk size 739.4 MB exceeds limit of 738.9 MB by 462.7 KB
agent_rpm_arm64_fips AbsoluteLimitExceeded static_quality_gate_agent_rpm_arm64_fips failed!
Disk size 695.0 MB exceeds limit of 694.8 MB by 226.9 KB
agent_suse_arm64 AbsoluteLimitExceeded static_quality_gate_agent_suse_arm64 failed!
Disk size 739.4 MB exceeds limit of 738.9 MB by 462.7 KB
agent_suse_arm64_fips AbsoluteLimitExceeded static_quality_gate_agent_suse_arm64_fips failed!
Disk size 695.0 MB exceeds limit of 694.8 MB by 226.9 KB
docker_agent_amd64 AbsoluteLimitExceeded static_quality_gate_docker_agent_amd64 failed!
Disk size 822.7 MB exceeds limit of 822.1 MB by 633.5 KB
docker_agent_arm64 AbsoluteLimitExceeded static_quality_gate_docker_agent_arm64 failed!
Disk size 823.4 MB exceeds limit of 823.0 MB by 418.5 KB
docker_agent_jmx_amd64 AbsoluteLimitExceeded static_quality_gate_docker_agent_jmx_amd64 failed!
Disk size 1013.6 MB exceeds limit of 1013.0 MB by 625.7 KB
docker_agent_jmx_arm64 AbsoluteLimitExceeded static_quality_gate_docker_agent_jmx_arm64 failed!
Disk size 1003.1 MB exceeds limit of 1002.7 MB by 420.7 KB

Static quality gate failures prevent this PR from merging!
You can check the static quality gates runbooks page for guidance and tools. Please either fix the size violation or request an exception.

Successful checks

Info

Quality gate Change Size (prev → curr → max)
agent_deb_amd64 N/A N/A → 763.717 → 765.300
agent_deb_amd64_fips N/A N/A → 715.978 → 716.540
agent_heroku_amd64 N/A N/A → 313.576 → 319.540
agent_msi N/A N/A → 648.711 → 661.050
agent_rpm_amd64 N/A N/A → 763.701 → 765.270
agent_rpm_amd64_fips N/A N/A → 715.961 → 716.540
agent_suse_amd64 N/A N/A → 763.701 → 765.270
agent_suse_amd64_fips N/A N/A → 715.961 → 716.540
docker_cluster_agent_amd64 N/A N/A → 211.312 → 212.130
docker_cluster_agent_arm64 N/A N/A → 224.402 → 225.220
docker_cws_instrumentation_amd64 N/A N/A → 7.439 → 8.400
docker_cws_instrumentation_arm64 N/A N/A → 6.877 → 7.110
docker_dogstatsd_amd64 N/A N/A → 39.589 → 40.440
docker_dogstatsd_arm64 N/A N/A → 37.686 → 38.580
docker_host_profiler_amd64 N/A N/A → 306.937 → 317.700
docker_host_profiler_arm64 N/A N/A → 318.216 → 328.970
dogstatsd_deb_amd64 N/A N/A → 30.330 → 31.210
dogstatsd_deb_arm64 N/A N/A → 28.338 → 29.590
dogstatsd_rpm_amd64 N/A N/A → 30.330 → 31.210
dogstatsd_suse_amd64 N/A N/A → 30.330 → 31.210
iot_agent_deb_amd64 N/A N/A → 46.619 → 47.550
iot_agent_deb_arm64 N/A N/A → 43.248 → 44.220
iot_agent_deb_armhf N/A N/A → 44.058 → 45.020
iot_agent_rpm_amd64 N/A N/A → 46.619 → 47.550
iot_agent_suse_amd64 N/A N/A → 46.618 → 47.550

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 26, 2026

Copy link
Copy Markdown

Regression Detector

Regression Detector Results

Metrics dashboard
Target profiles
Job ID: f9c53dff-e7fa-4c5a-b9ef-1b95d9d6feb0

Baseline: d725d90
Comparison: cfd8b79
Diff

Optimization Goals: ✅ No significant changes detected

Fine details of change detection per experiment

perf experiment goal Δ mean % Δ mean % CI trials links
dsd_uds_10mb_3k_timestamped_contexts_memory memory utilization +0.83 [+0.62, +1.05] 1 Logs
quality_gate_metrics_logs memory utilization +0.79 [+0.56, +1.02] 1 Logs bounds checks dashboard
quality_gate_idle memory utilization +0.51 [+0.47, +0.55] 1 Logs bounds checks dashboard
quality_gate_security_no_fs_load memory utilization +0.38 [+0.29, +0.46] 1 Logs bounds checks dashboard
quality_gate_logs % cpu utilization +0.29 [-0.60, +1.17] 1 Logs bounds checks dashboard
quality_gate_idle_all_features memory utilization +0.25 [+0.22, +0.28] 1 Logs bounds checks dashboard
quality_gate_security_mean_fs_load memory utilization +0.04 [+0.01, +0.08] 1 Logs bounds checks dashboard
quality_gate_security_idle memory utilization +0.01 [-0.04, +0.06] 1 Logs bounds checks dashboard
quality_gate_private_action_runner memory utilization -0.19 [-0.30, -0.07] 1 Logs bounds checks dashboard
dsd_uds_10mb_3k_timestamped_contexts_cpu % cpu utilization -0.23 [-0.54, +0.07] 1 Logs

Bounds Checks: ✅ Passed

perf experiment bounds_check_name replicates_passed observed_value links
quality_gate_idle intake_connections 10/10 4 = 4 bounds checks dashboard
quality_gate_idle memory_usage 10/10 173.19MiB ≤ 179MiB bounds checks dashboard
quality_gate_idle total_bytes_received 10/10 748.40KiB ≤ 819.20KiB bounds checks dashboard
quality_gate_idle_all_features intake_connections 10/10 4 = 4 bounds checks dashboard
quality_gate_idle_all_features memory_usage 10/10 526.51MiB ≤ 537MiB bounds checks dashboard
quality_gate_idle_all_features total_bytes_received 10/10 1.14MiB ≤ 1.25MiB bounds checks dashboard
quality_gate_logs intake_connections 10/10 17 ≤ 40 bounds checks dashboard
quality_gate_logs memory_usage 10/10 211.83MiB ≤ 228MiB bounds checks dashboard
quality_gate_logs missed_bytes 10/10 0B = 0B bounds checks dashboard
quality_gate_logs total_bytes_received 10/10 263.54MiB ≤ 292MiB bounds checks dashboard
quality_gate_metrics_logs cpu_usage 10/10 366.06 ≤ 2000 bounds checks dashboard
quality_gate_metrics_logs intake_connections 10/10 21 ≤ 40 bounds checks dashboard
quality_gate_metrics_logs memory_usage 10/10 424.93MiB ≤ 455MiB bounds checks dashboard
quality_gate_metrics_logs missed_bytes 10/10 0B = 0B bounds checks dashboard
quality_gate_metrics_logs total_bytes_received 10/10 0.94GiB ≤ 1.04GiB bounds checks dashboard
quality_gate_private_action_runner memory_usage 10/10 72.73MiB ≤ 75MiB bounds checks dashboard
quality_gate_security_idle cpu_usage 10/10 29.39 ≤ 100 bounds checks dashboard
quality_gate_security_idle memory_usage 10/10 324.60MiB ≤ 355MiB bounds checks dashboard
quality_gate_security_mean_fs_load cpu_usage 10/10 70.12 ≤ 200 bounds checks dashboard
quality_gate_security_mean_fs_load memory_usage 10/10 302.75MiB ≤ 335MiB bounds checks dashboard
quality_gate_security_no_fs_load cpu_usage 10/10 22.30 ≤ 100 bounds checks dashboard
quality_gate_security_no_fs_load memory_usage 10/10 312.55MiB ≤ 345MiB bounds checks dashboard

Explanation

Confidence level: 90.00%
Effect size tolerance: |Δ mean %| ≥ 5.00%

Performance changes are noted in the perf column of each table:

  • ✅ = significantly better comparison variant performance
  • ❌ = significantly worse comparison variant performance
  • ➖ = no significant change in performance

A regression test is an A/B test of target performance in a repeatable rig, where "performance" is measured as "comparison variant minus baseline variant" for an optimization goal (e.g., ingress throughput). Due to intrinsic variability in measuring that goal, we can only estimate its mean value for each experiment; we report uncertainty in that value as a 90.00% confidence interval denoted "Δ mean % CI".

For each experiment, we decide whether a change in performance is a "regression" -- a change worth investigating further -- if all of the following criteria are true:

  1. Its estimated |Δ mean %| ≥ 5.00%, indicating the change is big enough to merit a closer look.

  2. Its 90.00% confidence interval "Δ mean % CI" does not contain zero, indicating that if our statistical model is accurate, there is at least a 90.00% chance there is a difference in performance between baseline and comparison variants.

  3. Its configuration does not mark it "erratic".

CI Pass/Fail Decision

Passed. All Quality Gates passed.

  • quality_gate_security_idle, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_security_idle, bounds check cpu_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_logs, bounds check intake_connections: 10/10 replicas passed. Gate passed.
  • quality_gate_logs, bounds check total_bytes_received: 10/10 replicas passed. Gate passed.
  • quality_gate_logs, bounds check missed_bytes: 10/10 replicas passed. Gate passed.
  • quality_gate_logs, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_security_no_fs_load, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_security_no_fs_load, bounds check cpu_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check missed_bytes: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check cpu_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check intake_connections: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check total_bytes_received: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_idle, bounds check intake_connections: 10/10 replicas passed. Gate passed.
  • quality_gate_idle, bounds check total_bytes_received: 10/10 replicas passed. Gate passed.
  • quality_gate_idle, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_private_action_runner, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_idle_all_features, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_idle_all_features, bounds check intake_connections: 10/10 replicas passed. Gate passed.
  • quality_gate_idle_all_features, bounds check total_bytes_received: 10/10 replicas passed. Gate passed.
  • quality_gate_security_mean_fs_load, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_security_mean_fs_load, bounds check cpu_usage: 10/10 replicas passed. Gate passed.

Fix three findings from the Codex/Autotest review:

1. (P1) Per-directive regions leak into shared initialization
   initializeIfNeeded could store the first directive's ProviderConfig
   (e.g. region=us-east-1) as the component-wide default. Later directives
   with no overrides would inherit that region instead of auto-detecting.
   Fix: always auto-detect in initializeIfNeeded; per-instance config is
   applied separately by providerConfigForInstance.

2. (P2) Provider replacement keeps old provider
   registerProvider appended without removing the old entry for the same
   directive. Lookup could return the cancelled provider.
   Fix: remove any existing entry for the same directive before appending.

3. (P2) Secret refresh does not update fallback key
   The fallback ENC[...] handle is resolved once at config-load time and
   never re-resolved. Documented as a known limitation with a TODO.
@wynbennett

Copy link
Copy Markdown
Contributor Author

Review findings addressed in commit 546adfc

Finding 1 (P1) — Per-directive regions leak into shared initialization
Fixed. initializeIfNeeded now always auto-detects the cloud provider and region from the environment. Per-instance ProviderConfig is applied separately by providerConfigForInstance, so the first directive no longer pins the component-wide default.

Finding 2 (P2) — Provider replacement keeps old provider
Fixed. registerProvider now removes any existing entry for the same directive before appending the new one, so ProvidersFor and ProviderForDirective cannot return a cancelled provider.

Finding 3 (P2) — Secret refresh does not update fallback key
Documented as a known limitation with a TODO on resolveFallbackAPIKey. The resolved value is a snapshot at config-load time; a proper fix requires passing the raw ENC[...] handle and the secrets resolver to the component so it can re-resolve on secret refresh, or re-running discovery when the secrets subscriber fires.

Finding 4 (P1) — Wire the new providers into send paths
This is by design — this PR is the foundation layer. The three consumers (forwarder, trace writers, logs agent) are wired in PR #55480, which calls ProvidersFor / ProviderForDirective on the send path. SkipConfigWriteback: true is only set for additional_endpoints directives, where the resolved credential reaches the consumer through the Provider registry rather than the config tree.

…nges

Add Refresh() bool to the Provider interface so the forwarder can nudge a
re-exchange on 403, matching the secrets.Refresh() contract. The implementation
resets the credential to buffering (Authorize returns false, transaction
requeues) and sends a non-blocking signal to a capacity-1 channel — a burst of
403s coalesces into a single refresh, no storm.

Add a credential cache keyed by (orgUUID, targetSite) so two DELA directives
for the same org against the same site share one instanceProvider and one
background refresh goroutine instead of each making a redundant WIF exchange.
Dedup applies only in directive mode (SkipConfigWriteback); the flat-key path
still needs one authInstance per config slot for write-back.

Update all Provider implementations (mock, noop, test stubs) with Refresh().

WIF-48
Consolidates the per-package stubProvider copies duplicated across the
resolver, logs, trace writer, and trace API tests into one shared test
helper in comp/core/delegatedauth/mock.

WIF-48
…import

_test.go files are only visible within their own package. Rename to
stub_provider.go so consumer test packages can import StubProvider from
delegatedauth/mock. Update BUILD.bazel via gazelle.

WIF-48
Create pkg/credential as the canonical home for the Provider interface,
IsDirective, StampAuth, and Lookup types. This eliminates duplication of
the CredentialProvider type and provider-resolution logic across 6
consumer subsystems.

- pkg/credential/provider.go: Provider interface (Authorize + Refresh)
- pkg/credential/directive.go: DirectivePrefix const, IsDirective func
- pkg/credential/authorize.go: StampAuth helper (provider-or-static-key)
- pkg/credential/lookup.go: Lookup func type
- comp/core/delegatedauth/def: Provider is now an alias for credential.Provider
- Add pkg/credential/go.mod and replace directives to all submodule go.mod files

WIF-48
Merge main into foundation branch. The merge conflicted on
comp/otelcol/ddflareextension/impl/go.mod where our branch added
pkg/credential as an indirect require and main added golang.org/x/sys;
both are kept.

Register pkg/credential in modules.yml so modules.add-all-replace
auto-detects it and generates correct relative replace paths in all
sub-module go.mod files (previously the replace directives were
manually added with incorrect relative paths).

WIF-48
Without this entry golangci-lint fails with:
  typechecking error: pattern ./pkg/credential/...: main module does not
  contain package github.com/DataDog/datadog-agent/pkg/credential

WIF-48
wynbennett and others added 5 commits August 31, 2026 10:35
bazel mod tidy removes this entry because local modules are resolved
via replace directives, not go_deps. No other datadog_agent_pkg_*
module appears in deps/go.MODULE.bazel.

WIF-48
Same owners as comp/core/delegatedauth.

WIF-48
Required because test/fakeintake/go.mod changed (pkg/credential replace directive).

WIF-48
@dd-octo-sts

dd-octo-sts Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Benchmarks

Benchmark execution time: 2026-09-01 22:00:39

Comparing candidate commit cfd8b79 in PR branch wyn.bennett/wif-credential-provider-foundation with baseline commit f94f9d6 in branch main.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 3 metrics, 0 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant