npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

com.echo.security

v2.5.0

Published

Encrypts app-owned local data under Application.persistentDataPath: player save files, cached config JSON, downloaded level/song data, and specific PlayerPrefs entries. AES-256-CBC + HMAC-SHA256 (encrypt-then-MAC), per-device key custody via iOS Keychain

Readme

Echo Security

Package: com.echo.security v1.0.1 Unity: 2022.3+ Namespace: Echo.Security (assembly Echo.Security) Dependencies: com.echo.foundation, com.cysharp.unitask

Encrypts app-owned data across three independent layers, each gated by its own scripting define so a consumer can adopt only what it needs:

| Layer | Define | Covers | |---|---|---| | Secure Local Storage | SECURE_LOCAL_STORAGE | Player save files, cached config JSON, downloaded level/song data, and specific PlayerPrefs entries under Application.persistentDataPath. | | Secure Addressables | SECURE_ADDRESSABLES | Gates a consuming project's own Addressables bundle-encryption Editor tool; this package's EncryptedAssetBundleProvider runtime piece ships staged, not wired in. | | Secure Resources | SECURE_RESOURCES | Resources/StreamingAssets content baked directly into the player build (no persistent-path data involved). | | Secure ScriptableObjects | none (manual tool only) | ScriptableObjects that bake sensitive data into their own serialized fields - not gated by a define since it's not encryption, see below. |

All three default OFF (fail-insecure by design) and share the same primitives (SecureCryptoEngine's AES-256-CBC + HMAC-SHA256 encrypt-then-MAC) but are otherwise independent — enabling one does not enable the others. Zero Orchestra coupling — safe to drop into any project, with or without Orchestra installed.

See Security.md for the full rules this module enforces (scope, threat model, key custody, migration behavior).

Secure Local Storage

| Type | Purpose | |---|---| | SecureCryptoEngine | AES-256-CBC + HMAC-SHA256 encrypt-then-MAC primitives, constant-time tag comparison. | | SecureEnvelope | Versioned header format wrapping ciphertext for save files. | | SecureFileDataRepository<T> | Drop-in encrypted replacement for FileDataRepository<T> — same atomic-write/retry/backoff contract. | | SecureRawFileStore | Encrypted loose-file store (cached config JSON, downloaded level/song data); .sec sibling of the original path. | | SecurePlayerPrefs | Encrypted Set/Get Int/Float/String drop-in for PlayerPrefs, with legacy-plaintext migration. | | SecurePlaintextMigrator | One-shot, non-destructive plaintext → encrypted save migration. | | SecureKeyStore + ISecureKeyProvider | Per-device key custody: iOS Keychain / Android Keystore / editor-only dev-key fallback. | | SecureStorageConfig | Compile-time switches for all three layers (SECURE_LOCAL_STORAGE, SECURE_ADDRESSABLES, SECURE_RESOURCES), default OFF — fail-insecure by design. | | SecureStorageCategory | Logical category label used for key derivation / logging. |

using Echo.Security;

// Save files (replaces FileDataRepository<T>)
var repo = new SecureFileDataRepository<MyData>("my_data.json", SecureStorageCategory.PlayerData);
MyData data = await repo.LoadAsync();
await repo.SaveAsync(data);

// Sensitive PlayerPrefs entries
SecurePlayerPrefs.SetInt("ab_variant_bucket", 3);
int bucket = SecurePlayerPrefs.GetInt("ab_variant_bucket", 0);

Encryption is OFF until SECURE_LOCAL_STORAGE is added — toggle it from Echo > Security > Encryption Settings, which sets the define across Standalone, iOS, and Android at once.

Never call File.*/PlayerPrefs.* directly on data routed through this layer — go through SecureFileDataRepository<T>, SecureRawFileStore, or SecurePlayerPrefs so key derivation, atomic writes, and migration stay consistent.

Secure Addressables

Storage/BundleEncryptionKey.cs and Storage/EncryptedAssetBundleProvider.cs stage Addressables bundle-encryption support ahead of an eventual Addressables adoption. The provider ships fully commented out and is not wired into anything until the host project adds com.unity.addressables and deliberately wires a BundledAssetGroupSchema.AssetBundleProviderType to it. SECURE_ADDRESSABLES gates a consuming project's own Addressables bundle-encryption Editor tool (this package has no such tool of its own — see SecureStorageConfig.AddressableBundleEncryptionEnabled's doc comment).

Secure Resources

| Type | Purpose | |---|---| | SecureResourceCrypto | Transparent decrypt for Resources TextAssets — magic-byte detection, shared build-embedded key. | | SecureStreamingAssetsReader | StreamingAssets counterpart to SecureResourceCrypto (UnityWebRequest on Android, File elsewhere). | | SecureResourceEncryptionConfig | Per-project ScriptableObject listing which Resources/StreamingAssets folders to encrypt (drag-and-drop targets). |

To encrypt Resources/StreamingAssets content baked into the player (which has no bundle-provider indirection point the way Addressables does, so it ships plaintext-readable otherwise): add the SECURE_RESOURCES define via Echo > Security > Encryption Settings, create a SecureResourceEncryptionConfig asset (Assets > Create > Echo > Security > Resource Encryption Config), drag in the folders/files to cover, then run Echo > Security > Encrypt Resources & StreamingAssets (or the Encrypt Now button on the config asset's own Inspector — same action, no menu-hunting required). Readers (SecureResourceCrypto.DecodeText, SecureStreamingAssetsReader) fall back to plaintext transparently for any file the tool hasn't touched, so this is safe to adopt incrementally.

CI builds encrypt automatically — no manual step required. SecureResourceBuildPreprocessor (IPreprocessBuildWithReport) runs the same encrypt pass before every Player build whenever SECURE_RESOURCES is on, so a CI pipeline that only sets the define and builds still ships encrypted content. If the define is on but no SecureResourceEncryptionConfig exists (or one with zero targets configured), the build fails loudly instead of shipping plaintext silently — this is exactly the misconfiguration that previously let a project turn the define on and ship unencrypted resources with no error. Encryption is idempotent, so this pre-build pass is safe to run even when local Editor use already encrypted everything.

SecureResourceCrypto/SecureStreamingAssetsReader also decode images at runtime via DecodeTexture/ReadTextureAsync (decrypt-if-encrypted, then Texture2D.LoadImage), cached the same way as their text counterparts.

Secure ScriptableObjects

A ScriptableObject that bakes sensitive data into its own serialized fields ships that data in the Resources bundle regardless of SECURE_RESOURCES - Unity's own engine-level ScriptableObject deserializer reads the .asset file directly, with no hook for SecureResourceCrypto's magic-byte decrypt trick to sit in front of. This isn't fixable as "encryption" the way JSON is; the fix is ensuring the SO never ships with real sensitive data baked in, deferring to an already-encrypted source (typically JSON) reloaded at runtime instead.

| Type | Purpose | |---|---| | ISecureScriptableObject | Contract (HasSensitiveData() / ClearSensitiveData()) any ScriptableObject implements to declare how to wipe its own baked-in sensitive fields. |

using Echo.Security;

public class MyConfigSO : ScriptableObject, ISecureScriptableObject
{
    public MyConfigData Data; // populated by a "Parse from TextAsset"-style Editor button,
                               // and reloaded at runtime from an encrypted JSON source before
                               // anything reads it - safe to clear
    public Texture2D Icon;    // NOT reconstructible from JSON - never clear this

    public bool HasSensitiveData() => Data?.SomeList?.Count > 0;
    public void ClearSensitiveData() => Data = new MyConfigData();
}

Two ways to trigger a clear:

  1. Manual, project-wide. Run Echo/Security/Clear Secure ScriptableObject Data (pre-release) - finds every ISecureScriptableObject asset in the whole project and clears each.
  2. Automatic, config-scoped - no separate define. Drag an ISecureScriptableObject asset into a SecureResourceEncryptionConfig's Resources Targets (the same config Secure Resources already uses). SecureResourceEncryptionTool.EncryptAll detects it, skips it in the normal file-encrypt pass (encrypting a ScriptableObject's raw bytes would corrupt it), and calls ClearSensitiveData() on it instead - as part of the ordinary SECURE_RESOURCES-gated pass, both interactively and in SecureResourceBuildPreprocessor's CI path. Dragging the asset in there IS the opt-in.

Re-populating a cleared asset for continued local Editor work is on whatever mechanism the implementer already has (e.g. a "Parse from TextAsset" button, matching the LoadFromJson pattern SecureFileDataRepository<T> consumers already use) - unlike JSON encrypt/decrypt, there's no generic guaranteed-safe way to automatically restore a cleared SO. Don't drag a not-yet-safe-to-clear SO into a config that a local dev build target also builds with SECURE_RESOURCES on.

Images and audio in Resources

JSON works out of the box because Resources/ imports it as a TextAsset — Unity's TextAsset importer never parses content, it just stores raw bytes, so SecureResourceEncryptionTool can rewrite the file in place and SecureResourceCrypto transparently decrypts it back.

Images and audio dropped into Resources/ normally do NOT work the same way. Unity's TextureImporter/AudioImporter parses the file at import time and bakes it into engine-native Texture2D/AudioClip data inside the build's serialized asset data — not a byte-for-byte copy of the original file. Encrypting a normally-imported .png/.mp3 in place only corrupts the Editor's own reimport; by the time your code touches the asset at runtime, Unity already parsed the plaintext at import/build time, so encryption never gets a chance to matter.

Workaround: give the file an extension with no registered importer (.bytes is the conventional choice) so Unity imports it as a TextAsset instead of a native Texture/AudioClip — the same trick that makes JSON work.

For audio specifically, SecureResourceEncryptionTool.EncryptAll does this conversion for you: any covered .ogg/.mp3/.wav/.aiff/.aif is renamed to .bytes in place (content unchanged) as an automatic pre-pass, before the normal encrypt pass runs — no manual rename needed, on every call site (interactive menu, SecureResourceEncryptionConfigEditor's own Convert N Audio File(s) to .bytes button if you'd rather convert ahead of time without also encrypting yet, and SecureResourceBuildPreprocessor's CI path alike). Images have no equivalent auto-convert yet — give a covered .png/.jpg the .bytes extension yourself first. Then:

  1. SecureResourceEncryptionTool encrypts it in place exactly like JSON (it covers every file under a target regardless of extension).
  2. At runtime, reconstruct the asset manually from the decrypted bytes:
    • Image: SecureResourceCrypto.DecodeTexture(asset) (or DecodeTexture(byte[]) for StreamingAssets/ReadTextureAsync) — decrypts if encrypted, then Texture2D.LoadImage.
    • Audio: no built-in decoder from raw bytes straight to AudioClipLoadFromMemoryAsync only exists for AssetBundles. Realistically prefer StreamingAssets for audio (below) or accept a decrypt-to-temp-file + UnityWebRequestMultimedia.GetAudioClip hop (exactly what a consumer's own downloaded-audio decrypt path for persistentDataPath content likely already does — reuse that helper rather than writing a second copy).

StreamingAssets doesn't have this problem at all — files there are never parsed by an importer (Unity copies them byte-identical into the build regardless of extension), so any file type, including native .png/.mp3 extensions, can be covered by the encrypt tool with no workaround needed. Prefer StreamingAssets over the Resources .bytes trick for images/audio where the choice is open to you.

Notes

  • This module does not defend against a rooted/jailbroken device with a native debugger attached, and is not a substitute for server-side validation of purchases and economy state.