{"id":16526820,"url":"https://github.com/silverbucket/secure-store-redis","last_synced_at":"2026-02-03T21:10:59.379Z","repository":{"id":24735657,"uuid":"28148006","full_name":"silverbucket/secure-store-redis","owner":"silverbucket","description":"Encrypt the data you store in redis","archived":false,"fork":false,"pushed_at":"2024-10-24T00:16:51.000Z","size":216,"stargazers_count":7,"open_issues_count":9,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-24T00:41:01.207Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/silverbucket.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-12-17T17:38:07.000Z","updated_at":"2023-07-14T01:27:25.000Z","dependencies_parsed_at":"2023-10-30T20:32:42.964Z","dependency_job_id":"10ec223a-012a-4260-ab10-facc66f461b5","html_url":"https://github.com/silverbucket/secure-store-redis","commit_stats":{"total_commits":113,"total_committers":6,"mean_commits":"18.833333333333332","dds":"0.33628318584070793","last_synced_commit":"eea216f4aa619ad1249b84334cbf1306a7928acb"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silverbucket%2Fsecure-store-redis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silverbucket%2Fsecure-store-redis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silverbucket%2Fsecure-store-redis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/silverbucket%2Fsecure-store-redis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/silverbucket","download_url":"https://codeload.github.com/silverbucket/secure-store-redis/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221342018,"owners_count":16801272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T17:30:11.766Z","updated_at":"2026-02-03T21:10:59.369Z","avatar_url":"https://github.com/silverbucket.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# secure-store-redis\n\n[![npm version](https://img.shields.io/npm/v/secure-store-redis.svg)](https://www.npmjs.com/package/secure-store-redis)\n[![license](https://img.shields.io/npm/l/secure-store-redis.svg)](https://github.com/silverbucket/secure-store-redis/blob/master/LICENSE)\n[![build](https://github.com/silverbucket/secure-store-redis/actions/workflows/compliance.yml/badge.svg)](https://github.com/silverbucket/secure-store-redis/actions)\n\nEncrypt and store data in Redis. Uses AES-256-GCM encryption with unique IVs per entry.\n\n## Installation\n\n```bash\nnpm install secure-store-redis\n```\n\n## Quick Start\n\n```typescript\nimport SecureStore, { SecretValidator } from \"secure-store-redis\";\n\nconst store = new SecureStore({\n    uid: \"myApp\",\n    secret: SecretValidator.generate(),\n    redis: { url: \"redis://localhost:6379\" },\n});\n\nawait store.connect();\nawait store.save(\"key\", { foo: \"bar\" });\nconst data = await store.get(\"key\");\nawait store.disconnect();\n```\n\n## API\n\n### Constructor\n\n```typescript\nnew SecureStore(config: SecureStoreConfig)\n```\n\n| Option             | Type                            | Required | Description                                                       |\n| ------------------ | ------------------------------- | -------- | ----------------------------------------------------------------- |\n| `uid`              | string                          | Yes      | Unique prefix for Redis keys (e.g., `\"myApp\"`, `\"myApp:sessions\"`) |\n| `secret`           | string                          | Yes      | 32-character encryption secret. Use `SecretValidator.generate()`. |\n| `redis`            | RedisOptions \\| { url: string } \\| { client: Redis \\| Cluster } | Yes      | Redis connection config or existing client                        |\n| `allowWeakSecrets` | boolean                         | No       | Bypass secret strength validation (default: false)                |\n\n### Using an Existing Redis Client\n\nYou can pass your own ioredis `Redis` or `Cluster` client instead of connection options. This enables connection sharing, pre-configured clients, and integration with existing Redis infrastructure.\n\n```typescript\nimport { Redis } from \"ioredis\";\nimport SecureStore, { SecretValidator } from \"secure-store-redis\";\n\nconst redis = new Redis({ host: \"localhost\", port: 6379 });\n\nconst store = new SecureStore({\n    uid: \"myApp\",\n    secret: SecretValidator.generate(),\n    redis: { client: redis },\n});\n\nawait store.connect();\nawait store.save(\"key\", \"value\");\nawait store.disconnect();\n\n// Your client is still connected - you manage its lifecycle\nconsole.log(redis.status); // \"ready\"\nawait redis.quit();\n```\n\n#### Redis Cluster\n\n```typescript\nimport { Cluster } from \"ioredis\";\nimport SecureStore, { SecretValidator } from \"secure-store-redis\";\n\nconst cluster = new Cluster([\n    { host: \"node1\", port: 6379 },\n    { host: \"node2\", port: 6379 },\n]);\n\nconst store = new SecureStore({\n    uid: \"myApp\",\n    secret: SecretValidator.generate(),\n    redis: { client: cluster },\n});\n\nawait store.connect();\n```\n\n#### Sharing Connections\n\nMultiple SecureStore instances can share a single Redis connection:\n\n```typescript\nconst redis = new Redis();\n\nconst sessionsStore = new SecureStore({\n    uid: \"sessions\",\n    secret: sessionSecret,\n    redis: { client: redis },\n});\n\nconst cacheStore = new SecureStore({\n    uid: \"cache\",\n    secret: cacheSecret,\n    redis: { client: redis },\n});\n\nawait sessionsStore.connect();\nawait cacheStore.connect();\n\n// Both stores use the same connection\n// Disconnecting either store does NOT close the Redis client\n```\n\n**Important notes:**\n- When using an external client, `disconnect()` will NOT close the Redis connection - you are responsible for calling `redis.quit()` when done\n- The client can be in any connectable state (ready, connecting, or lazyConnect); `connect()` will wait for it to be ready\n- If the client is already closed, `connect()` will throw a `ConnectionError`\n\n### Methods\n\n#### `connect(): Promise\u003cvoid\u003e`\n\nConnect to Redis. Must be called before other operations.\n\n#### `save\u003cT\u003e(key: string, data: T, postfix?: string): Promise\u003cvoid\u003e`\n\nEncrypt and store data.\n\n#### `get\u003cT\u003e(key: string, postfix?: string): Promise\u003cT | null\u003e`\n\nRetrieve and decrypt data. Returns `null` if not found or decryption fails.\n\n#### `delete(key: string, postfix?: string): Promise\u003cnumber\u003e`\n\nDelete data. Returns count of deleted keys.\n\n#### `disconnect(client?: Redis): Promise\u003cvoid\u003e`\n\nClose Redis connection. Optionally pass a specific Redis client to disconnect.\n\n#### `namespace\u003cTSchema\u003e(name: string): TypedNamespace\u003cTSchema\u003e`\n\nCreate a typed namespace for organizing data. See [Namespaces](#namespaces) for details.\n\n### Properties\n\n- `client: RedisClient | undefined` - The underlying ioredis client (Redis or Cluster)\n- `isConnected: boolean` - Connection status\n\n## Namespaces\n\nNamespaces allow you to organize data with type-safe operations. Each namespace acts as an isolated partition within the same store.\n\n```typescript\nconst store = new SecureStore({\n    uid: \"myApp\",\n    secret: SecretValidator.generate(),\n    redis: { url: \"redis://localhost:6379\" },\n});\nawait store.connect();\n\n// Create a typed namespace\ninterface UserSchema {\n    profile: { name: string; age: number };\n    settings: { theme: string; notifications: boolean };\n}\n\nconst users = store.namespace\u003cUserSchema\u003e(\"users\");\n\n// Type-safe operations\nawait users.save(\"profile\", { name: \"John\", age: 30 });\nawait users.save(\"settings\", { theme: \"dark\", notifications: true });\n\nconst profile = await users.get(\"profile\"); // { name: string; age: number } | null\nconst settings = await users.get(\"settings\");\n\nawait users.delete(\"profile\");\n```\n\n### Namespace Methods\n\nEach namespace provides the same core operations as the store:\n\n- `get\u003cK\u003e(key: K): Promise\u003cT[K] | null\u003e`\n- `save\u003cK\u003e(key: K, data: T[K]): Promise\u003cvoid\u003e`\n- `delete\u003cK\u003e(key: K): Promise\u003cnumber\u003e`\n\n### Namespace Isolation\n\nData in different namespaces is completely isolated:\n\n```typescript\nconst users = store.namespace(\"users\");\nconst sessions = store.namespace(\"sessions\");\n\nawait users.save(\"data\", \"user-data\");\nawait sessions.save(\"data\", \"session-data\");\n\nawait users.get(\"data\");    // \"user-data\"\nawait sessions.get(\"data\"); // \"session-data\"\nawait store.get(\"data\");    // null (root store is separate)\n```\n\n## SecretValidator\n\nUtility class for generating and validating encryption secrets.\n\n### Methods\n\n#### `generate(length?: number): string`\n\nGenerate a cryptographically secure secret. Defaults to 32 characters if no length specified.\n\n#### `validate(secret: string): { valid: boolean; reason?: string }`\n\nValidate secret strength against security requirements.\n\n### Validation Rules\n\nSecrets must:\n- Be exactly 32 characters\n- Have sufficient entropy (Shannon entropy ≥ 4.0)\n- Not contain weak patterns (repeated chars, sequential numbers, etc.)\n- Contain at least 3 of: uppercase, lowercase, numbers, special characters\n\n## Error Handling\n\nAll errors extend `SecureStoreError` and include a `code` property for programmatic handling.\n\n```typescript\nimport {\n    SecureStoreError,\n    ConnectionError,\n    EncryptionError,\n    ValidationError,\n} from \"secure-store-redis\";\n\ntry {\n    await store.connect();\n} catch (err) {\n    if (err instanceof ConnectionError) {\n        console.error(`Connection failed (${err.code}):`, err.message);\n        // err.code === \"CONNECTION_ERROR\"\n    }\n}\n```\n\n| Error Class       | Code                 | When Thrown                          |\n| ----------------- | -------------------- | ------------------------------------ |\n| `ConnectionError` | `CONNECTION_ERROR`   | Redis connection failures            |\n| `EncryptionError` | `ENCRYPTION_ERROR`   | Encryption/decryption failures       |\n| `ValidationError` | `VALIDATION_ERROR`   | Invalid configuration, keys, or data |\n\n## Security Best Practices\n\n- Store secrets in environment variables, not code\n- Use `SecretValidator.generate()` for cryptographically secure secrets\n- Enable Redis authentication and TLS in production\n- Use unique `uid` values per application/environment to prevent data collisions\n- Rotate secrets periodically (requires re-encryption of existing data)\n\n## Migration from v3.x\n\n### Breaking Changes\n\n| Change               | Migration                                  |\n| -------------------- | ------------------------------------------ |\n| `uid` required       | Add explicit `uid` to constructor          |\n| `secret` required    | Use `SecretValidator.generate()` or provide your own |\n| `connect()` required | Call `await store.connect()` before use    |\n| AES-256-GCM          | Re-encrypt existing data (format changed)  |\n| Secret validation    | Use strong secrets or set `allowWeakSecrets: true` |\n\n### Example\n\n```typescript\n// v3.x\nconst store = new SecureStore({ redis: { url: \"...\" } });\nawait store.init();\n\n// v4.0\nconst store = new SecureStore({\n    uid: \"myApp\",\n    secret: SecretValidator.generate(),\n    redis: { url: \"...\" },\n});\nawait store.connect();\n```\n\n## TypeScript\n\nFull TypeScript support with exported types:\n\n```typescript\nimport SecureStore, {\n    SecureStoreConfig,\n    TypedNamespace,\n    RedisClient,\n    SecretValidator,\n    SecureStoreError,\n    ConnectionError,\n    EncryptionError,\n    ValidationError,\n} from \"secure-store-redis\";\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsilverbucket%2Fsecure-store-redis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsilverbucket%2Fsecure-store-redis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsilverbucket%2Fsecure-store-redis/lists"}