Skip to content

Commit 784d689

Browse files
committed
docs: improvements
1 parent a59132d commit 784d689

File tree

15 files changed

+316
-236
lines changed

15 files changed

+316
-236
lines changed

README.md

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# InferaDB Control
22

3-
**Policy Administration Endpoint** — multi-tenant orchestration with headless APIs, Kubernetes-native deployment, and WebAuthn authentication.
3+
Multi-tenant control plane with headless APIs, Kubernetes-native deployment, and WebAuthn authentication.
44

55
> [!IMPORTANT]
66
> Under active development. Not production-ready.
@@ -11,29 +11,30 @@
1111
git clone https://github.com/inferadb/control && cd control
1212
docker-compose up -d
1313
export INFERADB_CTRL__AUTH__KEY_ENCRYPTION_SECRET=$(openssl rand -base64 32)
14-
cargo run --bin inferadb-control
14+
make setup && make dev
1515
```
1616

1717
Register and login:
1818

1919
```bash
2020
# Register
21-
curl -X POST http://localhost:3000/v1/auth/register \
21+
curl -X POST http://localhost:9090/v1/auth/register \
2222
-H "Content-Type: application/json" \
2323
-d '{"email": "alice@example.com", "password": "securepass123", "name": "Alice"}'
2424

2525
# Login
26-
curl -X POST http://localhost:3000/v1/auth/login/password \
26+
curl -X POST http://localhost:9090/v1/auth/login/password \
2727
-H "Content-Type: application/json" \
2828
-d '{"email": "alice@example.com", "password": "securepass123"}'
2929
```
3030

3131
| Endpoint | URL |
3232
| -------- | ------------------------------- |
33-
| REST API | `http://localhost:3000` |
34-
| gRPC API | `http://localhost:3001` |
35-
| Health | `http://localhost:3000/health` |
36-
| Metrics | `http://localhost:3000/metrics` |
33+
| REST API | `http://localhost:9090` |
34+
| gRPC API | `http://localhost:9091` |
35+
| Mesh API | `http://localhost:9092` |
36+
| Health | `http://localhost:9090/healthz` |
37+
| Metrics | `http://localhost:9090/metrics` |
3738

3839
## Features
3940

@@ -43,7 +44,7 @@ curl -X POST http://localhost:3000/v1/auth/login/password \
4344
| **Multi-Tenancy** | Organization-based isolation with RBAC |
4445
| **Vault Management** | Policy containers with access grants |
4546
| **Client Auth** | Ed25519 certificates, JWT assertions |
46-
| **Token Issuance** | Vault-scoped JWTs for Server API |
47+
| **Token Issuance** | Vault-scoped JWTs for Engine API |
4748

4849
## Key Concepts
4950

@@ -55,49 +56,61 @@ curl -X POST http://localhost:3000/v1/auth/login/password \
5556
| Client | Service identity with Ed25519 certs |
5657
| Team | Group-based vault access |
5758

58-
**Auth Flow:** User → Session → Vault access → JWT → Server API
59+
**Auth Flow:** User → Session → Vault access → JWT → Engine API
5960

6061
## Architecture
6162

6263
```mermaid
6364
graph TD
64-
API[inferadb-control-api] --> Core[inferadb-control-core]
65+
Bin[inferadb-control] --> API[inferadb-control-api]
66+
API --> Core[inferadb-control-core]
6567
Core --> Storage[inferadb-control-storage]
6668
Storage --> FDB[(FoundationDB)]
67-
API --> GRPC[inferadb-control-engine-client]
69+
Core --> Engine[inferadb-control-engine-client]
6870
```
6971

7072
| Crate | Purpose |
7173
| ------------------------------ | ------------------------ |
74+
| inferadb-control | Binary entrypoint |
7275
| inferadb-control-api | REST/gRPC handlers |
7376
| inferadb-control-core | Business logic, entities |
7477
| inferadb-control-storage | Memory or FoundationDB |
78+
| inferadb-control-types | Shared type definitions |
7579
| inferadb-control-engine-client | Engine API client |
7680

7781
## Configuration
7882

79-
```bash
80-
INFERADB_CTRL__STORAGE__BACKEND=foundationdb
81-
INFERADB_CTRL__STORAGE__FDB_CLUSTER_FILE=/etc/foundationdb/fdb.cluster
82-
INFERADB_CTRL__SERVER__HTTP_PORT=3000
83-
INFERADB_CTRL__AUTH__KEY_ENCRYPTION_SECRET=<base64>
83+
```yaml
84+
control:
85+
listen:
86+
http: "0.0.0.0:9090"
87+
grpc: "0.0.0.0:9091"
88+
mesh: "0.0.0.0:9092"
89+
90+
webauthn:
91+
party: "localhost"
92+
origin: "http://localhost:9090"
8493
```
8594
95+
Environment variables use `INFERADB_CTRL__` prefix (e.g., `INFERADB_CTRL__LISTEN__HTTP`).
96+
8697
See [config.yaml](config.yaml) for all options.
8798

8899
## Development
89100

90101
```bash
91-
cargo test # All tests
92-
cargo clippy -- -D warnings # Lint
93-
cargo fmt # Format
102+
make setup # One-time setup
103+
make dev # Dev server with auto-reload
104+
make test # Run tests
105+
make check # Format, lint, audit
106+
cargo build --release # Release build
94107
```
95108

96109
## Deployment
97110

98111
```bash
99-
cargo build --release
100-
./target/release/inferadb-control --config /etc/inferadb/config.yaml
112+
docker run -p 9090:9090 inferadb/control:latest
113+
kubectl apply -k k8s/
101114
```
102115

103116
See [docs/deployment.md](docs/deployment.md) for Kubernetes.

config.integration.yaml

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,24 @@
11
# InferaDB Control Configuration - Integration Tests
2-
# This config is optimized for Docker container E2E testing
3-
#
4-
# This file uses the unified configuration format. Both engine and control
5-
# can share the same config file by reading their respective sections.
62

73
control:
8-
threads: 2 # Reduced for test environment
9-
logging: "debug" # Verbose logging for tests
10-
114
frontend:
125
url: "http://localhost:9090"
136

147
listen:
15-
# Client-facing HTTP/REST API server
168
http: "0.0.0.0:9090"
17-
18-
# Client-facing gRPC API server
199
grpc: "0.0.0.0:9091"
20-
21-
# Service mesh / inter-service communication
2210
mesh: "0.0.0.0:9092"
2311

24-
storage: "memory" # In-memory for fast tests
12+
storage: "foundationdb"
13+
14+
foundationdb:
15+
cluster_file: "/var/fdb/fdb.cluster"
2516

2617
webauthn:
2718
party: "localhost"
2819
origin: "http://localhost:9090"
2920

30-
secret: "test-integration-secret-key-32bytes-long!"
21+
key_file: "/tmp/integration-master.key"
3122

3223
email:
3324
host: "localhost"
@@ -36,13 +27,11 @@ control:
3627
name: "InferaDB Test"
3728

3829
limits:
39-
login_attempts_per_ip_per_hour: 1000 # Relaxed for tests
30+
login_attempts_per_ip_per_hour: 1000
4031
registrations_per_ip_per_day: 100
4132
email_verification_tokens_per_hour: 100
4233
password_reset_tokens_per_hour: 100
4334

44-
# Mesh configuration for engine communication
45-
# Discovery mode discovers all engine pod IPs
4635
mesh:
4736
url: "http://inferadb-engine.inferadb"
4837
grpc: 8081
@@ -52,7 +41,6 @@ control:
5241
timeout: 5000
5342
retries: 0
5443

55-
# Kubernetes discovery for pod-level cache invalidation
5644
discovery:
5745
mode:
5846
type: kubernetes

config.production.yaml

Lines changed: 6 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,42 @@
11
# InferaDB Control Configuration - Production Template
2-
#
3-
# This is a template for production deployment. Replace all placeholder values
4-
# (marked with <...>) with your actual production values.
5-
#
6-
# SECURITY: Do not commit this file with real credentials to version control.
7-
# Use environment variables or a secrets management system in production.
8-
#
9-
# This file uses the unified configuration format. Both engine and control
10-
# can share the same config file by reading their respective sections.
112

123
control:
13-
# Worker threads (recommended: number of CPU cores)
14-
threads: 8
15-
logging: "info"
16-
174
listen:
18-
# Client-facing HTTP/REST API server (web, CLI, SDK)
195
http: "0.0.0.0:9090"
20-
21-
# Client-facing gRPC API server
226
grpc: "0.0.0.0:9091"
23-
24-
# Service mesh / inter-service communication
257
mesh: "0.0.0.0:9092"
268

27-
# Storage backend: "memory" or "foundationdb"
289
storage: "foundationdb"
2910

30-
# FoundationDB configuration (only used when storage = "foundationdb")
3111
foundationdb:
32-
# FoundationDB cluster file path
33-
# This file contains the connection string for your FDB cluster
3412
cluster_file: "/etc/foundationdb/fdb.cluster"
3513

36-
# WebAuthn configuration (for passkey authentication)
3714
webauthn:
38-
# Relying party ID (your domain)
3915
party: "example.com"
40-
41-
# Origin URL (must match browser origin)
4216
origin: "https://example.com"
4317

44-
# Master secret for encrypting private keys (REQUIRED: 32+ bytes for AES-256)
45-
# SECURITY: Use environment variable in production!
46-
# Set via: INFERADB__CONTROL__SECRET
47-
secret: "<REPLACE-WITH-32+-BYTE-SECRET>"
18+
key_file: "/var/lib/inferadb/master.key"
4819

49-
# Email configuration (SMTP)
5020
email:
51-
# SMTP server settings
5221
host: "<SMTP-HOST>"
5322
port: 587
54-
55-
# SMTP authentication (optional, uncomment if needed)
5623
# username: "<SMTP-USERNAME>"
5724
# password: "<SMTP-PASSWORD>"
58-
59-
# From address
6025
address: "noreply@example.com"
6126
name: "InferaDB"
6227

63-
# Rate limits configuration
6428
limits:
65-
# Login attempts per IP per hour
6629
login_attempts_per_ip_per_hour: 100
67-
68-
# Registration attempts per IP per day
6930
registrations_per_ip_per_day: 5
70-
71-
# Email verification tokens per email per hour
7231
email_verification_tokens_per_hour: 5
73-
74-
# Password reset tokens per user per hour
7532
password_reset_tokens_per_hour: 3
7633

77-
# Mesh configuration for engine communication
78-
# All engine instances are discovered from this base URL + ports
79-
# In production with Kubernetes/Tailscale discovery, set discovery.mode appropriately
8034
mesh:
81-
# Base service URL (without port)
82-
# For Kubernetes: use the k8s service name (e.g., "http://inferadb-engine.inferadb")
83-
# For direct connection: use the host (e.g., "http://localhost")
8435
url: "http://inferadb-engine.inferadb"
85-
86-
# gRPC port for engine communication
8736
grpc: 8081
88-
89-
# Mesh port for webhooks and JWKS
9037
port: 8082
38+
39+
discovery:
40+
mode:
41+
type: kubernetes
42+
cache_ttl: 30

config.yaml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
# Good defaults for local development and testing
33

44
control:
5-
threads: 4
6-
logging: "info"
7-
85
frontend:
96
url: "http://localhost:9090"
107

@@ -13,19 +10,13 @@ control:
1310
grpc: "127.0.0.1:9091"
1411
mesh: "0.0.0.0:9092"
1512

16-
storage: "memory"
17-
1813
webauthn:
1914
party: "localhost"
2015
origin: "http://localhost:9090"
2116

22-
# secret: "" # Set via INFERADB__CONTROL__SECRET
23-
2417
email:
2518
host: "localhost"
26-
port: 1025 # MailHog default port for development
27-
# username: ""
28-
# password: "" # Set via INFERADB__CONTROL__EMAIL__PASSWORD
19+
port: 1025 # MailHog default port for development
2920
address: "noreply@inferadb.local"
3021
name: "InferaDB"
3122

crates/inferadb-control-api/src/handlers/auth.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,14 @@ impl AppState {
144144

145145
/// Create AppState for testing with default configuration
146146
/// This is used by both unit tests and integration tests
147+
#[allow(clippy::field_reassign_with_default)]
147148
pub fn new_test(storage: Arc<Backend>) -> Self {
148149
use inferadb_control_core::ManagementConfig;
149150

150151
// Create a minimal test config using default and overriding necessary fields
151152
let mut config = ManagementConfig::default();
152-
config.secret = Some("test-secret-key-at-least-32-bytes-long!".to_string());
153+
// Use a temporary key file for tests - MasterKey will auto-generate it
154+
config.key_file = Some("/tmp/test-master.key".to_string());
153155
config.webauthn.party = "localhost".to_string();
154156
config.webauthn.origin = "http://localhost:3000".to_string();
155157
let server_client = ServerApiClient::new("http://localhost".to_string(), 8080).unwrap();

crates/inferadb-control-api/src/handlers/clients.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use axum::{
55
};
66
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
77
use inferadb_control_core::{
8-
Error as CoreError, IdGenerator, PrivateKeyEncryptor, RepositoryContext, keypair,
8+
Error as CoreError, IdGenerator, MasterKey, PrivateKeyEncryptor, RepositoryContext, keypair,
99
};
1010
use inferadb_control_types::{
1111
dto::{
@@ -293,16 +293,9 @@ pub async fn create_certificate(
293293
let (public_key_base64, private_key_bytes) = keypair::generate();
294294

295295
// Encrypt private key for storage
296-
tracing::debug!("Retrieving secret from config");
297-
let master_secret = state
298-
.config
299-
.secret
300-
.as_ref()
301-
.ok_or_else(|| CoreError::Internal("secret not configured".to_string()))?
302-
.as_bytes();
303-
304-
tracing::debug!(secret_len = master_secret.len(), "Creating encryptor");
305-
let encryptor = PrivateKeyEncryptor::new(master_secret)?;
296+
tracing::debug!("Loading master key for encryption");
297+
let master_key = MasterKey::load_or_generate(state.config.key_file.as_deref())?;
298+
let encryptor = PrivateKeyEncryptor::from_master_key(&master_key)?;
306299

307300
tracing::debug!("Encrypting private key");
308301
let private_key_encrypted = encryptor.encrypt(&private_key_bytes)?;

0 commit comments

Comments
 (0)