Skip to content

fix: Redact anonymous context attributes in custom events - #191

Merged
keelerm84 merged 2 commits into
mainfrom
mk/SDK-2731/redact-anonymous-custom-events
Jul 21, 2026
Merged

fix: Redact anonymous context attributes in custom events#191
keelerm84 merged 2 commits into
mainfrom
mk/SDK-2731/redact-anonymous-custom-events

Conversation

@keelerm84

@keelerm84 keelerm84 commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

Custom events routed the event context through the non-redacting format_context_for_event/2, so an anonymous context's attributes were emitted intact instead of being redacted into _meta.redactedAttributes. Feature events already used the anonymous-redacting formatter; this makes custom events match.

Changes

  • src/ldclient_event_process_server.erl: the <<"custom">> clause of format_event_set_context/4 now calls ldclient_context_filter:format_context_for_event_with_anonyous_redaction/2, mirroring the <<"feature">> clause. The redaction logic in ldclient_context_filter.erl already handles single- and multi-kind anonymous contexts correctly.
  • test/ldclient_events_SUITE.erl: added add_custom_events_redacts_anonymous_context covering single-kind (all attributes redacted) and multi-kind (only the anonymous kind redacted).

Testing

  • rebar3 ct --suite=test/ldclient_events_SUITE — all pass.
  • Verified against the sdk-test-harness v2 branch (incl. #386): events/custom events/single-kind anonymous context redacts all attributes and .../multi-kind ... now pass.

How it was found

sdk-test-harness PR #386 re-gated the custom-event redaction tests on the anonymous-redaction + inline-context-all capabilities, surfacing this gap.

Jira: SDK-2731


Note

Medium Risk
Changes what PII leaves the SDK in custom event payloads (privacy fix); behavior is narrow and aligned with existing feature-event formatting.

Overview
Custom events now apply the same anonymous-context redaction as feature events when serializing inline context.

In format_event_set_context/4, the <<"custom">> path switches from format_context_for_event/2 to format_context_for_event_with_anonyous_redaction/2, so optional attributes on anonymous contexts are stripped and listed under _meta.redactedAttributes instead of being sent in the clear. Multi-kind payloads only redact anonymous kinds; non-anonymous kinds are unchanged.

Adds add_custom_events_redacts_anonymous_context in ldclient_events_SUITE for single- and multi-kind cases.

Reviewed by Cursor Bugbot for commit 1ed5c06. Bugbot is set up for automated code reviews on this repo. Configure here.

Custom events routed the event context through the non-redacting
format_context_for_event/2, so an anonymous context's attributes were
emitted intact instead of being redacted into _meta.redactedAttributes
(feature events already used the anonymous-redacting formatter). Route
custom events through format_context_for_event_with_anonyous_redaction/2
to match, and add regression coverage for single- and multi-kind
anonymous custom events.

SDK-2731
@keelerm84
keelerm84 requested a review from a team as a code owner July 21, 2026 11:11
@keelerm84
keelerm84 merged commit 56d2fb4 into main Jul 21, 2026
8 checks passed
@keelerm84
keelerm84 deleted the mk/SDK-2731/redact-anonymous-custom-events branch July 21, 2026 13:51
jareddellitt added a commit to knocklabs/erlang-server-sdk that referenced this pull request Jul 22, 2026
* feat: For otp 25+ use public_key:cacerts_get for the default certificate list. (launchdarkly#114)

closes launchdarkly#113
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>

---------

Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>

* fix: Make gun open errors explicitly into temporary failures (launchdarkly#118)

Updates `gun_open_*` failures during stream initialization to translate
to `temporary` failures, as expected by the calling function.

I think these errors were always meant to be explicitly `temporary`.
Otherwise, if these are currently returned the calling `try` block
wouldn't pattern match and I believe it would crash the process. At that
point we'd be relying on the supervisor restarts instead of managed
backoff.

Co-authored-by: Zurab Davitiani <zurab@launchdarkly.com>

* chore(main): release 3.1.0 (launchdarkly#116)

:robot: I have created a release *beep* *boop*
---


##
[3.1.0](launchdarkly/erlang-server-sdk@v3.0.4...v3.1.0)
(2024-01-04)


### Features

* For otp 25+ use public_key:cacerts_get for the default certificate
list.
([launchdarkly#114](launchdarkly#114))
([b7065aa](launchdarkly@b7065aa))


### Bug Fixes

* Make gun open errors explicitly into temporary failures
([launchdarkly#118](launchdarkly#118))
([fc70445](launchdarkly@fc70445))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat: Always inline contexts for feature events (launchdarkly#119)

* feat: Redact anonymous attributes within feature events (launchdarkly#120)

* chore(main): release 3.2.0 (launchdarkly#122)

:robot: I have created a release *beep* *boop*
---


##
[3.2.0](launchdarkly/erlang-server-sdk@v3.1.0...v3.2.0)
(2024-03-14)


### Features

* Always inline contexts for feature events
([launchdarkly#119](launchdarkly#119))
([e5c6cc4](launchdarkly@e5c6cc4))
* Redact anonymous attributes within feature events
([launchdarkly#120](launchdarkly#120))
([d334861](launchdarkly@d334861))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: add .sdk_metadata.json (launchdarkly#123)

* chore: add Elixir to metadata languages (launchdarkly#124)

* feat: add tlsv1.3 to default cipher suites (launchdarkly#128)

**Requirements**

- [ ] I have added test coverage for new or changed functionality
- [ ] I have followed the repository's [pull request submission
guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [ ] I have validated my changes against all supported platform
versions

**Related issues**

launchdarkly#127

**Describe the solution you've provided**

SDK client should be able to connect successfully to a server presenting
TLSv1.2 and TLSv1.3 cipher suites.

**Describe alternatives you've considered**

Only support TLSv1.2 specifically.

---------

Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>

* chore(main): release 3.3.0 (launchdarkly#129)

:robot: I have created a release *beep* *boop*
---


##
[3.3.0](launchdarkly/erlang-server-sdk@v3.2.0...v3.3.0)
(2024-05-20)


### Features

* add tlsv1.3 to default cipher suites
([launchdarkly#128](launchdarkly#128))
([4074483](launchdarkly@4074483))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: Use contract test service action. (launchdarkly#125)

* chore: Add support for TLS tests to the contract test service. (launchdarkly#130)

* chore: Add workflow dispatch for CI. (launchdarkly#131)

After some changes to the contract tests I would like to be able to
manually trigger this.

* fix: Add mising events_uri type to options (launchdarkly#133)

**Requirements**

- [x] I have added test coverage for new or changed functionality
- [x] I have followed the repository's [pull request submission
guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [x] I have validated my changes against all supported platform
versions

**Related issues**

**Describe the solution you've provided**

Add missing type which causing a static analyzer (dialyzer) error when
calling the lib from elixir.

**Describe alternatives you've considered**


**Additional context**

* chore(main): release 3.3.1 (launchdarkly#134)

:robot: I have created a release *beep* *boop*
---


##
[3.3.1](launchdarkly/erlang-server-sdk@v3.3.0...v3.3.1)
(2024-09-23)


### Bug Fixes

* Add mising events_uri type to options
([launchdarkly#133](launchdarkly#133))
([65bb39c](launchdarkly@65bb39c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: Fix test builds with meck incompatibilities. (launchdarkly#135)

Works around: eproxus/meck#248

* feat: Use ets for last-known server-time. (launchdarkly#136)

Addresses main bottleneck of launchdarkly#132.

* feat: Add support for client-side prerequisite events. (launchdarkly#137)

BEGIN_COMMIT_OVERRIDE
feat: Add support for client-side prerequisite events.
feat: Add support for client-side visibility for all_flags_state.
END_COMMIT_OVERRIDE

SDK-803

* chore(main): release 3.4.0 (launchdarkly#138)

:robot: I have created a release *beep* *boop*
---


##
[3.4.0](launchdarkly/erlang-server-sdk@v3.3.1...v3.4.0)
(2024-10-24)


### Features

* Add support for client-side prerequisite events.
([167308a](launchdarkly@167308a))
* Add support for client-side visibility for all_flags_state.
([167308a](launchdarkly@167308a))
* Use ets for last-known server-time.
([launchdarkly#136](launchdarkly#136))
([568ac51](launchdarkly@568ac51))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat: Updates shotgun version to 1.1.0 release (launchdarkly#139)

**Requirements**

- [ ] I have added test coverage for new or changed functionality
- [ ] I have followed the repository's [pull request submission
guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [ ] I have validated my changes against all supported platform
versions

**Related issues**

Provide links to any issues in this repository or elsewhere relating to
this pull request.
None found.

**Describe the solution you've provided**
There was a new version of shotgun released on Oct 24 that has an
additional callback for a down message. Upgrading the dependency will
support that.

**Describe alternatives you've considered**
The alternative would be not to update the dependencies

**Additional context**
The applications I am using this in are getting those messages sent now
and ending up in undefined function errors in our error tracking tool.
It is not a great consequence, but upgrading the dependency seems like a
good practice anyway.

* chore(main): release 3.5.0 (launchdarkly#140)

:robot: I have created a release *beep* *boop*
---


##
[3.5.0](launchdarkly/erlang-server-sdk@v3.4.0...v3.5.0)
(2024-11-07)


### Features

* Updates shotgun version to 1.1.0 release
([launchdarkly#139](launchdarkly#139))
([52dafc3](launchdarkly@52dafc3))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat: Add support for setting the redis username when using redis persistence. (launchdarkly#142)

**Requirements**

- [ ] I have added test coverage for new or changed functionality
- [x] I have followed the repository's [pull request submission
guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [x] I have validated my changes against all supported platform
versions

**Describe the solution you've provided**

`eredis` supports taking in a username for connections in addition to
the password. The default is `undefined`, which will remain unchanged
unless `redis_password` is explicitly configured.

* feat: Update certifi to 2.14.0 (launchdarkly#144)

Regular update to ensure current certificate files when using certifi.

Extended the tls-tests to verify connections with certifi as well as
default. (In some environments both tests will use certifi).

* chore(main): release 3.6.0 (launchdarkly#143)

:robot: I have created a release *beep* *boop*
---


##
[3.6.0](launchdarkly/erlang-server-sdk@v3.5.0...v3.6.0)
(2025-03-17)


### Features

* Add support for setting the redis username when using redis
persistence.
([launchdarkly#142](launchdarkly#142))
([c37fefd](launchdarkly@c37fefd))
* Update certifi to 2.14.0
([launchdarkly#144](launchdarkly#144))
([69771cf](launchdarkly@69771cf))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: Add user agent to .sdk_metadata.json (launchdarkly#145)

* chore: Update Ubuntu versions from 20.04/22.04 to 24.04 in GitHub workflows (launchdarkly#146)

Update workflows to use ubuntu 24.04

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* feat: Inline context for custom events (launchdarkly#147)

* chore(main): release 3.7.0 (launchdarkly#148)

:robot: I have created a release *beep* *boop*
---


##
[3.7.0](launchdarkly/erlang-server-sdk@v3.6.0...v3.7.0)
(2025-05-08)


### Features

* Inline context for custom events
([launchdarkly#147](launchdarkly#147))
([1cd2357](launchdarkly@1cd2357))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: Change certifi dependency to a minimum version. (launchdarkly#150)

Fixes: launchdarkly#149

* chore(main): release 3.7.1 (launchdarkly#151)

:robot: I have created a release *beep* *boop*
---


##
[3.7.1](launchdarkly/erlang-server-sdk@v3.7.0...v3.7.1)
(2025-06-03)


### Bug Fixes

* Change certifi dependency to a minimum version.
([launchdarkly#150](launchdarkly#150))
([b1a91c0](launchdarkly@b1a91c0)),
closes
[launchdarkly#149](launchdarkly#149)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: Fix an issue with rollouts with a missing attribute. (launchdarkly#153)

When a rollout had a seed, and the context being evaluated was missing
the attribute referenced by `bucketBy`, then the `null` atom would
attempt to be used as a binary.

When the attribute is `null`, then the bucket value should always be
`0`.

This doesn't affect the experimentation logic related to presence of the
context itself.

Fixes: launchdarkly#152

* chore(main): release 3.7.2 (launchdarkly#154)

:robot: I have created a release *beep* *boop*
---


##
[3.7.2](launchdarkly/erlang-server-sdk@v3.7.1...v3.7.2)
(2025-06-25)


### Bug Fixes

* Fix an issue with rollouts with a missing attribute.
([launchdarkly#153](launchdarkly#153))
([a94ed9c](launchdarkly@a94ed9c)),
closes
[launchdarkly#152](launchdarkly#152)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat: Update minimum OTP version to 24 (launchdarkly#156)

BEGIN_COMMIT_OVERRIDE
feat: Update minimum OTP version to 24.
fix: Fix issue where sometimes a dropped stream would not reconnect.
END_COMMIT_OVERRIDE

In shotgun 1.1.0 a new feature was introduced which introduced a bug
with the SSE connection. Where previously it would close its process in
all cases where it disconnected, now it would not do that in some
situations. This caused launchdarkly#155.

We contributed an update to shotgun to allow disabling the feature for
async connections where you need to monitor the connection state.
inaka/shotgun#203

This was released in shotgun 1.2.1.

This PR updates to 1.2.1 and uses the option to disable the `reopen`
feature added in shotgun 1.1.0.
This PR also raises the minimum erlang version to 24. This is required
to gun 2.2 which shotgun 1.2.0 and newer depends on. OTP doesn't have an
official support timeline, but OTP 24 came out in 2021, so most people
will likely be on 24 or greater especially when using elixir.

* chore(main): release 3.8.0 (launchdarkly#157)

:robot: I have created a release *beep* *boop*
---


##
[3.8.0](launchdarkly/erlang-server-sdk@v3.7.2...v3.8.0)
(2025-08-08)


### Features

* Update minimum OTP version to 24.
([82f0e77](launchdarkly@82f0e77))


### Bug Fixes

* Fix issue where sometimes a dropped stream would not reconnect.
([82f0e77](launchdarkly@82f0e77))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: Fix seconds reference guide link. Update minimum OTP version in the readme. (launchdarkly#159)

Fixes: launchdarkly#158

* fix: Prevent uninitialized SDK warning in daemon mode. (launchdarkly#162)

When using daemon mode a warning would be produced on variation calls
that the SDK had not finished initialization. This situation doesn't
apply to daemon mode where we only read from the persistent store. It is
instead intended to apply to persistent store modes where the SDK is
writing to the persistence and applies to evaluations between SDK
startup and initialization being complete.

This change updates the null update process to immediately report that
it is initialized.

* fix: Handle connection errors during read operations. (launchdarkly#163)

This PR adds error handling for read operations from the redis
persistent store. This is an incremental step in addressing launchdarkly#161.

In the case we cannot read from redis, we will treat that as a flag not
being found. If we cannot read from redis when doing an initialization
check, then we will consider the store not to have been initialized.

This addresses the primary issues with daemon mode, but it doesn't
address persistent store errors in non-daemon mode, or other cases that
could cause the redis process to exit. Subsequent PRs will address these
issues.

The detailed variation interface does not report why a flag cannot be
found, and isn't the correct avenue for doing so, and as such we may
have to consider supporting the data store status interface. This is
also not tackled in this PR.

* fix: Handle redis errors during mutations. (launchdarkly#165)

Next PR for launchdarkly#161 

When redis wasn't available during application start it would cause the
redis process to crash even in daemon mode.

There are a few factors that contributed to this:
- We tried to write to redis even in daemon mode. We attempted to
pre-create features/segements buckets. We should only read in daemon
mode.
- When we failed to complete any redis operation we would crash the
redis storage process.
- When the redis storage process crashed it didn't restart.

This PR makes adjustments to each of these behaviors.

There is some additional logic for ETS which will trigger a reconnection
if there was an attempt at a store update during a store outage. We may
want to consider something like this for redis as well. It will not
affect daemon mode.

* fix: Redact SDK in gen_server descriptions and network errors. (launchdarkly#166)

There are two categories of issues addressed in this PR.

The first is that there are situations where an SDK key can be logged if
it is part of process state. So this PR introduces format_status
callbacks which control how that formatting is done, and redacts the SDK
key.

The second is there are some network errors that could log the headers
which include authorization. This PR adds some extra formatting to
network errors to reduce the detail contained in the error. A
side-effect will be that in some situations logs will contain less
useful data for troubleshooting.

* chore(main): release 3.8.1 (launchdarkly#164)

:robot: I have created a release *beep* *boop*
---


##
[3.8.1](launchdarkly/erlang-server-sdk@v3.8.0...v3.8.1)
(2025-11-12)


### Bug Fixes

* Handle connection errors during read operations.
([launchdarkly#163](launchdarkly#163))
([5838c2b](launchdarkly@5838c2b))
* Handle redis errors during mutations.
([launchdarkly#165](launchdarkly#165))
([9dbe853](launchdarkly@9dbe853))
* Prevent uninitialized SDK warning in daemon mode.
([launchdarkly#162](launchdarkly#162))
([0961922](launchdarkly@0961922))
* Redact SDK in gen_server descriptions and network errors.
([launchdarkly#166](launchdarkly#166))
([3a4e005](launchdarkly@3a4e005))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: Fix an issue where <<kind>> would be included in the list of context kinds when kind was binary. (launchdarkly#168)

```
can_get_kinds_of_multi_context_binary_kind(_) ->
    [<<"org">>, <<"user">>] =
        ldclient_context:get_kinds(#{
            <<"kind">> => <<"multi">>,
            <<"user">> => #{key => <<"user-key">>},
            <<"org">> => #{key => <<"org-key">>}}).
```

Was `[<<"kind">>,<<"org">>, <<"user">>]`, when it should be `[<<"org">>,
<<"user">>]`.

It worked correct when kind was an atom.

```
can_get_kinds_of_multi_context_binary_kind(_) ->
    [<<"org">>, <<"user">>] =
        ldclient_context:get_kinds(#{
            kind => <<"multi">>, % <-- kind instead of <<"kind">>
            <<"user">> => #{key => <<"user-key">>},
            <<"org">> => #{key => <<"org-key">>}}).
```

* feat: Support combining multi-contexts using ldcontext:new_multi_from. (launchdarkly#167)

Updates ldclient:new_multi_from to support merging single and multi-kind
contexts into a single multi-kind context.

Fixes: launchdarkly#126

* chore(main): release 3.9.0 (launchdarkly#169)

:robot: I have created a release *beep* *boop*
---


##
[3.9.0](launchdarkly/erlang-server-sdk@v3.8.1...v3.9.0)
(2026-02-09)


### Features

* Support combining multi-contexts using ldcontext:new_multi_from.
([launchdarkly#167](launchdarkly#167))
([7c9f934](launchdarkly@7c9f934)),
closes
[launchdarkly#126](launchdarkly#126)


### Bug Fixes

* Fix an issue where &lt;<kind&gt;> would be included in the list of
context kinds when kind was binary.
([launchdarkly#168](launchdarkly#168))
([8cdee5c](launchdarkly@8cdee5c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore(ci): update supercharge/redis-github-action to v1.8.1 (launchdarkly#171)

**Requirements**

- [x] I have added test coverage for new or changed functionality — N/A,
CI infrastructure change only
- [x] I have followed the repository's [pull request submission
guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [x] I have validated my changes against all supported platform
versions — N/A, CI infrastructure change only

**Related issues**

Fixes the CI failure in [PR
launchdarkly#170](launchdarkly#170) where
all three "Build and Test" matrix jobs fail at the Redis setup step.

**Describe the solution you've provided**

Updates the pinned SHA for `supercharge/redis-github-action` in
`.github/actions/ci/action.yml` from v1.8.0 (`ea9b21c...`) to v1.8.1
(`bc274cb...`).

The v1.8.0 Docker container action bundles a Docker client using API
version 1.40, which is now incompatible with the Docker daemon on
`ubuntu-24.04` runners (minimum API version 1.44). This causes the
error:

```
docker: Error response from daemon: client version 1.40 is too old.
Minimum supported API version is 1.44, please upgrade your client to a newer version.
```

**Human review checklist**

- [ ] Verify `bc274cb7238cd63a45029db04ee48c07a72609fd` corresponds to
the [v1.8.1
release](https://github.com/supercharge/redis-github-action/releases/tag/1.8.1)
of `supercharge/redis-github-action`
- [ ] Confirm CI passes with the updated action

**Describe alternatives you've considered**

Could replace the action entirely with a raw `docker run` step for
Redis, but updating to the latest release is the simplest and least
disruptive fix.

**Additional context**

- Link to Devin run:
https://app.devin.ai/sessions/6ca703ca90bb4f6795aa4922d3f5251a
- Requested by: rlamb@launchdarkly.com

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: rlamb@launchdarkly.com <rlamb@launchdarkly.com>

* chore: Update sdk_metadata features (launchdarkly#170)

**Requirements**

- [x] I have added test coverage for new or changed functionality — N/A,
metadata-only change
- [x] I have followed the repository's [pull request submission
guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [x] I have validated my changes against all supported platform
versions — N/A, metadata-only change

**Related issues**

N/A

**Describe the solution you've provided**

Adds a `features` map to `.sdk_metadata.json` listing all features the
Erlang Server SDK supports, with their introduction versions. Features
were identified by cross-referencing:

1. The SDK's column in the [server-side SDK feature
table](https://github.com/launchdarkly/ld-docs-private/blob/587f1518865abdf4ffc3bcb9cf08c3a67b2f8540/fern/topics/sdk/index.mdx)
(A-K section, Erlang column)
2. Canonical feature keys from
[feature_info.json](https://github.com/launchdarkly/sdk-meta/blob/main/products/feature_info.json)
3. The repository's CHANGELOG.md for version verification

16 features are included (alphabetical): `allFlags`, `appMetadata`,
`contexts`, `experimentation`, `fileDataSource`,
`inlineContextCustomEvents`, `inlineContextEvaluationEvents`,
`offlineMode`, `privateAttrs`, `relayProxyDaemon`, `relayProxyProxy`,
`storingData`, `storingDataRedis`, `testDataSource`, `track`,
`variationDetail`.

**Items for reviewer attention:**

- **`fileDataSource` version is `1.2`**, not `1.0` as the docs table
states. The CHANGELOG clearly shows file data source was added in
v1.2.0, not v1.0.0. This appears to be a docs table error.
- **`inlineContextEvaluationEvents` at `3.2`** was identified from the
CHANGELOG (v3.2.0: "Always inline contexts for feature events") but is
not explicitly listed as a row in the docs table. The docs table only
has "Inline contexts in custom events" as a row. Verify this is a
correct addition.
- **`storingDataRedis` at `1.0`** was added alongside the generic
`storingData` since Redis is the only persistent store the SDK supports
(added in v1.0.0 per CHANGELOG). Verify both entries are desired.
- **`experimentation` at `1.2`**: The docs table says 1.2. Basic
experimentation support (`track_with_metric`) existed since pre-release,
but 1.2 added experiment traffic allocation. Confirm 1.2 is the correct
version.

**Describe alternatives you've considered**

Could have used the docs table version of `1.0` for `fileDataSource`
verbatim, but the CHANGELOG evidence is clear that the feature was
introduced in 1.2.0.

**Additional context**

- Link to Devin run:
https://app.devin.ai/sessions/7d74c54d1157417bb702d163ee654c87
- Requested by: rlamb@launchdarkly.com

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>

* chore: Update fetch-depth comment (launchdarkly#172)

* [SEC-7924] chore: pin third-party GitHub Actions to commit SHAs

Pin all third-party GitHub Actions to full-length commit SHAs to prevent
supply chain attacks. Addresses findings from the
third-party-action-not-pinned-to-commit-sha Semgrep rule.

* ci: Include release version with SHA. (launchdarkly#175)

<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Low Risk**
> Comment-only change in the GitHub Actions workflow; the action ref
(SHA) and behavior remain the same.
> 
> **Overview**
> Updates `.github/workflows/release-please.yml` to clarify the pinned
`google-github-actions/release-please-action` version by changing the
inline comment from `v3` to `v3.7.13` while keeping the same commit SHA.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
7df7b1b. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

* chore: Adding missing permissions to stale workflow (launchdarkly#176)

* chore: Update cowlib for contract tests to work with OTP 28. (launchdarkly#177)

<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Dependency upgrade of the HTTP server library plus overridden
dependency resolution could introduce subtle runtime or build
differences in the test service, especially around Cowboy/Ranch
integration.
> 
> **Overview**
> Updates `test-service` to use `cowboy` `2.14.2` (from `2.8.0`) to
improve compatibility with newer OTP versions.
> 
> Adds a `rebar.config` `overrides` block that forces `cowboy` to
resolve with `cowlib` and `ranch`, as a workaround for
ninenines/cowboy#1670 and older `rebar3` behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c3a1e03. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

* chore: Upgrade release-please-action from v3 to v5 (launchdarkly#179)

Upgrade release-please-action from v3.7.13 to v5.0.0 and migrate from the old google-github-actions org to googleapis.

* ci: grant pull-requests:read so lint-pr-title workflow starts (launchdarkly#180)

## Summary

Adds `permissions: pull-requests: read` at the workflow level in
`.github/workflows/lint-pr-title.yml`.

The reusable workflow at
`launchdarkly/gh-actions/.github/workflows/lint-pr-title.yml` was
updated
([gh-actions#86](launchdarkly/gh-actions#86)) to
declare `permissions: pull-requests: read` at the job level. A reusable
workflow can only request a subset of the permissions the caller grants,
so without an explicit `permissions` block in the caller, the workflow
fails with `startup_failure`. Same fix as
[sdk-meta#429](launchdarkly/sdk-meta#429).

## Review & Testing Checklist for Human

- [ ] Verify the `Lint PR title` workflow run on this PR succeeds (exits
`success` rather than `startup_failure`)

### Notes

No product code is changed — this is a workflow-only permissions fix.

Link to Devin session:
https://app.devin.ai/sessions/c7b96da5c9074500aa684bc9a9ba1c31
Requested by: @kinyoklion

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: workflow-only change that just adds explicit `pull-requests:
read` permissions to allow the reusable lint workflow to start.
> 
> **Overview**
> Fixes the `Lint PR title` GitHub Action by explicitly granting
**workflow-level** `permissions: pull-requests: read`, so the reusable
`launchdarkly/gh-actions` lint workflow can request the needed
permission and avoid `startup_failure`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9862c80. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* feat: add X-LaunchDarkly-Instance-Id header (launchdarkly#181)

* chore(main): release 3.10.0 (launchdarkly#182)

:robot: I have created a release *beep* *boop*
---


##
[3.10.0](launchdarkly/erlang-server-sdk@v3.9.0...v3.10.0)
(2026-05-28)


### Features

* add X-LaunchDarkly-Instance-Id header
([launchdarkly#181](launchdarkly#181))
([2fbe6cc](launchdarkly@2fbe6cc))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: Fix apache license format (launchdarkly#183)

* chore(main): release 3.10.1 (launchdarkly#184)

:robot: I have created a release *beep* *boop*
---


##
[3.10.1](launchdarkly/erlang-server-sdk@v3.10.0...v3.10.1)
(2026-05-28)


### Bug Fixes

* Fix apache license format
([launchdarkly#183](launchdarkly#183))
([de83758](launchdarkly@de83758))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update SECURITY.md to reflect bug bounty program

* feat: Update rebar config, use shotgun ~> 1.2 (launchdarkly#187)

## Description

This version of `shotgun` brings in `gun` 2.2, which has multiple
CVEs, causing app authors to be aware and explicitly override `gun`
or `shotgun`.

## Changes

Update rebar.config to use `shotgun` `~> 1.2`.

Before
```
$ rebar3 tree
└─ ldclient─3.10.1 (project app)
   ...
   ├─ shotgun─1.2.1 (hex package)
   │  └─ gun─2.2.0 (hex package) <- vulnerable
   │     └─ cowlib─2.17.1 (hex package)
   ...
```

With this change
```
$ rebar3 tree
└─ ldclient─3.10.1 (project app)
   ...
   ├─ shotgun─1.2.2 (hex package)
   │  └─ gun─2.4.1 (hex package) <- ok
   │     └─ cowlib─2.17.1 (hex package)
   ...
```

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Single-line rebar dependency constraint change with no Erlang source
edits; risk is limited to minor HTTP client library behavior differences
in shotgun/gun.
> 
> **Overview**
> **Dependency-only change** in `rebar.config`: `shotgun` moves from a
pinned `1.2.1` to **`~> 1.2`**, so rebar can resolve a newer 1.2.x
release.
> 
> That pulls **`gun` 2.4.1** (via shotgun 1.2.2) instead of **`gun`
2.2.0**, which had known CVEs and often forced downstream apps to
override `gun` or `shotgun` explicitly. HTTP/SSE behavior still goes
through shotgun; this is a transitive security bump, not an application
code change.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0eb2c90. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

* feat: Ensure shotgun dependency is at least 1.2.2. (launchdarkly#189)

<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Low Risk**
> Dependency constraint-only change with no logic changes; low risk
unless 1.2.2 introduces breaking behavior in shotgun itself.
> 
> **Overview**
> Tightens the **shotgun** HTTP client dependency in `rebar.config` from
`~> 1.2` to **`~> 1.2.2`**, so builds resolve at least version 1.2.2. No
application or test code changes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f45f20a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

* chore(main): release 3.11.0 (launchdarkly#188)

:robot: I have created a release *beep* *boop*
---


##
[3.11.0](launchdarkly/erlang-server-sdk@v3.10.1...v3.11.0)
(2026-06-17)


### Features

* Ensure shotgun dependency is at least 1.2.2.
([launchdarkly#189](launchdarkly#189))
([b06a5c6](launchdarkly@b06a5c6))
* Update rebar config, use shotgun ~&gt; 1.2
([launchdarkly#187](launchdarkly#187))
([6c3e626](launchdarkly@6c3e626))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Mechanical version and changelog updates only; no runtime or
dependency file changes in this diff.
> 
> **Overview**
> **Release 3.11.0** bumps the package version from **3.10.1** to
**3.11.0** across Release Please metadata
(`.release-please-manifest.json`), `ldclient.app.src`, and the `VERSION`
macro in `ldclient_config.erl`, and adds the matching **CHANGELOG**
entry.
> 
> The release notes call out **shotgun** dependency work already landed
on main: **rebar** now uses `shotgun ~> 1.2` with a floor of **1.2.2**.
This PR does not change `rebar.config` or SDK logic—only version and
changelog artifacts for the release.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
81c5ae2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: Redact anonymous context attributes in custom events (launchdarkly#191)

---------

Co-authored-by: Yordis Prieto <yordis.prieto@gmail.com>
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
Co-authored-by: zurab-darkly <56062336+zurab-darkly@users.noreply.github.com>
Co-authored-by: Zurab Davitiani <zurab@launchdarkly.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matthew M. Keeler <mkeeler@launchdarkly.com>
Co-authored-by: Casey Waldren <cwaldren@launchdarkly.com>
Co-authored-by: Boris Wainberg <bwainb044@gmail.com>
Co-authored-by: Todd Resudek <todd.resudek@jackpocket.com>
Co-authored-by: William <hexedpackets@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
Co-authored-by: Patrick Kaeding <pkaeding@launchdarkly.com>
Co-authored-by: Owen McCarthy <omccarth333@gmail.com>
Co-authored-by: Kane Parkinson <kparkinson@launchdarkly.com>
Co-authored-by: Kane Parkinson <93555788+kparkinson-ld@users.noreply.github.com>
Co-authored-by: Matt Savoia <xbrucexwaynex@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants