Per-actor policies
The object-storage backend can have store-level defaults for compression + encryption. But sometimes specific actors need different settings:
- A sensitive actor needs encryption (others don’t).
- A large-state actor benefits from brotli (others use gzip).
- A small-state actor opts out of compression.
Override per-actor by implementing the compression() /
encryption() methods:
class SensitiveActor extends DurableStateActor<Cmd, State> { protected encryption() { return { keyRing: sensitiveKeyRing }; }
protected compression() { return { algorithm: 'gzip' as const, level: 9 }; }}The framework calls these on each persist/load and applies the returned config.
Default vs override
Section titled “Default vs override”// Store-level config — applies to every actor unless overridden:const store = new ObjectStorageDurableStateStore({ backend, compression: { algorithm: 'gzip', level: 6 }, encryption: { keyRing: defaultKeyRing },});
// Per-actor: overrideclass MyActor extends DurableStateActor<...> { protected compression() { return { algorithm: 'none' as const }; } // encryption() not overridden → uses defaultKeyRing}Each method is independently overridable:
- Return your own config → that config applies.
- Don’t override → store’s default applies.
- Return
undefinedfrom the method → also store’s default.
Use cases
Section titled “Use cases”Per-actor encryption
Section titled “Per-actor encryption”class CreditCardActor extends DurableStateActor<...> { protected encryption() { return { keyRing: pciKeyRing }; // separate key ring for PCI scope }}
class GenericActor extends DurableStateActor<...> { // No override → uses store default (no encryption, or generic keyRing)}PCI / HIPAA / GDPR scope: data classified as sensitive uses a separate master key from non-sensitive. Compromise of one keyRing doesn’t expose the other.
Per-actor compression
Section titled “Per-actor compression”class ChatTranscriptActor extends DurableStateActor<...> { // Large text state — brotli wins meaningfully protected compression() { return { algorithm: 'brotli' as const, level: 8 }; }}
class CounterActor extends DurableStateActor<...> { // Tiny state — compression overhead exceeds savings protected compression() { return { algorithm: 'none' as const }; }}Match the compression to the data shape. Default gzip is a safe middle ground; per-actor tuning extracts more.
What happens on persist
Section titled “What happens on persist”DurableStateActor.persist(state) ↓serializer.toBinary(state) → JSON or CBOR bytes ↓this.compression() → applies if not 'none' ↓this.encryption() → applies if set ↓backend.put(key, ciphertext, opts)The framework looks up the actor’s overrides at each persist — so even if you change the override values dynamically (rare), new writes use new settings.
What happens on load
Section titled “What happens on load”DurableStateActor.preStart ↓backend.get(key) → bytes + metadata ↓detect encryption from metadata (key-id) → decrypt ↓detect compression from metadata (Content-Encoding) → decompress ↓serializer.fromBinary → stateReads use the metadata stored with the object, not the
actor’s current overrides. An object encrypted under
pciKeyRing is decrypted via pciKeyRing whether or not the
actor’s current encryption() says so.
This means: rotating an actor’s policy doesn’t break old data as long as the necessary keys / algorithms are available.
Migration when changing policies
Section titled “Migration when changing policies”// Old: no encryption.// New: encryption under pciKeyRing.class CreditCardActor extends DurableStateActor<...> { protected encryption() { return { keyRing: pciKeyRing }; }}Old (unencrypted) reads work — no metadata → plaintext path. New writes encrypt. Eventually:
- A re-encryption sweep filtered to this actor’s persistenceIds migrates the rest.
See key rotation.
Working with the snapshot store
Section titled “Working with the snapshot store”class Account extends PersistentActor<Cmd, Event, State> { // Per-actor compression for snapshots: protected compression() { return { algorithm: 'brotli' as const }; }
// Per-actor encryption for snapshots: protected encryption() { return { keyRing: accountKeyRing }; }}Same methods apply when the snapshot store is backed by object-storage. Persistent actors and durable-state actors both honor them.
Where to next
Section titled “Where to next”- Object storage overview — the bigger picture.
- Compression — algorithm + level details.
- Encryption — encryption setup.
- Snapshot store backend — for snapshots in object storage.