---
title: Encrypting operational secrets
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/security/encrypted-values.mdx
description: Mark a value [Encrypted] to protect it at rest without enrolling it in GDPR right-to-erasure.
---

import { Tabs, TabItem } from '@astrojs/starlight/components';


Marking a value `[Encrypted]` tells Chronicle to encrypt it at rest — the same way `[PII]` does — but as a security measure with no data subject and no lawful basis for erasure, rather than a GDPR compliance mechanism. See [Security](/chronicle/security/) for when to reach for this instead of `[PII]`.

## The marker

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class EncryptedAttribute(EncryptionScope scope = EncryptionScope.Subject, string details = "") : Attribute
{
    public EncryptionScope Scope { get; } = scope;
    public string Details { get; } = details;
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/confidentiality/encrypted/marker.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.confidentiality.Encrypted
import io.cratis.chronicle.confidentiality.EncryptionScope
import io.cratis.chronicle.events.EventType

// annotation class Encrypted(
//     val scope: EncryptionScope = EncryptionScope.Subject,
//     val description: String = ""
// )
// Apply it to a class (a concept type), a property, a field, or a constructor parameter.
// Both arguments are optional; description records why the value needs encryption.
@EventType
data class EncryptedMarkerPartnerIntegrationConfigured(
    @Encrypted(EncryptionScope.Subject, "Partner API credential - an operational secret with no data subject")
    val apiKey: String
)
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/confidentiality/encrypted/marker.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.confidentiality.Encrypted;
import io.cratis.chronicle.confidentiality.EncryptionScope;
import io.cratis.chronicle.events.EventType;

// @interface Encrypted {
//     EncryptionScope scope() default EncryptionScope.Subject;
//     String description() default "";
// }
// Apply it to a class (a concept type), a record component, a field, or a constructor parameter.
// Both elements are optional; description records why the value needs encryption.
@EventType
record EncryptedMarkerPartnerIntegrationConfigured(
        @Encrypted(
            scope = EncryptionScope.Subject,
            description = "Partner API credential - an operational secret with no data subject")
        String apiKey) {
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/confidentiality/encrypted/marker.md)

</TabItem>
<TabItem label="Elixir">

```elixir
# Chronicle.Confidentiality.encrypted/1,2,3 - imported automatically inside
# modules that `use Chronicle.Events.EventType` or
# `use Chronicle.ReadModels.ReadModel`. Marks one struct field.
#
#   defmacro encrypted(field, scope \\ :subject, details \\ "")
#
# Chronicle.Concept.encrypted/0,1,2 - imported automatically inside modules
# that `use Chronicle.Concept`. Marks the concept's value itself.
#
#   defmacro encrypted(scope \\ :subject, details \\ "")
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/confidentiality/encrypted/marker.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { encrypted, EncryptionScope, eventType } from '@cratis/chronicle';

// encrypted(scope: EncryptionScope = EncryptionScope.Subject, details?: string): PropertyDecorator & ClassDecorator
// Apply it to a class (a concept type) or to a property. Both arguments are optional.
@eventType()
class EncryptedMarkerPartnerIntegrationConfigured {
    @encrypted(EncryptionScope.Subject, 'Partner API credential - an operational secret with no data subject')
    apiKey = '';
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/confidentiality/encrypted/marker.md)

</TabItem>
</Tabs>

`EncryptedAttribute` lives in `Cratis.Chronicle.ProtectedValues` — a namespace of its own, deliberately not nested under `Compliance`. The optional `details` parameter records why the value needs encryption, exactly like `[PII]`'s `details` parameter.

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.ProtectedValues;
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/confidentiality/encrypted/import.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.confidentiality.Encrypted
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/confidentiality/encrypted/import.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.confidentiality.Encrypted;
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/confidentiality/encrypted/import.md)

</TabItem>
<TabItem label="Elixir">

```elixir
# encrypted/1,2,3 is imported automatically inside modules that
# `use Chronicle.Events.EventType` or `use Chronicle.ReadModels.ReadModel` -
# there is nothing to import explicitly.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/confidentiality/encrypted/import.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { encrypted } from '@cratis/chronicle';
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/confidentiality/encrypted/import.md)

</TabItem>
</Tabs>

## Applying to a concept

The preferred approach, exactly as with `[PII]`: mark the concept type itself. Every event using this type is then automatically encrypted.

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
[Encrypted]
public record PartnerApiKey(string Value) : ConceptAs<string>(Value);

public record PartnerIntegrationConfiguredWithKey(PartnerApiKey ApiKey);
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/confidentiality/encrypted/concept-level.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.concepts.ConceptAs
import io.cratis.chronicle.confidentiality.Encrypted

@Encrypted
data class EncryptedAttrPartnerApiKey(override val value: String) : ConceptAs<String>

data class EncryptedAttrPartnerIntegrationConfiguredWithKey(val apiKey: EncryptedAttrPartnerApiKey)
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/confidentiality/encrypted/concept-level.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.concepts.ConceptAs;
import io.cratis.chronicle.confidentiality.Encrypted;

@Encrypted
record EncryptedAttrPartnerApiKey(String value) implements ConceptAs<String> {
    @Override
    public String getValue() {
        return value;
    }
}

record EncryptedAttrPartnerIntegrationConfiguredWithKey(EncryptedAttrPartnerApiKey apiKey) {
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/confidentiality/encrypted/concept-level.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Confidentiality.Encrypted.PartnerApiKey do
  use Chronicle.Concept, type: :string
  encrypted()
end

defmodule MyApp.Events.EncryptedAttrPartnerIntegrationConfiguredWithKey do
  use Chronicle.Events.EventType, id: "encrypted-attr-partner-integration-configured-with-key"

  defstruct api_key: %MyApp.Confidentiality.Encrypted.PartnerApiKey{}
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/confidentiality/encrypted/concept-level.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { encrypted } from '@cratis/chronicle';
import { ConceptAs } from '@cratis/fundamentals';

@encrypted()
class EncryptedAttrPartnerApiKey extends ConceptAs<string> {
    static readonly valueType = String;

    constructor(value: string) {
        super(value);
    }
}

class EncryptedAttrPartnerIntegrationConfiguredWithKey {
    apiKey: EncryptedAttrPartnerApiKey = new EncryptedAttrPartnerApiKey('');
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/confidentiality/encrypted/concept-level.md)

</TabItem>
</Tabs>

## Applying to an event property

You can mark a single property directly when the value has no dedicated concept type:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
public record PartnerIntegrationConfigured(
    [Encrypted] string ApiKey,
    string PartnerName);

// When this event is written, ApiKey is encrypted. PartnerName is stored as plaintext.
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/confidentiality/encrypted/property-level.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.confidentiality.Encrypted
import io.cratis.chronicle.events.EventType

@EventType
data class EncryptedAttrPartnerIntegrationConfigured(
    @Encrypted val apiKey: String,
    val partnerName: String
)

// When this event is written, apiKey is encrypted. partnerName is stored as plaintext.
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/confidentiality/encrypted/property-level.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.confidentiality.Encrypted;
import io.cratis.chronicle.events.EventType;

@EventType
record EncryptedAttrPartnerIntegrationConfigured(
        @Encrypted String apiKey,
        String partnerName) {
}

// When this event is written, apiKey is encrypted. partnerName is stored as plaintext.
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/confidentiality/encrypted/property-level.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.EncryptedAttrPartnerIntegrationConfigured do
  use Chronicle.Events.EventType, id: "encrypted-attr-partner-integration-configured"

  defstruct [:partner_name, :api_key]

  encrypted(:api_key, :subject, "Partner API key")
end

# When this event is written, api_key is encrypted. partner_name is stored as plaintext.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/confidentiality/encrypted/property-level.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { encrypted, eventType } from '@cratis/chronicle';

@eventType()
class EncryptedAttrPartnerIntegrationConfigured {
    @encrypted() apiKey = '';
    partnerName = '';
}

// When this event is written, apiKey is encrypted. partnerName is stored as plaintext.
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/confidentiality/encrypted/property-level.md)

</TabItem>
</Tabs>

## `[PII]` and `[Encrypted]` never mix

A property cannot resolve both `[PII]` and `[Encrypted]` metadata. Chronicle applies every matching compliance handler for a property in sequence, so a value marked both ways would be encrypted first under the PII key and then again under the `[Encrypted]` key — releasing it would decrypt with the wrong key against ciphertext, which fails loudly rather than returning a wrong value.

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
// Throws PIIAndEncryptedCombinedNotSupported at schema-generation time,
// and is flagged at compile time by CHR0053.
public record EncryptedCustomerRegistered(
    [PII] [Encrypted] string SomeValue);
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/confidentiality/encrypted/pii-and-encrypted-restriction.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.compliance.Pii
import io.cratis.chronicle.confidentiality.Encrypted
import io.cratis.chronicle.events.EventType

// Throws PiiAndEncryptedCombinedNotSupported at schema-generation time.
@EventType
data class EncryptedAttrCustomerRegistered(@Pii @Encrypted val someValue: String)
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/confidentiality/encrypted/pii-and-encrypted-restriction.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.compliance.Pii;
import io.cratis.chronicle.confidentiality.Encrypted;
import io.cratis.chronicle.events.EventType;

// Throws PiiAndEncryptedCombinedNotSupported at schema-generation time.
@EventType
record EncryptedAttrCustomerRegistered(@Pii @Encrypted String someValue) {
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/confidentiality/encrypted/pii-and-encrypted-restriction.md)

</TabItem>
<TabItem label="Elixir">

```elixir
# Raises Chronicle.Confidentiality.PiiAndEncryptedCombinedNotSupported at
# schema-generation time.
#
# defmodule MyApp.Events.EncryptedAttrCustomerRegistered do
#   use Chronicle.Events.EventType, id: "encrypted-attr-customer-registered"
#   defstruct [:some_value]
#
#   pii(:some_value)
#   encrypted(:some_value)
# end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/confidentiality/encrypted/pii-and-encrypted-restriction.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { pii } from '@cratis/chronicle';
import { encrypted, eventType } from '@cratis/chronicle';

// Throws PIIAndEncryptedCombinedNotSupported at schema-generation time.
@eventType()
class EncryptedAttrCustomerRegistered {
    @pii() @encrypted() someValue = '';
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/confidentiality/encrypted/pii-and-encrypted-restriction.md)

</TabItem>
</Tabs>

Choose exactly one: `[PII]` for personal data with a lawful basis for erasure, `[Encrypted]` for an operational secret with none. In .NET, [CHR0053](/chronicle/code-analysis/chr0053/) catches the two directly-visible combinations at compile time; the check shown above is the backstop every client applies at schema-generation time.

## Why the two keys are never the same

A subject can carry both a `[PII]` value and an `[Encrypted]` value — a customer record with a `[PII]` email address and an `[Encrypted]` API key issued to that customer, for example. The two are provisioned under deliberately disjoint key identities, even though they resolve to the same compliance identity (the subject, or the event source id when none is set). Erasing the subject's PII — a lawful, routine GDPR right-to-erasure request — destroys only the PII key. The `[Encrypted]` key, and the value it protects, is untouched and remains fully readable.

This is enforced two ways:

- The key identities are disjoint by construction — nothing on the PII path ever constructs an identifier that could collide with an `[Encrypted]` value's key identity.
- `DeleteEncryptionKeyFor` and `AllowNewEncryptionKeyFor` — the two operations behind GDPR erasure — refuse an identifier that belongs to an `[Encrypted]` value, as defense in depth for the one entry point (the compliance gRPC surface) that accepts a caller-supplied identifier directly.

There is no operation anywhere that deletes an `[Encrypted]` value's key. A key protecting a secret with no data subject has no lawful basis to be destroyed on request.

## Scope

`EncryptionScope` controls the identity a value's key is provisioned under:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
public enum EncryptionScope
{
    Subject,    // per compliance identity - the default
    Namespace,  // one key shared by every value marked this way in the event store namespace
    Global      // one key shared by every value marked this way across the whole installation
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/confidentiality/encrypted/scope-enum.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.confidentiality.EncryptionScope

// EncryptionScope has exactly these three members - the when below is exhaustive over them.
fun encryptionScopeKeyBoundary(scope: EncryptionScope): String = when (scope) {
    EncryptionScope.Subject -> "per compliance identity - the default"
    EncryptionScope.Namespace -> "one key shared by every value marked this way in the event store namespace"
    EncryptionScope.Global -> "one key shared by every value marked this way across the whole installation"
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/confidentiality/encrypted/scope-enum.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.confidentiality.EncryptionScope;

// EncryptionScope has exactly these three members - the switch below is exhaustive over them.
class EncryptionScopeKeyBoundaries {
    static String describe(EncryptionScope scope) {
        return switch (scope) {
            case Subject -> "per compliance identity - the default";
            case Namespace -> "one key shared by every value marked this way in the event store namespace";
            case Global -> "one key shared by every value marked this way across the whole installation";
        };
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/confidentiality/encrypted/scope-enum.md)

</TabItem>
<TabItem label="Elixir">

```elixir
# The scope is an atom passed as the scope argument of encrypted/...:
#
#   :subject    - per compliance identity - the default
#   :namespace  - one key shared by every value marked this way in the event store namespace
#   :global     - one key shared by every value marked this way across the whole installation
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/confidentiality/encrypted/scope-enum.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { EncryptionScope } from '@cratis/chronicle';

// EncryptionScope is exported by @cratis/chronicle and has exactly these three members.
const encryptionScopeKeyBoundaries: Record<EncryptionScope, string> = {
    [EncryptionScope.Subject]: 'per compliance identity - the default',
    [EncryptionScope.Namespace]: 'one key shared by every value marked this way in the event store namespace',
    [EncryptionScope.Global]: 'one key shared by every value marked this way across the whole installation'
};
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/confidentiality/encrypted/scope-enum.md)

</TabItem>
</Tabs>

`EncryptionScope.Subject` (the default) provisions a key per compliance identity, the same way `[PII]` resolves its identity (the subject, or the event source id when none is set) — but always under a disjoint key from any `[PII]` value on the same subject.

`EncryptionScope.Namespace` provisions one key shared by every value marked that way in a given event store namespace, regardless of which document it came from — useful for a secret that belongs to the deployment rather than to any one entity, such as a webhook signing secret shared across every partner integration.

`EncryptionScope.Global` provisions one key shared by every value marked that way across the whole installation — every event store, every namespace.

Widening the scope is safe for `[Encrypted]` in a way it is never safe for `[PII]`: an operational secret has no data subject, so there is no erasure request a shared key could ever need to honor separately for one subject and not another. `[PII]` always resolves to a compliance identity and never gains a wider scope — see [Compliance](/chronicle/compliance/).

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
[Encrypted]
public record PartnerApiKeyScoped(string Value) : ConceptAs<string>(Value);       // one key per partner (EncryptionScope.Subject, the default)

[Encrypted(EncryptionScope.Namespace)]
public record PartnerWebhookSecret(string Value) : ConceptAs<string>(Value); // one key for every partner in the namespace

[Encrypted(EncryptionScope.Global)]
public record LicenseToken(string Value) : ConceptAs<string>(Value);        // one key for the whole installation
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/confidentiality/encrypted/scope-examples.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.concepts.ConceptAs
import io.cratis.chronicle.confidentiality.Encrypted
import io.cratis.chronicle.confidentiality.EncryptionScope

// One key per partner (EncryptionScope.Subject, the default).
@Encrypted
data class EncryptedAttrPartnerApiKeyScoped(override val value: String) : ConceptAs<String>

// One key for every partner in the namespace.
@Encrypted(scope = EncryptionScope.Namespace)
data class EncryptedAttrPartnerWebhookSecret(override val value: String) : ConceptAs<String>

// One key for the whole installation.
@Encrypted(scope = EncryptionScope.Global)
data class EncryptedAttrLicenseToken(override val value: String) : ConceptAs<String>
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/confidentiality/encrypted/scope-examples.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.concepts.ConceptAs;
import io.cratis.chronicle.confidentiality.Encrypted;
import io.cratis.chronicle.confidentiality.EncryptionScope;

// One key per partner (EncryptionScope.Subject, the default).
@Encrypted
record EncryptedAttrPartnerApiKeyScoped(String value) implements ConceptAs<String> {
    @Override
    public String getValue() {
        return value;
    }
}

// One key for every partner in the namespace.
@Encrypted(scope = EncryptionScope.Namespace)
record EncryptedAttrPartnerWebhookSecret(String value) implements ConceptAs<String> {
    @Override
    public String getValue() {
        return value;
    }
}

// One key for the whole installation.
@Encrypted(scope = EncryptionScope.Global)
record EncryptedAttrLicenseToken(String value) implements ConceptAs<String> {
    @Override
    public String getValue() {
        return value;
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/confidentiality/encrypted/scope-examples.md)

</TabItem>
<TabItem label="Elixir">

```elixir
# One key per partner (:subject, the default).
defmodule MyApp.Confidentiality.Encrypted.PartnerApiKeyScoped do
  use Chronicle.Concept, type: :string
  encrypted()
end

# One key for every partner in the namespace.
defmodule MyApp.Confidentiality.Encrypted.PartnerWebhookSecret do
  use Chronicle.Concept, type: :string
  encrypted(:namespace)
end

# One key for the whole installation.
defmodule MyApp.Confidentiality.Encrypted.LicenseToken do
  use Chronicle.Concept, type: :string
  encrypted(:global)
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/confidentiality/encrypted/scope-examples.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { encrypted, EncryptionScope } from '@cratis/chronicle';
import { ConceptAs } from '@cratis/fundamentals';

// One key per partner (EncryptionScope.Subject, the default).
@encrypted()
class EncryptedAttrPartnerApiKeyScoped extends ConceptAs<string> {
    static readonly valueType = String;

    constructor(value: string) {
        super(value);
    }
}

// One key for every partner in the namespace.
@encrypted(EncryptionScope.Namespace)
class EncryptedAttrPartnerWebhookSecret extends ConceptAs<string> {
    static readonly valueType = String;

    constructor(value: string) {
        super(value);
    }
}

// One key for the whole installation.
@encrypted(EncryptionScope.Global)
class EncryptedAttrLicenseToken extends ConceptAs<string> {
    static readonly valueType = String;

    constructor(value: string) {
        super(value);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/confidentiality/encrypted/scope-examples.md)

</TabItem>
</Tabs>

## Constraints

### EventSourceId is not supported

Applying `[Encrypted]` to an event source identifier throws `EncryptedNotSupportedOnEventSourceId` at runtime, and in .NET is flagged at compile time by [CHR0052](/chronicle/code-analysis/chr0052/):

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
public record PartnerId(Guid Value) : EventSourceId<Guid>(Value);

// Throws EncryptedNotSupportedOnEventSourceId
public record PartnerIntegrationConfiguredWithId(
    [Encrypted] PartnerId PartnerId,
    string ApiKey);
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/confidentiality/encrypted/event-source-id-restriction.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.concepts.EventSourceId
import io.cratis.chronicle.confidentiality.Encrypted

// This will throw EncryptedNotSupportedOnEventSourceId
@Encrypted
data class EncryptedAttrPartnerId(override val value: String) : EventSourceId
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/confidentiality/encrypted/event-source-id-restriction.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.concepts.EventSourceId;
import io.cratis.chronicle.confidentiality.Encrypted;

// This will throw EncryptedNotSupportedOnEventSourceId
@Encrypted
record EncryptedAttrPartnerId(String value) implements EventSourceId {
    @Override
    public String getValue() {
        return value;
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/confidentiality/encrypted/event-source-id-restriction.md)

</TabItem>
<TabItem label="Elixir">

```elixir
# Raises ArgumentError at compile time — encryption is not supported on a
# Chronicle.Concept declared with event_source_id: true.
#
# defmodule MyApp.Confidentiality.Encrypted.PartnerId do
#   use Chronicle.Concept, type: :uuid, event_source_id: true
#   encrypted()
# end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/confidentiality/encrypted/event-source-id-restriction.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { encrypted } from '@cratis/chronicle';

// TypeScript has no dedicated EventSourceId<T> type - the event source identifier is always
// the conventional 'eventSourceId' property. Marking it @encrypted() throws
// EncryptedNotSupportedOnEventSourceId at decoration time, for the same reason C# forbids
// [Encrypted] on EventSourceId<T>: encrypting it would make its own decryption key unfindable.
class EncryptedAttrPartnerIntegrationConfiguredWithId {
    @encrypted() eventSourceId = '';
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/confidentiality/encrypted/event-source-id-restriction.md)

</TabItem>
</Tabs>

Event source identifiers are used to correlate events. If the identifier itself is a secret, use a non-sensitive surrogate as the event source id and store the secret in a separate `[Encrypted]` property.

## Client coverage

`[Encrypted]` is available in the .NET, TypeScript, Kotlin/Java, and Elixir clients.
