vectra-client 0.3.1 → 0.3.3
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 +28 -187
- 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: 510bd3a530df754db719892b7157846a72a29998659052fa7fda33b615ddab84
|
|
4
|
+
data.tar.gz: 6e853240a0231e909fda25709b72610f5d294e630f3576a0a52c0949e500e47f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a1658293c6bd4f62c777b106f40da783b56b68498b4cb73dda4af6df542277c8545105bafe47c6ba1fea106f68e934395df42f0d8453d35a2cdc3d89ef80500d
|
|
7
|
+
data.tar.gz: 9a108eb9be1df21a216824f2e9f0bbe60bb721bf6145cb2b4f772ecb1d7abe7b2e618c2d70d67455ed484ad65c05b20b3ec65485196740a51809fa9dd49a70c3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,203 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [Unreleased]
|
|
9
|
-
|
|
10
|
-
### Added
|
|
11
|
-
|
|
12
|
-
- **Proactive Rate Limiting** (`Vectra::RateLimiter`)
|
|
13
|
-
- Token bucket algorithm for smooth rate limiting
|
|
14
|
-
- Burst support for handling traffic spikes
|
|
15
|
-
- Per-provider rate limiter registry
|
|
16
|
-
- `RateLimitedClient` wrapper for automatic throttling
|
|
17
|
-
- Prevents API rate limit errors before they occur
|
|
18
|
-
|
|
19
|
-
- **Structured JSON Logging** (`Vectra::JsonLogger`)
|
|
20
|
-
- Machine-readable JSON log format
|
|
21
|
-
- Automatic operation logging via instrumentation
|
|
22
|
-
- Custom metadata support
|
|
23
|
-
- Integration with standard Ruby Logger via `JsonFormatter`
|
|
24
|
-
- Log levels: debug, info, warn, error, fatal
|
|
25
|
-
|
|
26
|
-
- **Health Check Functionality** (`Vectra::HealthCheck`)
|
|
27
|
-
- Built-in `client.health_check` method
|
|
28
|
-
- Connectivity and latency testing
|
|
29
|
-
- Optional index statistics inclusion
|
|
30
|
-
- Pool health checking for pgvector
|
|
31
|
-
- `AggregateHealthCheck` for multi-provider setups
|
|
32
|
-
- JSON-serializable results
|
|
33
|
-
|
|
34
|
-
- **Error Tracking Integrations**
|
|
35
|
-
- Sentry adapter with breadcrumbs, context, and fingerprinting
|
|
36
|
-
- Honeybadger adapter with severity tags and configurable notifications
|
|
37
|
-
- Automatic error context and grouping
|
|
38
|
-
|
|
39
|
-
- **Circuit Breaker Pattern** (`Vectra::CircuitBreaker`)
|
|
40
|
-
- Three-state circuit (closed, open, half-open)
|
|
41
|
-
- Automatic failover with fallback support
|
|
42
|
-
- Per-provider circuit registry
|
|
43
|
-
- Thread-safe implementation
|
|
44
|
-
- Configurable failure/success thresholds
|
|
3
|
+
## [v0.3.2](https://github.com/stokry/vectra/tree/v0.3.2) (2026-01-08)
|
|
45
4
|
|
|
46
|
-
|
|
5
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v0.3.1...v0.3.2)
|
|
47
6
|
|
|
48
|
-
|
|
7
|
+
## [v0.3.1](https://github.com/stokry/vectra/tree/v0.3.1) (2026-01-08)
|
|
49
8
|
|
|
50
|
-
|
|
51
|
-
- Concurrent batch upsert with configurable worker count
|
|
52
|
-
- Automatic chunking of large vector sets
|
|
53
|
-
- Async delete and fetch operations
|
|
54
|
-
- Error aggregation and partial success handling
|
|
55
|
-
|
|
56
|
-
- **Streaming Results** (`Vectra::Streaming`)
|
|
57
|
-
- Lazy enumeration for large query result sets
|
|
58
|
-
- Memory-efficient processing with automatic pagination
|
|
59
|
-
- `query_each` and `query_stream` methods
|
|
60
|
-
- Duplicate detection across pages
|
|
61
|
-
|
|
62
|
-
- **Caching Layer** (`Vectra::Cache` and `Vectra::CachedClient`)
|
|
63
|
-
- In-memory LRU cache with configurable TTL
|
|
64
|
-
- Transparent caching for query and fetch operations
|
|
65
|
-
- Index-level cache invalidation
|
|
66
|
-
- Thread-safe with mutex synchronization
|
|
67
|
-
|
|
68
|
-
- **Connection Pool with Warmup** (`Vectra::Pool`)
|
|
69
|
-
- Configurable pool size and checkout timeout
|
|
70
|
-
- Connection warmup at startup
|
|
71
|
-
- Health checking and automatic reconnection
|
|
72
|
-
- Pool statistics and monitoring
|
|
73
|
-
|
|
74
|
-
- **CI Benchmark Integration**
|
|
75
|
-
- Weekly scheduled benchmark runs
|
|
76
|
-
- PostgreSQL (pgvector) integration in CI
|
|
77
|
-
- Benchmark result artifact storage
|
|
78
|
-
|
|
79
|
-
### Configuration
|
|
80
|
-
|
|
81
|
-
New configuration options:
|
|
82
|
-
- `cache_enabled` - Enable/disable caching (default: false)
|
|
83
|
-
- `cache_ttl` - Cache time-to-live in seconds (default: 300)
|
|
84
|
-
- `cache_max_size` - Maximum cache entries (default: 1000)
|
|
85
|
-
- `async_concurrency` - Concurrent workers for batch ops (default: 4)
|
|
86
|
-
|
|
87
|
-
### Dependencies
|
|
88
|
-
|
|
89
|
-
- Added `concurrent-ruby ~> 1.2` for thread-safe operations
|
|
90
|
-
|
|
91
|
-
## [0.2.1] - 2025-01-08
|
|
92
|
-
|
|
93
|
-
### Added
|
|
94
|
-
|
|
95
|
-
- **Qdrant Provider** - Full Qdrant vector database support:
|
|
96
|
-
- Vector upsert, query, fetch, update, delete operations
|
|
97
|
-
- Collection management (create, list, describe, delete)
|
|
98
|
-
- Multiple similarity metrics: cosine, euclidean, dot product
|
|
99
|
-
- Namespace support via payload filtering
|
|
100
|
-
- Advanced metadata filtering with Qdrant operators ($eq, $ne, $gt, $gte, $lt, $lte, $in, $nin)
|
|
101
|
-
- Automatic point ID hashing for string IDs
|
|
102
|
-
- Support for both local and cloud Qdrant instances
|
|
103
|
-
- Optional API key authentication for local deployments
|
|
9
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v0.3.0...v0.3.1)
|
|
104
10
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
- Enhanced error handling with `Faraday::RetriableResponse` support
|
|
108
|
-
- Configuration now allows optional API key for Qdrant and pgvector (local instances)
|
|
109
|
-
- Better retry middleware integration across all providers
|
|
110
|
-
|
|
111
|
-
### Provider Support
|
|
112
|
-
|
|
113
|
-
- ✅ Pinecone - Fully implemented
|
|
114
|
-
- ✅ pgvector (PostgreSQL) - Fully implemented
|
|
115
|
-
- ✅ Qdrant - Fully implemented
|
|
116
|
-
- 🚧 Weaviate - Stub implementation (planned for v0.3.0)
|
|
117
|
-
|
|
118
|
-
## [0.2.0] - 2025-01-08
|
|
119
|
-
|
|
120
|
-
### Added
|
|
121
|
-
|
|
122
|
-
- **Instrumentation & Monitoring** - Track all vector operations with New Relic, Datadog, or custom handlers
|
|
123
|
-
- **ActiveRecord Integration** - `has_vector` DSL for seamless Rails model integration with automatic indexing
|
|
124
|
-
- **Rails Generator** - `rails generate vectra:install` for quick setup
|
|
125
|
-
- **Automatic Retry Logic** - Exponential backoff with jitter for transient database errors
|
|
126
|
-
- **Performance Benchmarks** - Measure batch operations and connection pooling performance
|
|
127
|
-
- **Comprehensive Documentation** - USAGE_EXAMPLES.md, IMPLEMENTATION_GUIDE.md with 10+ real-world examples
|
|
11
|
+
## [v0.3.0](https://github.com/stokry/vectra/tree/v0.3.0) (2026-01-08)
|
|
128
12
|
|
|
129
|
-
|
|
13
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v0.2.2...v0.3.0)
|
|
130
14
|
|
|
131
|
-
|
|
132
|
-
- pgvector Connection module includes retry logic with smart error detection
|
|
133
|
-
- Configuration expanded with instrumentation, pool_size, batch_size, max_retries options
|
|
15
|
+
## [v0.2.2](https://github.com/stokry/vectra/tree/v0.2.2) (2026-01-08)
|
|
134
16
|
|
|
135
|
-
|
|
17
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v0.2.1...v0.2.2)
|
|
136
18
|
|
|
137
|
-
- Added USAGE_EXAMPLES.md with e-commerce search, RAG chatbot, duplicate detection examples
|
|
138
|
-
- Added IMPLEMENTATION_GUIDE.md for developers implementing new features
|
|
139
|
-
- Added NEW_FEATURES_v0.2.0.md with migration guide
|
|
140
19
|
|
|
141
|
-
|
|
20
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v0.2.0...v0.2.1)
|
|
142
21
|
|
|
143
|
-
|
|
22
|
+
## [v0.2.0](https://github.com/stokry/vectra/tree/v0.2.0) (2026-01-08)
|
|
144
23
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
- Metadata filtering with JSONB
|
|
151
|
-
- IVFFlat index creation for fast similarity search
|
|
152
|
-
- `Vectra.pgvector` convenience method for creating pgvector clients
|
|
153
|
-
- Comprehensive unit and integration tests for pgvector provider
|
|
24
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v0.1.3...v0.2.0)
|
|
25
|
+
|
|
26
|
+
## [v0.3.3](https://github.com/stokry/vectra/tree/v0.3.3) (2026-01-09)
|
|
27
|
+
|
|
28
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v0.3.2...v0.3.3)
|
|
154
29
|
|
|
155
30
|
### Changed
|
|
156
31
|
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
## Planned
|
|
192
|
-
|
|
193
|
-
### [0.4.0]
|
|
194
|
-
|
|
195
|
-
- Weaviate provider implementation
|
|
196
|
-
- Additional similarity metrics
|
|
197
|
-
- Vector quantization support
|
|
198
|
-
|
|
199
|
-
### [1.0.0]
|
|
200
|
-
|
|
201
|
-
- Background job integration (Sidekiq, GoodJob)
|
|
202
|
-
- Production-ready with full documentation
|
|
203
|
-
- Performance monitoring dashboard
|
|
32
|
+
- Version bump and minor maintenance release.
|
|
33
|
+
|
|
34
|
+
## [v0.1.3](https://github.com/stokry/vectra/tree/v0.1.3) (2026-01-07)
|
|
35
|
+
|
|
36
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v0.1.1...v0.1.3)
|
|
37
|
+
|
|
38
|
+
## [v0.1.1](https://github.com/stokry/vectra/tree/v0.1.1) (2026-01-07)
|
|
39
|
+
|
|
40
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/0ab2ea7b42d7fbf0b540b24889cc2b24254fef2e...v0.1.1)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
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.3
|
|
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
|