vectra-client 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a0a1971b5aaa4e71cce684542a9baa8b5c43a47b5e9faa8231bb3dbf5ebe2d1
4
- data.tar.gz: d80c394c3cfbc4b94e42e649c26333238feb0e946570e6cb918c13c73d491d37
3
+ metadata.gz: 2d3586fe349f6746ba939c2596bf5b1110551a8e7ed16a8dc6581eaa2f91af81
4
+ data.tar.gz: 11d141983fba57e97fc0d96792f75e8e2e50eac453671ddb25ce8456002acac2
5
5
  SHA512:
6
- metadata.gz: e422697e86cd942b25d49b88a15254789754dea5646fe5407989c4915f4bb4fc3f36a84f19d38a2409bf684c7ce67bde02d012590e909cb980777e35beabf1c1
7
- data.tar.gz: 5ef94bacfffe1ce6f67faeaa92febf739b498a14675661612058104dc7d1119e59dc0284a1c32338b08cd73c8cc4aac63e0be249c8250e9d78699dd45eb93b3d
6
+ metadata.gz: 6396f52e61633d9b2eed5502edbc80f8c001d85aea638afad4ff5c377e7d141ca80dd6198ace23808324411355924b2e653aa661e157fa4ae33c0b905bf31e4d
7
+ data.tar.gz: 1581ca62c63111caaf6fc26ced7503a101ceb30c8a0531b3fa454a0ac10376446f5a358dfbb29f045de9ba799114803af02b6a021828debc8df14cab129a5147
data/CHANGELOG.md CHANGED
@@ -43,7 +43,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
43
43
  - Thread-safe implementation
44
44
  - Configurable failure/success thresholds
45
45
 
46
- ## [0.3.0] - 2025-01-08
46
+ - **Credential Rotation** (`Vectra::CredentialRotator`)
47
+ - Zero-downtime API key rotation
48
+ - Pre-rotation validation
49
+ - Rollback support
50
+ - Multi-provider rotation manager
51
+ - Per-provider rotation tracking
52
+
53
+ - **Audit Logging** (`Vectra::AuditLog`)
54
+ - Structured audit events for compliance
55
+ - Access, authentication, authorization logging
56
+ - Configuration change tracking
57
+ - Credential rotation audit trail
58
+ - Automatic API key sanitization
59
+ - Global audit logging support
60
+
61
+ ## [v0.3.2](https://github.com/stokry/vectra/tree/v0.3.2) (2026-01-08)
62
+
63
+ [Full Changelog](https://github.com/stokry/vectra/compare/v0.3.1...v0.3.2)
64
+
65
+ ### Fixed
66
+
67
+ - Audit log keyword parameter ordering and optional fields now align with linting and usage expectations.
68
+ - Credential rotation utilities use symbol procs and string interpolation while keeping the existing `switch_to_secondary` API.
69
+
70
+ ## [v0.3.1](https://github.com/stokry/vectra/tree/v0.3.1) (2026-01-08)
71
+
72
+ [Full Changelog](https://github.com/stokry/vectra/compare/v0.3.0...v0.3.1)
73
+
74
+ ## [v0.3.0](https://github.com/stokry/vectra/tree/v0.3.0) (2026-01-08)
75
+
76
+ [Full Changelog](https://github.com/stokry/vectra/compare/v0.2.2...v0.3.0)
47
77
 
48
78
  ### Added
49
79
 
@@ -88,7 +118,13 @@ New configuration options:
88
118
 
89
119
  - Added `concurrent-ruby ~> 1.2` for thread-safe operations
90
120
 
91
- ## [0.2.1] - 2025-01-08
121
+ ## [v0.2.2](https://github.com/stokry/vectra/tree/v0.2.2) (2026-01-08)
122
+
123
+ [Full Changelog](https://github.com/stokry/vectra/compare/v0.2.1...v0.2.2)
124
+
125
+ ## [v0.2.1](https://github.com/stokry/vectra/tree/v0.2.1) (2026-01-08)
126
+
127
+ [Full Changelog](https://github.com/stokry/vectra/compare/v0.2.0...v0.2.1)
92
128
 
93
129
  ### Added
94
130
 
@@ -113,9 +149,11 @@ New configuration options:
113
149
  - ✅ Pinecone - Fully implemented
114
150
  - ✅ pgvector (PostgreSQL) - Fully implemented
115
151
  - ✅ Qdrant - Fully implemented
116
- - 🚧 Weaviate - Stub implementation (planned for v0.3.0)
152
+ - 🚧 Weaviate - Stub implementation (planned for v0.4.0)
153
+
154
+ ## [v0.2.0](https://github.com/stokry/vectra/tree/v0.2.0) (2026-01-08)
117
155
 
118
- ## [0.2.0] - 2025-01-08
156
+ [Full Changelog](https://github.com/stokry/vectra/compare/v0.1.3...v0.2.0)
119
157
 
120
158
  ### Added
121
159
 
@@ -138,7 +176,13 @@ New configuration options:
138
176
  - Added IMPLEMENTATION_GUIDE.md for developers implementing new features
139
177
  - Added NEW_FEATURES_v0.2.0.md with migration guide
140
178
 
141
- ## [0.1.1] - 2025-01-07
179
+ ## [v0.1.3](https://github.com/stokry/vectra/tree/v0.1.3) (2026-01-07)
180
+
181
+ [Full Changelog](https://github.com/stokry/vectra/compare/v0.1.1...v0.1.3)
182
+
183
+ ## [v0.1.1](https://github.com/stokry/vectra/tree/v0.1.1) (2026-01-07)
184
+
185
+ [Full Changelog](https://github.com/stokry/vectra/compare/0ab2ea7b42d7fbf0b540b24889cc2b24254fef2e...v0.1.1)
142
186
 
143
187
  ### Added
144
188
 
@@ -157,37 +201,6 @@ New configuration options:
157
201
  - Updated gemspec description to include pgvector
158
202
  - Added `pg` gem as development dependency
159
203
 
160
- ## [0.1.0] - 2024-XX-XX
161
-
162
- ### Added
163
-
164
- - Initial release
165
- - Pinecone provider with full support for:
166
- - Vector upsert, query, fetch, update, delete operations
167
- - Index management (list, describe, create, delete)
168
- - Namespace support
169
- - Metadata filtering
170
- - Configuration system with global and per-client options
171
- - Automatic retry logic with exponential backoff
172
- - Comprehensive error handling with specific error classes:
173
- - `AuthenticationError` - API key issues
174
- - `RateLimitError` - Rate limiting with retry-after
175
- - `NotFoundError` - Resource not found
176
- - `ValidationError` - Invalid request parameters
177
- - `ServerError` - Server-side errors
178
- - `ConnectionError` - Network issues
179
- - `TimeoutError` - Request timeouts
180
- - `Vector` class for vector representation with:
181
- - Cosine similarity calculation
182
- - Euclidean distance calculation
183
- - Metadata support
184
- - `QueryResult` class with Enumerable support:
185
- - Score filtering
186
- - Easy iteration
187
- - Result statistics
188
- - Full RSpec test suite
189
- - YARD documentation
190
-
191
204
  ## Planned
192
205
 
193
206
  ### [0.4.0]
@@ -200,4 +213,4 @@ New configuration options:
200
213
 
201
214
  - Background job integration (Sidekiq, GoodJob)
202
215
  - Production-ready with full documentation
203
- - Performance monitoring dashboard
216
+ - Performance monitoring dashboard
data/SECURITY.md CHANGED
@@ -46,30 +46,100 @@ We will:
46
46
 
47
47
  ### API Key Management
48
48
 
49
+ **CRITICAL: Always use environment variables or secure vaults for API keys.**
50
+
49
51
  - **Never commit API keys** to version control
50
- - Store API keys in environment variables or secure vaults
52
+ - Store API keys in environment variables or secure vaults (AWS Secrets Manager, HashiCorp Vault, etc.)
51
53
  - Use different API keys for development, staging, and production
52
- - Rotate API keys regularly
54
+ - Rotate API keys regularly (every 90 days recommended)
53
55
  - Limit API key permissions to minimum required access
56
+ - Monitor API key usage for anomalies
54
57
 
55
58
  ```ruby
56
59
  # ✅ Good - Use environment variables
57
60
  Vectra.configure do |config|
58
- config.api_key = ENV['PINECONE_API_KEY']
61
+ config.provider = :pinecone
62
+ config.api_key = ENV['PINECONE_API_KEY'] # Set in environment
63
+ config.environment = ENV['PINECONE_ENVIRONMENT'] || 'us-east-1'
64
+ end
65
+
66
+ # ✅ Good - Use Rails credentials (encrypted)
67
+ Vectra.configure do |config|
68
+ config.api_key = Rails.application.credentials.dig(:pinecone, :api_key)
69
+ end
70
+
71
+ # ✅ Good - Use AWS Secrets Manager
72
+ require 'aws-sdk-secretsmanager'
73
+ secrets = Aws::SecretsManager::Client.new
74
+ secret = JSON.parse(secrets.get_secret_value(secret_id: 'vectra/pinecone').secret_string)
75
+
76
+ Vectra.configure do |config|
77
+ config.api_key = secret['api_key']
59
78
  end
60
79
 
61
80
  # ❌ Bad - Hardcoded API key
62
81
  Vectra.configure do |config|
63
- config.api_key = "pk-123456789" # Never do this!
82
+ config.api_key = "pk-123456789" # NEVER DO THIS!
64
83
  end
84
+
85
+ # ❌ Bad - API key in config file
86
+ # config/vectra.yml
87
+ # api_key: "pk-123456789" # This will be committed to git!
88
+ ```
89
+
90
+ ### Credential Rotation
91
+
92
+ Use built-in credential rotation helpers for zero-downtime key rotation:
93
+
94
+ ```ruby
95
+ require 'vectra/credential_rotation'
96
+
97
+ # Setup rotation
98
+ rotator = Vectra::CredentialRotator.new(
99
+ primary_key: ENV['PINECONE_API_KEY'],
100
+ secondary_key: ENV['PINECONE_API_KEY_NEW'],
101
+ provider: :pinecone
102
+ )
103
+
104
+ # Test new key before switching
105
+ if rotator.test_secondary
106
+ rotator.switch_to_secondary
107
+ puts "Rotation complete!"
108
+ else
109
+ puts "New key validation failed - keeping primary"
110
+ end
111
+
112
+ # Rollback if needed
113
+ rotator.rollback
65
114
  ```
66
115
 
116
+ **Rotation Best Practices:**
117
+
118
+ 1. **Test new credentials** before switching
119
+ 2. **Monitor for errors** after rotation
120
+ 3. **Keep old key active** for 24-48 hours after rotation
121
+ 4. **Rotate during low-traffic periods** when possible
122
+ 5. **Use gradual migration** for high-traffic systems
123
+
67
124
  ### Network Security
68
125
 
69
126
  - Always use HTTPS for API connections (enforced by default)
70
127
  - Verify SSL certificates (enabled by default)
71
128
  - Use VPN or private networks when possible
72
129
  - Monitor API usage for unusual patterns
130
+ - **mTLS Support**: For providers that support mutual TLS, configure client certificates
131
+
132
+ ```ruby
133
+ # mTLS configuration (provider-specific)
134
+ # Note: mTLS support depends on provider capabilities
135
+ # For pgvector, use SSL connection parameters:
136
+ Vectra.configure do |config|
137
+ config.provider = :pgvector
138
+ config.host = "postgresql://user:pass@host/db?sslmode=verify-full&sslcert=/path/to/client.crt&sslkey=/path/to/client.key"
139
+ end
140
+
141
+ # For cloud providers, check provider documentation for mTLS setup
142
+ ```
73
143
 
74
144
  ### Data Security
75
145
 
@@ -137,6 +207,7 @@ end
137
207
  - Monitor for authentication failures
138
208
  - Track unusual query patterns
139
209
  - Set up alerts for rate limit violations
210
+ - **Enable audit logging** for compliance requirements
140
211
 
141
212
  ```ruby
142
213
  # ❌ Bad - Logs API key
@@ -144,8 +215,67 @@ logger.info("Using API key: #{config.api_key}")
144
215
 
145
216
  # ✅ Good - Logs without sensitive data
146
217
  logger.info("Initializing Vectra client for #{config.provider}")
218
+
219
+ # ✅ Good - Use audit logging for security events
220
+ require 'vectra/audit_log'
221
+
222
+ audit = Vectra::AuditLog.new(output: "log/audit.json.log")
223
+
224
+ # Log access events
225
+ audit.log_access(
226
+ user_id: current_user.id,
227
+ operation: "query",
228
+ index: "sensitive-data",
229
+ result_count: 10
230
+ )
231
+
232
+ # Log authentication events
233
+ audit.log_authentication(
234
+ user_id: user.id,
235
+ success: true,
236
+ provider: "pinecone"
237
+ )
238
+
239
+ # Log credential rotations
240
+ audit.log_credential_rotation(
241
+ provider: "pinecone",
242
+ success: true,
243
+ rotated_by: admin_user.id
244
+ )
245
+ ```
246
+
247
+ ### Audit Logging
248
+
249
+ For compliance and security auditing:
250
+
251
+ ```ruby
252
+ # Setup global audit logging
253
+ Vectra::AuditLogging.setup!(
254
+ output: "log/audit.json.log",
255
+ app: "my-service",
256
+ env: Rails.env
257
+ )
258
+
259
+ # Automatic audit logging for operations
260
+ Vectra::Instrumentation.on_operation do |event|
261
+ Vectra::AuditLogging.log(:access,
262
+ user_id: current_user&.id,
263
+ operation: event.operation,
264
+ index: event.index,
265
+ success: event.success?
266
+ )
267
+ end
147
268
  ```
148
269
 
270
+ **Audit Log Events:**
271
+ - `access` - Data access operations
272
+ - `authentication` - Auth success/failure
273
+ - `authorization` - Permission checks
274
+ - `configuration_change` - Config modifications
275
+ - `credential_rotation` - Key rotations
276
+ - `data_modification` - Upsert/delete/update
277
+ - `error` - Security-relevant errors
278
+
149
279
  ## Known Security Considerations
150
280
 
151
281
  ### API Key Exposure
@@ -36,6 +36,7 @@
36
36
  <li><a href="{{ site.baseurl }}/guides/getting-started" class="tma-sidebar__link {% if page.url == '/guides/getting-started/' %}tma-sidebar__link--active{% endif %}">Quick Start</a></li>
37
37
  <li><a href="{{ site.baseurl }}/guides/performance" class="tma-sidebar__link {% if page.url == '/guides/performance/' %}tma-sidebar__link--active{% endif %}">Performance</a></li>
38
38
  <li><a href="{{ site.baseurl }}/guides/monitoring" class="tma-sidebar__link {% if page.url == '/guides/monitoring/' %}tma-sidebar__link--active{% endif %}">Monitoring</a></li>
39
+ <li><a href="{{ site.baseurl }}/guides/security" class="tma-sidebar__link {% if page.url == '/guides/security/' %}tma-sidebar__link--active{% endif %}">Security</a></li>
39
40
  </ul>
40
41
  </div>
41
42
 
@@ -0,0 +1,348 @@
1
+ ---
2
+ layout: page
3
+ title: Security Best Practices
4
+ permalink: /guides/security/
5
+ ---
6
+
7
+ # Security Best Practices
8
+
9
+ Complete guide for securing Vectra in production environments.
10
+
11
+ ## API Key Management
12
+
13
+ ### Environment Variables (Recommended)
14
+
15
+ **Always use environment variables for API keys:**
16
+
17
+ ```ruby
18
+ # ✅ Good
19
+ Vectra.configure do |config|
20
+ config.provider = :pinecone
21
+ config.api_key = ENV['PINECONE_API_KEY'] # Set in .env or system env
22
+ config.environment = ENV['PINECONE_ENVIRONMENT'] || 'us-east-1'
23
+ end
24
+ ```
25
+
26
+ ```bash
27
+ # Set in environment
28
+ export PINECONE_API_KEY="your-key-here"
29
+ export PINECONE_ENVIRONMENT="us-east-1"
30
+ ```
31
+
32
+ ### Rails Credentials (Encrypted)
33
+
34
+ ```ruby
35
+ # ✅ Good - Rails encrypted credentials
36
+ Vectra.configure do |config|
37
+ config.api_key = Rails.application.credentials.dig(:pinecone, :api_key)
38
+ end
39
+ ```
40
+
41
+ ```bash
42
+ # Edit credentials
43
+ rails credentials:edit
44
+
45
+ # Add to credentials.yml.enc:
46
+ # pinecone:
47
+ # api_key: your-key-here
48
+ ```
49
+
50
+ ### Secret Management Services
51
+
52
+ ```ruby
53
+ # ✅ Good - AWS Secrets Manager
54
+ require 'aws-sdk-secretsmanager'
55
+
56
+ secrets = Aws::SecretsManager::Client.new
57
+ secret = JSON.parse(
58
+ secrets.get_secret_value(secret_id: 'vectra/pinecone').secret_string
59
+ )
60
+
61
+ Vectra.configure do |config|
62
+ config.api_key = secret['api_key']
63
+ end
64
+ ```
65
+
66
+ ```ruby
67
+ # ✅ Good - HashiCorp Vault
68
+ require 'vault'
69
+
70
+ Vault.auth.aws
71
+ secret = Vault.logical.read("secret/vectra/pinecone")
72
+
73
+ Vectra.configure do |config|
74
+ config.api_key = secret.data[:api_key]
75
+ end
76
+ ```
77
+
78
+ ### What NOT to Do
79
+
80
+ ```ruby
81
+ # ❌ NEVER hardcode API keys
82
+ Vectra.configure do |config|
83
+ config.api_key = "pk-123456789" # This will be committed to git!
84
+ end
85
+
86
+ # ❌ NEVER store in config files
87
+ # config/vectra.yml
88
+ # api_key: "pk-123456789" # This will be committed!
89
+
90
+ # ❌ NEVER log API keys
91
+ logger.info("API key: #{config.api_key}") # Will appear in logs!
92
+ ```
93
+
94
+ ## Credential Rotation
95
+
96
+ Use built-in credential rotation for zero-downtime key updates:
97
+
98
+ ```ruby
99
+ require 'vectra/credential_rotation'
100
+
101
+ # Setup rotation
102
+ rotator = Vectra::CredentialRotator.new(
103
+ primary_key: ENV['PINECONE_API_KEY'],
104
+ secondary_key: ENV['PINECONE_API_KEY_NEW'],
105
+ provider: :pinecone
106
+ )
107
+
108
+ # Test new key before switching
109
+ if rotator.test_secondary
110
+ rotator.switch_to_secondary
111
+ puts "✅ Rotation complete"
112
+ else
113
+ puts "❌ New key validation failed - keeping primary"
114
+ end
115
+
116
+ # Rollback if issues occur
117
+ rotator.rollback
118
+ ```
119
+
120
+ ### Multi-Provider Rotation
121
+
122
+ ```ruby
123
+ # Register multiple providers
124
+ Vectra::CredentialRotationManager.register(:pinecone,
125
+ primary: ENV['PINECONE_API_KEY'],
126
+ secondary: ENV['PINECONE_API_KEY_NEW']
127
+ )
128
+
129
+ Vectra::CredentialRotationManager.register(:qdrant,
130
+ primary: ENV['QDRANT_API_KEY'],
131
+ secondary: ENV['QDRANT_API_KEY_NEW']
132
+ )
133
+
134
+ # Test all new keys
135
+ results = Vectra::CredentialRotationManager.test_all_secondary
136
+ # => { pinecone: true, qdrant: false }
137
+
138
+ # Rotate all if tests pass
139
+ if results.values.all?
140
+ Vectra::CredentialRotationManager.rotate_all
141
+ end
142
+
143
+ # Check rotation status
144
+ Vectra::CredentialRotationManager.status
145
+ ```
146
+
147
+ ### Rotation Best Practices
148
+
149
+ 1. **Test before switching** - Always validate new credentials
150
+ 2. **Monitor after rotation** - Watch for errors for 24-48 hours
151
+ 3. **Keep old key active** - Don't revoke immediately
152
+ 4. **Rotate during low traffic** - Minimize impact
153
+ 5. **Use gradual migration** - For high-traffic systems
154
+
155
+ ## Audit Logging
156
+
157
+ Enable audit logging for compliance and security monitoring:
158
+
159
+ ```ruby
160
+ require 'vectra/audit_log'
161
+
162
+ # Setup audit logging
163
+ audit = Vectra::AuditLog.new(
164
+ output: "log/audit.json.log",
165
+ app: "my-service",
166
+ env: Rails.env
167
+ )
168
+
169
+ # Log access events
170
+ audit.log_access(
171
+ user_id: current_user.id,
172
+ operation: "query",
173
+ index: "sensitive-data",
174
+ result_count: 10
175
+ )
176
+
177
+ # Log authentication
178
+ audit.log_authentication(
179
+ user_id: user.id,
180
+ success: true,
181
+ provider: "pinecone"
182
+ )
183
+
184
+ # Log credential rotations
185
+ audit.log_credential_rotation(
186
+ provider: "pinecone",
187
+ success: true,
188
+ rotated_by: admin_user.id
189
+ )
190
+
191
+ # Log data modifications
192
+ audit.log_data_modification(
193
+ user_id: user.id,
194
+ operation: "upsert",
195
+ index: "vectors",
196
+ record_count: 100
197
+ )
198
+ ```
199
+
200
+ ### Global Audit Logging
201
+
202
+ ```ruby
203
+ # Setup once in initializer
204
+ Vectra::AuditLogging.setup!(
205
+ output: "log/audit.json.log",
206
+ app: "my-service"
207
+ )
208
+
209
+ # Use anywhere
210
+ Vectra::AuditLogging.log(:access,
211
+ user_id: current_user.id,
212
+ operation: "query",
213
+ index: "data"
214
+ )
215
+ ```
216
+
217
+ ### Audit Log Format
218
+
219
+ ```json
220
+ {
221
+ "timestamp": "2025-01-08T12:00:00.123Z",
222
+ "level": "info",
223
+ "logger": "vectra",
224
+ "message": "audit.access",
225
+ "event_type": "access",
226
+ "user_id": "user123",
227
+ "operation": "query",
228
+ "resource": "my-index",
229
+ "result_count": 10
230
+ }
231
+ ```
232
+
233
+ ## Network Security
234
+
235
+ ### HTTPS/TLS
236
+
237
+ - **Always use HTTPS** - Enforced by default
238
+ - **Verify certificates** - Enabled by default
239
+ - **Use VPN/private networks** when possible
240
+
241
+ ### mTLS (Mutual TLS)
242
+
243
+ For providers supporting mutual TLS authentication:
244
+
245
+ ```ruby
246
+ # pgvector with client certificates
247
+ Vectra.configure do |config|
248
+ config.provider = :pgvector
249
+ config.host = "postgresql://user:pass@host/db?" \
250
+ "sslmode=verify-full&" \
251
+ "sslcert=/path/to/client.crt&" \
252
+ "sslkey=/path/to/client.key&" \
253
+ "sslrootcert=/path/to/ca.crt"
254
+ end
255
+ ```
256
+
257
+ **Note:** mTLS support depends on provider capabilities. Check provider documentation.
258
+
259
+ ## Data Security
260
+
261
+ ### Input Sanitization
262
+
263
+ ```ruby
264
+ # Sanitize metadata before upsert
265
+ def sanitize_metadata(metadata)
266
+ metadata.reject { |k, _| k.to_s.match?(/password|secret|token|ssn|credit_card/i) }
267
+ end
268
+
269
+ vectors = [{
270
+ id: "vec1",
271
+ values: embedding,
272
+ metadata: sanitize_metadata(user_data)
273
+ }]
274
+
275
+ client.upsert(index: "my-index", vectors: vectors)
276
+ ```
277
+
278
+ ### Access Control
279
+
280
+ ```ruby
281
+ # Implement application-level access control
282
+ class VectorService
283
+ def query(user:, index:, vector:, top_k:)
284
+ # Check permissions
285
+ unless user.can_access?(index)
286
+ audit.log_authorization(
287
+ user_id: user.id,
288
+ resource: index,
289
+ allowed: false,
290
+ reason: "Insufficient permissions"
291
+ )
292
+ raise ForbiddenError, "Access denied"
293
+ end
294
+
295
+ # Log access
296
+ audit.log_access(
297
+ user_id: user.id,
298
+ operation: "query",
299
+ index: index,
300
+ result_count: top_k
301
+ )
302
+
303
+ client.query(index: index, vector: vector, top_k: top_k)
304
+ end
305
+ end
306
+ ```
307
+
308
+ ## Compliance
309
+
310
+ ### GDPR/Privacy
311
+
312
+ - **Data retention policies** - Implement automatic deletion
313
+ - **Right to deletion** - Support user data removal
314
+ - **Data encryption** - Encrypt sensitive metadata
315
+ - **Access logs** - Maintain audit trails
316
+
317
+ ### HIPAA/Healthcare
318
+
319
+ - **Encryption at rest** - Provider responsibility
320
+ - **Encryption in transit** - HTTPS enforced
321
+ - **Access controls** - Application-level
322
+ - **Audit logging** - Required for compliance
323
+
324
+ ### PCI-DSS
325
+
326
+ - **No card data in vectors** - Never store card numbers
327
+ - **Tokenization** - Use tokens instead of raw data
328
+ - **Access monitoring** - Audit all access
329
+
330
+ ## Security Checklist
331
+
332
+ - [ ] API keys stored in environment variables or vaults
333
+ - [ ] No API keys in version control
334
+ - [ ] Different keys for dev/staging/production
335
+ - [ ] Credential rotation implemented
336
+ - [ ] Audit logging enabled
337
+ - [ ] HTTPS/TLS enforced
338
+ - [ ] Input sanitization implemented
339
+ - [ ] Access controls in place
340
+ - [ ] Rate limiting configured
341
+ - [ ] Error monitoring setup
342
+ - [ ] Security alerts configured
343
+
344
+ ## Related
345
+
346
+ - [SECURITY.md](https://github.com/stokry/vectra/blob/main/SECURITY.md) - Security policy
347
+ - [Monitoring Guide]({{ site.baseurl }}/guides/monitoring) - Security monitoring
348
+ - [Performance Guide]({{ site.baseurl }}/guides/performance) - Rate limiting
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vectra
4
+ # Audit logging for security and compliance
5
+ #
6
+ # Provides structured audit logs for security-sensitive operations,
7
+ # including authentication, authorization, and data access.
8
+ #
9
+ # @example Basic usage
10
+ # audit = Vectra::AuditLog.new(output: "log/audit.json.log")
11
+ #
12
+ # audit.log_access(
13
+ # user_id: "user123",
14
+ # operation: "query",
15
+ # index: "sensitive-data",
16
+ # result_count: 10
17
+ # )
18
+ #
19
+ class AuditLog
20
+ # Audit event types
21
+ EVENT_TYPES = %i[
22
+ access
23
+ authentication
24
+ authorization
25
+ configuration_change
26
+ credential_rotation
27
+ data_modification
28
+ error
29
+ ].freeze
30
+
31
+ attr_reader :logger, :enabled
32
+
33
+ # Initialize audit logger
34
+ #
35
+ # @param output [IO, String] Log output destination
36
+ # @param enabled [Boolean] Enable/disable audit logging
37
+ # @param metadata [Hash] Default metadata for all events
38
+ def initialize(output: $stdout, enabled: true, **metadata)
39
+ @enabled = enabled
40
+ @logger = enabled ? Vectra::JsonLogger.new(output, **metadata) : nil
41
+ end
42
+
43
+ # Log access event
44
+ #
45
+ # @param user_id [String, nil] User identifier
46
+ # @param operation [Symbol, String] Operation type
47
+ # @param index [String] Index accessed
48
+ # @param result_count [Integer, nil] Number of results
49
+ # @param metadata [Hash] Additional metadata
50
+ def log_access(operation:, index: nil, result_count: nil, user_id: nil, **metadata)
51
+ log_event(
52
+ type: :access,
53
+ user_id: user_id,
54
+ operation: operation.to_s,
55
+ resource: index,
56
+ result_count: result_count,
57
+ **metadata
58
+ )
59
+ end
60
+
61
+ # Log authentication event
62
+ #
63
+ # @param user_id [String, nil] User identifier
64
+ # @param success [Boolean] Authentication success
65
+ # @param provider [String, nil] Provider name
66
+ # @param metadata [Hash] Additional metadata
67
+ def log_authentication(success:, provider: nil, user_id: nil, **metadata)
68
+ log_event(
69
+ type: :authentication,
70
+ user_id: user_id,
71
+ success: success,
72
+ provider: provider,
73
+ **metadata
74
+ )
75
+ end
76
+
77
+ # Log authorization event
78
+ #
79
+ # @param user_id [String] User identifier
80
+ # @param resource [String] Resource accessed
81
+ # @param allowed [Boolean] Authorization result
82
+ # @param reason [String, nil] Reason if denied
83
+ def log_authorization(user_id:, resource:, allowed:, reason: nil)
84
+ log_event(
85
+ type: :authorization,
86
+ user_id: user_id,
87
+ resource: resource,
88
+ allowed: allowed,
89
+ reason: reason
90
+ )
91
+ end
92
+
93
+ # Log configuration change
94
+ #
95
+ # @param user_id [String] User who made change
96
+ # @param change_type [String] Type of change
97
+ # @param old_value [Object, nil] Previous value
98
+ # @param new_value [Object, nil] New value
99
+ def log_configuration_change(user_id:, change_type:, old_value: nil, new_value: nil)
100
+ log_event(
101
+ type: :configuration_change,
102
+ user_id: user_id,
103
+ change_type: change_type,
104
+ old_value: sanitize_value(old_value),
105
+ new_value: sanitize_value(new_value)
106
+ )
107
+ end
108
+
109
+ # Log credential rotation
110
+ #
111
+ # @param provider [String] Provider name
112
+ # @param success [Boolean] Rotation success
113
+ # @param rotated_by [String, nil] User who initiated rotation
114
+ def log_credential_rotation(provider:, success:, rotated_by: nil)
115
+ log_event(
116
+ type: :credential_rotation,
117
+ provider: provider,
118
+ success: success,
119
+ rotated_by: rotated_by
120
+ )
121
+ end
122
+
123
+ # Log data modification
124
+ #
125
+ # @param user_id [String, nil] User identifier
126
+ # @param operation [String] Operation (upsert, delete, update)
127
+ # @param index [String] Index modified
128
+ # @param record_count [Integer] Number of records affected
129
+ def log_data_modification(operation:, index:, record_count:, user_id: nil)
130
+ log_event(
131
+ type: :data_modification,
132
+ user_id: user_id,
133
+ operation: operation.to_s,
134
+ resource: index,
135
+ record_count: record_count
136
+ )
137
+ end
138
+
139
+ # Log error event
140
+ #
141
+ # @param error [Exception] Error that occurred
142
+ # @param context [Hash] Error context
143
+ def log_error(error:, **context)
144
+ log_event(
145
+ type: :error,
146
+ error_class: error.class.name,
147
+ error_message: error.message,
148
+ severity: error_severity(error),
149
+ **context
150
+ )
151
+ end
152
+
153
+ private
154
+
155
+ def log_event(type:, **data)
156
+ return unless @enabled && @logger
157
+
158
+ @logger.info(
159
+ "audit.#{type}",
160
+ event_type: type.to_s,
161
+ timestamp: Time.now.utc.iso8601(3),
162
+ **data
163
+ )
164
+ end
165
+
166
+ def sanitize_value(value)
167
+ case value
168
+ when String
169
+ # Mask sensitive values: keep a short prefix and suffix, hide the middle
170
+ return value unless value.length >= 10
171
+
172
+ prefix = value[0, 9]
173
+ suffix = value[-4, 4]
174
+ "#{prefix}...#{suffix}"
175
+ when Hash
176
+ value.transform_values { |v| sanitize_value(v) }
177
+ else
178
+ value
179
+ end
180
+ end
181
+
182
+ def error_severity(error)
183
+ case error
184
+ when Vectra::AuthenticationError
185
+ "critical"
186
+ when Vectra::ServerError, Vectra::ConnectionError
187
+ "high"
188
+ when Vectra::RateLimitError
189
+ "medium"
190
+ else
191
+ "low"
192
+ end
193
+ end
194
+ end
195
+
196
+ # Global audit log instance
197
+ module AuditLogging
198
+ class << self
199
+ attr_accessor :audit_log
200
+
201
+ # Setup global audit logging
202
+ #
203
+ # @param output [IO, String] Log output
204
+ # @param enabled [Boolean] Enable audit logging
205
+ # @param metadata [Hash] Default metadata
206
+ # @return [AuditLog]
207
+ def setup!(output: "log/audit.json.log", enabled: true, **metadata)
208
+ @audit_log = AuditLog.new(output: output, enabled: enabled, **metadata)
209
+ end
210
+
211
+ # Log audit event
212
+ #
213
+ # @param type [Symbol] Event type
214
+ # @param data [Hash] Event data
215
+ def log(type, **data)
216
+ return unless @audit_log
217
+
218
+ @audit_log.public_send("log_#{type}", **data)
219
+ rescue NoMethodError
220
+ # Event type not supported
221
+ @audit_log.instance_variable_get(:@logger)&.info("audit.#{type}", **data)
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vectra
4
+ # Credential rotation helper for seamless API key updates
5
+ #
6
+ # Provides utilities for rotating API keys without downtime by supporting
7
+ # multiple credentials and gradual migration.
8
+ #
9
+ # @example Basic rotation
10
+ # rotator = Vectra::CredentialRotator.new(
11
+ # primary_key: ENV['PINECONE_API_KEY'],
12
+ # secondary_key: ENV['PINECONE_API_KEY_NEW']
13
+ # )
14
+ #
15
+ # # Test new key before switching
16
+ # if rotator.test_secondary
17
+ # rotator.switch_to_secondary
18
+ # end
19
+ #
20
+ class CredentialRotator
21
+ attr_reader :primary_key, :secondary_key, :current_key
22
+
23
+ # Initialize credential rotator
24
+ #
25
+ # @param primary_key [String] Current active API key
26
+ # @param secondary_key [String, nil] New API key to rotate to
27
+ # @param provider [Symbol] Provider name
28
+ # @param test_client [Client, nil] Client instance for testing
29
+ def initialize(primary_key:, secondary_key: nil, provider: nil, test_client: nil)
30
+ @primary_key = primary_key
31
+ @secondary_key = secondary_key
32
+ @provider = provider
33
+ @test_client = test_client
34
+ @current_key = primary_key
35
+ @rotation_complete = false
36
+ end
37
+
38
+ # Test if secondary key is valid
39
+ #
40
+ # @param timeout [Float] Test timeout in seconds
41
+ # @return [Boolean] true if secondary key works
42
+ def test_secondary(timeout: 5)
43
+ return false if secondary_key.nil? || secondary_key.empty?
44
+
45
+ client = build_test_client(secondary_key)
46
+ client.healthy?(timeout: timeout)
47
+ rescue StandardError
48
+ false
49
+ end
50
+
51
+ # Switch to secondary key
52
+ #
53
+ # @param validate [Boolean] Validate key before switching
54
+ # @return [Boolean] true if switched successfully
55
+ # rubocop:disable Naming/PredicateMethod
56
+ def switch_to_secondary(validate: true)
57
+ return false if secondary_key.nil? || secondary_key.empty?
58
+
59
+ if validate && !test_secondary
60
+ raise CredentialRotationError, "Secondary key validation failed"
61
+ end
62
+
63
+ @current_key = secondary_key
64
+ @rotation_complete = true
65
+ true
66
+ end
67
+ # rubocop:enable Naming/PredicateMethod
68
+
69
+ # Rollback to primary key
70
+ #
71
+ # @return [void]
72
+ def rollback
73
+ @current_key = primary_key
74
+ @rotation_complete = false
75
+ end
76
+
77
+ # Check if rotation is complete
78
+ #
79
+ # @return [Boolean]
80
+ def rotation_complete?
81
+ @rotation_complete
82
+ end
83
+
84
+ # Get current active key
85
+ #
86
+ # @return [String]
87
+ def active_key
88
+ @current_key
89
+ end
90
+
91
+ private
92
+
93
+ def build_test_client(key)
94
+ return @test_client if @test_client
95
+
96
+ config = Vectra::Configuration.new
97
+ config.provider = @provider || Vectra.configuration.provider
98
+ config.api_key = key
99
+ config.host = Vectra.configuration.host
100
+ config.environment = Vectra.configuration.environment
101
+
102
+ Vectra::Client.new(
103
+ provider: config.provider,
104
+ api_key: key,
105
+ host: config.host,
106
+ environment: config.environment
107
+ )
108
+ end
109
+ end
110
+
111
+ # Error raised during credential rotation
112
+ class CredentialRotationError < Vectra::Error; end
113
+
114
+ # Credential rotation manager for multiple providers
115
+ #
116
+ # @example
117
+ # manager = Vectra::CredentialRotationManager.new
118
+ #
119
+ # manager.register(:pinecone,
120
+ # primary: ENV['PINECONE_API_KEY'],
121
+ # secondary: ENV['PINECONE_API_KEY_NEW']
122
+ # )
123
+ #
124
+ # manager.rotate_all
125
+ #
126
+ module CredentialRotationManager
127
+ class << self
128
+ # Register a credential rotator
129
+ #
130
+ # @param provider [Symbol] Provider name
131
+ # @param primary [String] Primary API key
132
+ # @param secondary [String, nil] Secondary API key
133
+ # @return [CredentialRotator]
134
+ def register(provider, primary:, secondary: nil)
135
+ rotators[provider] = CredentialRotator.new(
136
+ primary_key: primary,
137
+ secondary_key: secondary,
138
+ provider: provider
139
+ )
140
+ end
141
+
142
+ # Get rotator for provider
143
+ #
144
+ # @param provider [Symbol] Provider name
145
+ # @return [CredentialRotator, nil]
146
+ def [](provider)
147
+ rotators[provider.to_sym]
148
+ end
149
+
150
+ # Test all secondary keys
151
+ #
152
+ # @return [Hash<Symbol, Boolean>] Test results
153
+ def test_all_secondary
154
+ rotators.transform_values(&:test_secondary)
155
+ end
156
+
157
+ # Rotate all providers
158
+ #
159
+ # @param validate [Boolean] Validate before rotating
160
+ # @return [Hash<Symbol, Boolean>] Rotation results
161
+ def rotate_all(validate: true)
162
+ rotators.transform_values { |r| r.switch_to_secondary(validate: validate) }
163
+ end
164
+
165
+ # Rollback all rotations
166
+ #
167
+ # @return [void]
168
+ def rollback_all
169
+ rotators.each_value(&:rollback)
170
+ end
171
+
172
+ # Get rotation status
173
+ #
174
+ # @return [Hash<Symbol, Hash>]
175
+ def status
176
+ rotators.transform_values do |r|
177
+ {
178
+ rotation_complete: r.rotation_complete?,
179
+ has_secondary: !r.secondary_key.nil?,
180
+ active_key: "#{r.active_key[0, 8]}..." # First 8 chars only
181
+ }
182
+ end
183
+ end
184
+
185
+ # Clear all rotators
186
+ #
187
+ # @return [void]
188
+ def clear!
189
+ @rotators = {}
190
+ end
191
+
192
+ private
193
+
194
+ def rotators
195
+ @rotators ||= {}
196
+ end
197
+ end
198
+ end
199
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vectra
4
- VERSION = "0.3.1"
4
+ VERSION = "0.3.2"
5
5
  end
data/lib/vectra.rb CHANGED
@@ -15,6 +15,8 @@ require_relative "vectra/circuit_breaker"
15
15
  require_relative "vectra/rate_limiter"
16
16
  require_relative "vectra/logging"
17
17
  require_relative "vectra/health_check"
18
+ require_relative "vectra/credential_rotation"
19
+ require_relative "vectra/audit_log"
18
20
  require_relative "vectra/active_record"
19
21
  require_relative "vectra/providers/base"
20
22
  require_relative "vectra/providers/pinecone"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vectra-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mijo Kristo
@@ -264,6 +264,7 @@ files:
264
264
  - docs/guides/runbooks/high-error-rate.md
265
265
  - docs/guides/runbooks/high-latency.md
266
266
  - docs/guides/runbooks/pool-exhausted.md
267
+ - docs/guides/security.md
267
268
  - docs/index.md
268
269
  - docs/providers/index.md
269
270
  - docs/providers/pgvector.md
@@ -277,11 +278,13 @@ files:
277
278
  - lib/generators/vectra/templates/vectra.rb
278
279
  - lib/vectra.rb
279
280
  - lib/vectra/active_record.rb
281
+ - lib/vectra/audit_log.rb
280
282
  - lib/vectra/batch.rb
281
283
  - lib/vectra/cache.rb
282
284
  - lib/vectra/circuit_breaker.rb
283
285
  - lib/vectra/client.rb
284
286
  - lib/vectra/configuration.rb
287
+ - lib/vectra/credential_rotation.rb
285
288
  - lib/vectra/errors.rb
286
289
  - lib/vectra/health_check.rb
287
290
  - lib/vectra/instrumentation.rb