Browser persistence should not force your application code to handle ciphertext, storage envelopes, or reload restoration. Chapter 11 shows how AES-256-GCM encrypts finalized Feature State at the persistence boundary while the FeatureCell continues to work with plain in-memory values.
This is also a security boundary lesson. Encryption and persistence are Pro capabilities: development evaluation can use the documented licensing bypass, but production use requires a valid Pro license and must not rely on that bypass. The chapter also makes clear what browser-side encryption does not provide: authorization, XSS protection, secret confidentiality, or cross-device recovery.
Key takeaway: This tutorial starts with the completed Chapter 7 project and adds the Pro Encrypt and Persist behaviors, a stable teaching salt, and tab-scoped Session Storage persistence.
Why Persistence Needs a Deliberate Threat Model
Keeping a local workspace across a reload is useful. It can preserve non-sensitive preferences, a draft, or a short-lived session view without making the user rebuild the same context every time. But persistence gives data a longer life than the current page, so the storage lifetime and the value of the data have to justify each other.
Chapter 11 places encryption at the boundary between finalized application State and the persistence mechanism. The application continues to read and write ordinary values through its FeatureCell boundary. The persisted envelope is encrypted before it reaches Session Storage, and the value is decrypted before it returns to the application.
That boundary protects the confidentiality and integrity of the persisted envelope when an attacker obtains storage alone but does not have the key-derived secret. It does not turn browser JavaScript into a confidential key store. A successful same-origin script, XSS, malicious extension, or compromised browser runtime can still read plain in-memory State and any secret available to the client.
Warning: Encryption and Persistence behaviors require a valid Pro license for production use. The tutorial uses
devMode: truewithbypassLicensing: trueonly for local development evaluation. Do not carry that bypass into production, and do not paste production secrets or sensitive user data into the teaching example.
Register Encrypt and Persist at the Pipeline Boundary
The application configuration registers the two Pro behaviors beside the existing collection behavior. The tutorial's behavior list is intentionally visible, but declaration order does not redefine the behaviors' fixed pipeline stages. Encryption transforms the finalized outbound value before Persist writes its envelope.
For the framework-agnostic core API, the FeatureCell is configured with both behaviors:
import { FeatureCell } from '@sdux-vault/core';
import {
withAes256EncryptBehavior,
withArrayByIdMergeBehavior,
withSessionStoragePersistBehavior
} from '@sdux-vault/addons';
export const characterCell = FeatureCell<readonly StarWarsCharacter[]>(
{
key: 'star-wars-character',
initialState: STAR_WARS_CHARACTERS
},
[
withArrayByIdMergeBehavior,
withAes256EncryptBehavior,
withSessionStoragePersistBehavior
]
);
The bypass is an evaluation convenience, not a licensing model for shipped software. In production, the Pro behaviors need a valid license. That distinction belongs in the setup instructions because developers should know whether they are exploring a capability or configuring a production application.
Keep Plain State Inside the FeatureCell
Encryption is useful here because it stays out of the feature's domain logic. The service still configures its filters, reducers, collection identity, and initialization. It also supplies the secret configuration before initialization. Nothing in the component has to serialize a character, assemble an envelope, or decrypt a reload result.
The core configuration uses the same FeatureCell created above:
const EXAMPLE_AES256_SALT = new Uint8Array([
0x53, 0x44, 0x75, 0x58, 0x2d, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x2d, 0x54, 0x75,
0x74, 0x6f, 0x72
]);
characterCell
.setAes256Secret({
aes256Secret: 'sdux-vault-tutorial-only-secret',
salt: EXAMPLE_AES256_SALT,
iterations: 250_000
})
.initialize();
The call to setAes256Secret() belongs before initialize(). The stable 16-byte salt in the tutorial makes the exercise repeatable, but it is not a password or a confidential key. A stable salt supports key derivation for ciphertext that must be read later; it does not solve production key management.
Warning: In production, supply the salt dynamically from a secure, durable source that your application controls. The stable salt shown here exists to make the lesson repeatable and the reload behavior easy to see; it is not a production secret-management strategy. Keep the salt stable for data that must be decrypted later, protect the secret that derives the encryption key, and choose storage and key-management policies that match your threat model.
Plain in memory, protected at rest: Trusted application code continues to receive plain Feature State. The encryption behavior protects the value at the persistence boundary rather than forcing every consumer to understand the storage format.
Stable Salt, Reload Restoration, and Initial State Priority
A stable salt matters because the persisted ciphertext must be decryptable after the page reloads. The tutorial also uses a stable storage key so the same FeatureCell can find its Session Storage envelope again. The AES-GCM initialization vector is generated for encryption and stored in the envelope; it is not a replacement for the stable salt.
On startup, persisted State takes priority over initialState. That is the behavior users expect from restoration: the initial characters seed a feature that has never been persisted, but they should not overwrite a valid session snapshot on every reload. The FeatureCell still presents the restored value as ordinary State after decryption.
| Concern | Owner | Responsibility |
|---|---|---|
| Feature State | FeatureCell | Expose plain values to trusted application code. |
| Encryption | AES-256 Encrypt behavior | Protect the finalized value before persistence. |
| Storage lifetime | Session Storage Persist behavior | Restore the encrypted envelope across reloads in the current tab. |
| Threat model | Application team | Choose data, secrets, retention, authorization, and recovery policies. |
Session Storage Is Tab-Scoped, Not Cross-Device Recovery
Session Storage gives this tutorial a clear and limited persistence story. Reload the page in the current browser tab and the encrypted envelope can be restored. Open another tab and it does not become a shared source of truth. Close the tab and the session normally ends, although browser crash recovery and session-restoration features can affect that lifetime.
That scope is often a good fit for temporary local work, but it is not a substitute for a server, an account-backed data model, or a cross-device recovery plan. Encryption also does not provide authorization, server security, data minimization, or a retention policy. Those are separate application decisions.
Warning: A secret bundled into browser JavaScript is available to the browser runtime. Use the tutorial secret and salt to learn the boundary, not as a production secret-management strategy.
What Client-Side Encryption Does Not Protect
The most valuable part of Chapter 11 is its list of non-goals. The encrypted envelope is not the same thing as an encrypted application runtime. If hostile code executes successfully in the same origin, it can observe plain State before encryption or use any client-available secret. If the feature needs authorization, the server and identity boundary must provide it.
The chapter's recovery path is similarly intentional. Malformed storage data should not become partial application State. A failed decryption should be normalized and the relevant storage key cleared, followed by a fresh authoritative load. Recovery is a new decision, not a reason to silently trust corrupted or unauthenticated data.
- Encryption does not prevent XSS or malicious extensions.
- Encryption does not hide plain State from trusted application code.
- Encryption does not create authorization or server security.
- Session Storage does not provide cross-tab or cross-device recovery.
- A tutorial secret and salt are not production key management.
Deeper Dive
Chapter 11 is a practical lesson in keeping persistence concerns at the boundary. Read the Encrypt and Persist tutorial chapter for the complete Angular walkthrough, source tabs, and StackBlitz project. Before using the Pro behaviors in production, replace the tutorial licensing bypass with a valid Pro license and make the secret, storage lifetime, authorization, retention, and recovery decisions appropriate for your data.
Top comments (0)