vectra-client 0.3.0 → 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 +86 -37
- data/SECURITY.md +134 -4
- data/docs/_layouts/page.html +2 -0
- data/docs/guides/monitoring.md +860 -0
- data/docs/guides/runbooks/cache-issues.md +267 -0
- data/docs/guides/runbooks/high-error-rate.md +152 -0
- data/docs/guides/runbooks/high-latency.md +287 -0
- data/docs/guides/runbooks/pool-exhausted.md +216 -0
- data/docs/guides/security.md +348 -0
- data/lib/vectra/audit_log.rb +225 -0
- data/lib/vectra/circuit_breaker.rb +336 -0
- data/lib/vectra/client.rb +2 -0
- data/lib/vectra/credential_rotation.rb +199 -0
- data/lib/vectra/health_check.rb +254 -0
- data/lib/vectra/instrumentation/honeybadger.rb +128 -0
- data/lib/vectra/instrumentation/sentry.rb +117 -0
- data/lib/vectra/logging.rb +242 -0
- data/lib/vectra/rate_limiter.rb +304 -0
- data/lib/vectra/version.rb +1 -1
- data/lib/vectra.rb +6 -0
- metadata +15 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: page
|
|
3
|
+
title: "Runbook: Pool Exhaustion"
|
|
4
|
+
permalink: /guides/runbooks/pool-exhausted/
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Runbook: Pool Exhaustion
|
|
8
|
+
|
|
9
|
+
**Alert:** `VectraPoolExhausted`
|
|
10
|
+
**Severity:** Critical
|
|
11
|
+
**Threshold:** 0 available connections for 1 minute
|
|
12
|
+
|
|
13
|
+
## Symptoms
|
|
14
|
+
|
|
15
|
+
- `Vectra::Pool::TimeoutError` exceptions
|
|
16
|
+
- Requests timing out waiting for connections
|
|
17
|
+
- Application threads blocked
|
|
18
|
+
|
|
19
|
+
## Quick Diagnosis
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# Check pool stats
|
|
23
|
+
client = Vectra::Client.new(provider: :pgvector, host: ENV['DATABASE_URL'])
|
|
24
|
+
puts client.provider.pool_stats
|
|
25
|
+
# => { available: 0, checked_out: 10, size: 10 }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Check PostgreSQL connections
|
|
30
|
+
psql -c "SELECT count(*) FROM pg_stat_activity WHERE application_name LIKE '%vectra%';"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Investigation Steps
|
|
34
|
+
|
|
35
|
+
### 1. Check Current Pool State
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
stats = client.provider.pool_stats
|
|
39
|
+
puts "Available: #{stats[:available]}"
|
|
40
|
+
puts "Checked out: #{stats[:checked_out]}"
|
|
41
|
+
puts "Total size: #{stats[:size]}"
|
|
42
|
+
puts "Shutdown: #{stats[:shutdown]}"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Identify Connection Leaks
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
# Look for connections not being returned
|
|
49
|
+
# Common causes:
|
|
50
|
+
# - Missing ensure blocks
|
|
51
|
+
# - Exceptions before checkin
|
|
52
|
+
# - Long-running operations
|
|
53
|
+
|
|
54
|
+
# Bad:
|
|
55
|
+
conn = pool.checkout
|
|
56
|
+
do_something(conn) # If this raises, connection is leaked!
|
|
57
|
+
pool.checkin(conn)
|
|
58
|
+
|
|
59
|
+
# Good:
|
|
60
|
+
pool.with_connection do |conn|
|
|
61
|
+
do_something(conn)
|
|
62
|
+
end # Always returns connection
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 3. Check for Long-Running Queries
|
|
66
|
+
|
|
67
|
+
```sql
|
|
68
|
+
-- PostgreSQL: Find long-running queries
|
|
69
|
+
SELECT pid, now() - pg_stat_activity.query_start AS duration, query
|
|
70
|
+
FROM pg_stat_activity
|
|
71
|
+
WHERE state != 'idle'
|
|
72
|
+
AND query NOT LIKE '%pg_stat_activity%'
|
|
73
|
+
ORDER BY duration DESC;
|
|
74
|
+
|
|
75
|
+
-- Kill long-running query if needed
|
|
76
|
+
SELECT pg_terminate_backend(pid);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 4. Check Application Thread Count
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
# If using Puma/Sidekiq
|
|
83
|
+
# Ensure pool_size >= max_threads
|
|
84
|
+
puts "Thread count: #{Thread.list.count}"
|
|
85
|
+
puts "Pool size: #{client.config.pool_size}"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Resolution Steps
|
|
89
|
+
|
|
90
|
+
### Immediate: Restart Connection Pool
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
# Force pool restart
|
|
94
|
+
client.provider.shutdown_pool
|
|
95
|
+
# Pool will be recreated on next operation
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Increase Pool Size
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
Vectra.configure do |config|
|
|
102
|
+
config.provider = :pgvector
|
|
103
|
+
config.host = ENV['DATABASE_URL']
|
|
104
|
+
config.pool_size = 20 # Increase from default 5
|
|
105
|
+
config.pool_timeout = 10 # Increase timeout
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Fix Connection Leaks
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
# Always use with_connection block
|
|
113
|
+
client.provider.with_pooled_connection do |conn|
|
|
114
|
+
# Your code here
|
|
115
|
+
# Connection automatically returned
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Or ensure checkin in rescue
|
|
119
|
+
begin
|
|
120
|
+
conn = pool.checkout
|
|
121
|
+
do_work(conn)
|
|
122
|
+
ensure
|
|
123
|
+
pool.checkin(conn) if conn
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Reduce Connection Hold Time
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
# Break up long operations
|
|
131
|
+
large_dataset.each_slice(100) do |batch|
|
|
132
|
+
client.provider.with_pooled_connection do |conn|
|
|
133
|
+
process_batch(batch, conn)
|
|
134
|
+
end
|
|
135
|
+
# Connection returned between batches
|
|
136
|
+
end
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Add Connection Warmup
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
# In application initializer
|
|
143
|
+
client = Vectra::Client.new(provider: :pgvector, host: ENV['DATABASE_URL'])
|
|
144
|
+
client.provider.warmup_pool(5) # Pre-create 5 connections
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Prevention
|
|
148
|
+
|
|
149
|
+
### 1. Right-size Pool
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
# Formula: pool_size = (max_threads * 1.5) + background_workers
|
|
153
|
+
# Example: Puma with 5 threads, 3 Sidekiq workers
|
|
154
|
+
pool_size = (5 * 1.5) + 3 # = 10.5, round to 12
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 2. Monitor Pool Usage
|
|
158
|
+
|
|
159
|
+
```promql
|
|
160
|
+
# Alert when pool is >80% utilized
|
|
161
|
+
vectra_pool_connections{state="checked_out"}
|
|
162
|
+
/ vectra_pool_connections{state="available"} > 0.8
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 3. Implement Connection Timeout
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
Vectra.configure do |config|
|
|
169
|
+
config.pool_timeout = 5 # Fail fast instead of hanging
|
|
170
|
+
end
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 4. Use Connection Pool Metrics
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
# Log pool stats periodically
|
|
177
|
+
every(60.seconds) do
|
|
178
|
+
stats = client.provider.pool_stats
|
|
179
|
+
logger.info "Pool: avail=#{stats[:available]} out=#{stats[:checked_out]}"
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## PostgreSQL-Specific
|
|
184
|
+
|
|
185
|
+
### Check max_connections
|
|
186
|
+
|
|
187
|
+
```sql
|
|
188
|
+
SHOW max_connections; -- Default: 100
|
|
189
|
+
|
|
190
|
+
-- Increase if needed (requires restart)
|
|
191
|
+
ALTER SYSTEM SET max_connections = 200;
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Monitor Connection Usage
|
|
195
|
+
|
|
196
|
+
```sql
|
|
197
|
+
SELECT
|
|
198
|
+
count(*) as total,
|
|
199
|
+
count(*) FILTER (WHERE state = 'active') as active,
|
|
200
|
+
count(*) FILTER (WHERE state = 'idle') as idle
|
|
201
|
+
FROM pg_stat_activity;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Escalation
|
|
205
|
+
|
|
206
|
+
| Time | Action |
|
|
207
|
+
|------|--------|
|
|
208
|
+
| 1 min | Restart pool, page on-call |
|
|
209
|
+
| 5 min | Increase pool size, restart app |
|
|
210
|
+
| 15 min | Check for connection leaks |
|
|
211
|
+
| 30 min | Escalate to DBA |
|
|
212
|
+
|
|
213
|
+
## Related
|
|
214
|
+
|
|
215
|
+
- [High Error Rate Runbook]({{ site.baseurl }}/guides/runbooks/high-error-rate)
|
|
216
|
+
- [Performance Guide]({{ site.baseurl }}/guides/performance)
|
|
@@ -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
|