| Crates.io | dwctl |
| lib.rs | dwctl |
| version | 2.9.2 |
| created_at | 2025-10-21 04:46:09.518609+00 |
| updated_at | 2026-01-24 10:11:31.097081+00 |
| description | The Doubleword Control Layer - A self-hostable observability and analytics platform for LLM applications |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1893276 |
| size | 3,048,481 |
Rust-based API server for user, group, and model management with PostgreSQL database.
cargo install sqlx-cli# Start PostgreSQL (macOS with Homebrew)
brew services start postgresql
# Create database
createdb control_layer
# Or connect to existing PostgreSQL instance
psql -c "CREATE DATABASE control_layer;"
Create .env file in the dwctl directory:
# dwctl/.env
DATABASE_URL=postgres://your-username@localhost:5432/control_layer
Replace your-username with your PostgreSQL username.
cd dwctl
sqlx migrate run
# Generate offline query cache
cargo sqlx prepare
# This creates .sqlx/ directory with cached query metadata
cd dwctl
# Run with live database connection
cargo run
# Run tests (requires database)
cargo test
The service uses config.yaml (or DWCTL_* environment variables):
DWCTL_HOST: Server host (default: 0.0.0.0)DWCTL_PORT: Server port (default: 3001)DATABASE_URL: PostgreSQL connection stringThe control layer uses PostgreSQL for data storage. By default, all components share a single database using separate schemas:
public): Core application data (users, groups, models, etc.)fusillade): Batch processing dataoutlet): Request logging datadatabase:
type: external
url: postgres://user:pass@localhost:5432/dwctl
For high-traffic deployments, you can configure a read replica for read-heavy operations:
database:
type: external
url: postgres://user:pass@primary:5432/dwctl
replica_url: postgres://user:pass@replica:5432/dwctl
Components can optionally use dedicated databases instead of schemas. This is useful for:
database:
type: external
url: postgres://user:pass@localhost:5432/dwctl
# Use dedicated database for batch processing
fusillade:
mode: dedicated
url: postgres://user:pass@localhost:5432/fusillade
replica_url: postgres://user:pass@replica:5432/fusillade
pool:
max_connections: 30
# Keep outlet in main database (default behavior)
outlet:
mode: schema
name: outlet
pool:
max_connections: 5
Each component can have its own pool configuration:
database:
type: external
url: postgres://user:pass@localhost:5432/dwctl
pool:
max_connections: 10
min_connections: 0
acquire_timeout_secs: 30
idle_timeout_secs: 600
max_lifetime_secs: 1800
Control Layer supports two authentication methods:
Username/password authentication with session cookies. Users can register and log in directly through the Control Layer UI or API.
Accepts user identity from HTTP headers set by an upstream authentication proxy (e.g., oauth2-proxy, Vouch, Authentik, Auth0).
You can configure authentication in two ways:
Single Header Mode (Simple)
x-doubleword-user header with the user's emailx-doubleword-user: "user@example.com"Dual Header Mode (Federated Identity)
x-doubleword-user (unique identifier from IdP) and x-doubleword-email (email address)x-doubleword-user: "github|user123", x-doubleword-email: "user@example.com"x-doubleword-user: "google-oauth2|456", x-doubleword-email: "user@example.com"In dual header mode, the same email can belong to different users as long as they have different external user IDs from their identity providers.
See config.yaml for proxy header authentication settings:
auth:
proxy_header:
enabled: true
header_name: "x-doubleword-user" # User identifier or email (single mode)
email_header_name: "x-doubleword-email" # User's email (dual mode, optional)
auto_create_users: true # Auto-create users on first login
Control Layer uses an additive role-based access control system where users can have multiple roles that combine to provide different levels of access.
Roles are additive, meaning users gain the combined permissions of all their assigned roles:
StandardUser roleStandardUser is preserved during role updatesDatabase connection errors
brew services start postgresql.env filepsql -l | grep control_layerMigration errors
# Reset database
sqlx database reset # add `-y` to skip confirmation and `-f` if you get a
# 'other user are connected' error (usually your IDE is also connected)
Migrations are stored in the migrations/ directory, and run automatically on startup.
001_initial.sql - Users, groups, models tables002_listen_notify.sql - PostgreSQL notify triggers003_make_hosted_on_not_null.sql - Schema updates/admin/docs when running