Home Posts Post Search Tag Search

Real World Event Sourcing - 08 - Securing Event-Sourced Applications
Published on: 2026-07-28 Tags: elixir, Systems, event-sourcing, Real World Event Sourcing, EventHandler, EventStoreDB, Event Schema Evolution

8. Securing Event-Sourced Applications

Okay now we need to talk about securing the event streams. Let’s divide them into distinct categories and then try and solve the issues.


• Aggregates and incoming command requests
• The event stream
• Sensitive information on events
• Projections
• Process manager state



Securing Event Streams

One of the first thing you should do is understand which parts of the application need their own security measures and which ones will be covered by being layered under other parts of the code.


The event log is the purest form of truth if someone can mess with it you are going to have issues. You can say with some certainty that the events created on your own machine will be secure but anything that is created outside will not have the same guarantees. Follow this mantra, “trust no one, and defense-in-depth“.


You will want to have some way of making sure that a user should be able to add an event, a JSON Web Token (JWT) being valid will help with that.


Moving on to the read side of things you should make sure that a user will only be able to read any projections and or any part of the code should only be able to read aggregates, projectors, etc.

Moving on to the idea of using the cloud envelope you can see that there might be ways of using the meta data to better source the information. Here is a cloud event that you have already seen

{
  "specversion": "1.0",
  "id": "2a0562bb-6657-4918-ad21-bec63f38bc11",
  "type": "building_spawned",
  "source": "lunar_frontiers",
  "datacontenttype": "application/json",
  "time": "2023-07-30T18:07:40.210505Z",
  "data": {
    "game_id": "9c0562bb-6657-4918-ad21-bec63f38bc10",
    "site_id": "193e4856-3ffa-4509-b6b0-16f2d106d046",
    "site_type": "HQ",
    . . .
  }
}

You might want to use the source field to identify the exact user or application that is sending the information. This is where the JWT might be useful to make sure that the information is valid and had the rights to add an event. Here is a more updated version of that meta data.

{
  "specversion": "1.0",
  "id": "2a0562bb-6657-4918-ad21-bec63f38bc11",
  "type": "building_spawned",
  "source": "eyJhbGciOiJIUzI1NiIsInR5cC...xk",
  "datacontenttype": "application/json",
  "time": "2023-07-30T18:07:40.210505Z",
  "data": {
    "game_id": "9c0562bb-6657-4918-ad21-bec63f38bc10",
    "site_id": "193e4856-3ffa-4509-b6b0-16f2d106d046",
    "site_type": "HQ",
    . . .
  }
}

You can see the JWT has been trimmed but it holds all the information that we need to see if that user has the right credentials. There are other ways of identifying a person. There is also SPIFFE (Secure Production Identity Framework for Everyone)




Securing Command Paths

Commands are short lived requests. If you are allowing an outside entity to issue commands you need to worry about at least one of the following: Authentication (knowing who an entity is) or Authorization (know what and entity can do).




Securing Projections and Component State

This all starts with the data store. Making sure that you have the right authorization if sometimes up to the service that you are using to store all the data.




Supporting GDPR and the Right to Be Forgotten

GDPR (General Data Protection Regulation). One of the biggest thing for this is the “Right to be Forgotten.” This is very important in the EU and we will go over a few things about this now.


Crypto-Shredding

(PII) (Personally Identifiable Information). You must be able to get rid of any PII of any user that asks for it. But this would break the first rule that events are immutable. There is a better way and that is getting rid of the private key that is used to decrypt the data. Let’s look at an event and see what is PII and what we can do about it to be able to Crypto-Shred.

{
  "specversion": "1.0",
  "id": "2a0562bb-6657-4918-ad21-bec63f38bc11",
  "type": "funds_withdrawn",
  "source": "eyJhbGciOiJIUzI1NiIsInR5cC...xk",
  "datacontenttype": "application/json",
  "time": "2023-07-30T18:07:40.210505Z",
  "data": {
    "account_number": "3BR5679DF",
    "amount": 2300,
    "memo": "this is the best application ever"
    . . .
  }
}

This contains a persons account_number and would be a way to identify them later on. If we wanted to be able to obfuscate the information we could assign a PII reference ID that would be used to look up that persons needed data. Here is the same event but obfuscated.

{
  "specversion": "1.0",
  "id": "2a0562bb-6657-4918-ad21-bec63f38bc11",
  "x-pii-reference-key": "30a584ae-4c80-4056-baf2-1d2b0a1c4bf3",
  "type": "funds_withdrawn",
  "source": "eyJhbGciOiJIUzI1NiIsInR5cC...xk",
  "datacontenttype": "application/json",
  "time": "2023-07-30T18:07:40.210505Z",
  "data": {
    "account_number": " ... encrypted data ... ",,
    "amount": 2300,
    "memo": "this is the best application ever"
    . . .
  }
}

Supporting Retention Periods

There is also a way of doing the above but with a set period of time to keep PII. You would do the first layer of encryption then add a second layer on top that would need to be renewed every X days/months/hours/etc. You would then decrypt and encrypt the data every time it needs to be accessed or and event needs to be added for that user.


Rationalizing Crypto-Shredding with External Context




Wrapping Up