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 +4 -4
- data/CHANGELOG.md +50 -37
- data/SECURITY.md +134 -4
- data/docs/_layouts/page.html +1 -0
- data/docs/guides/security.md +348 -0
- data/lib/vectra/audit_log.rb +225 -0
- data/lib/vectra/credential_rotation.rb +199 -0
- data/lib/vectra/version.rb +1 -1
- data/lib/vectra.rb +2 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2d3586fe349f6746ba939c2596bf5b1110551a8e7ed16a8dc6581eaa2f91af81
|
|
4
|
+
data.tar.gz: 11d141983fba57e97fc0d96792f75e8e2e50eac453671ddb25ce8456002acac2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
## [
|
|
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.
|
|
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
|
-
|
|
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
|
-
## [
|
|
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.
|
|
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" #
|
|
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
|
data/docs/_layouts/page.html
CHANGED
|
@@ -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
|
data/lib/vectra/version.rb
CHANGED
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.
|
|
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
|