12 releases (4 breaking)
Uses new Rust 2024
| new 0.18.1 | Dec 20, 2025 |
|---|---|
| 0.18.0 | Dec 20, 2025 |
| 0.17.4 | Dec 19, 2025 |
| 0.6.0 | Nov 6, 2025 |
| 0.4.2 | Oct 21, 2025 |
#1337 in Database interfaces
2MB
42K
SLoC
Control Layer API Server (dwctl)
Rust-based API server for user, group, and model management with PostgreSQL database.
Local Development Setup
Prerequisites
- Rust (latest stable)
- PostgreSQL running locally
- sqlx-cli:
cargo install sqlx-cli
1. Database Setup
# 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;"
2. Environment Configuration
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.
3. Run Database Migrations
cd dwctl
sqlx migrate run
4. Generate Query Cache (for builds without database)
# Generate offline query cache
cargo sqlx prepare
# This creates .sqlx/ directory with cached query metadata
Running the Service
cd dwctl
# Run with live database connection
cargo run
# Run tests (requires database)
cargo test
Configuration
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 string
Authentication
Control Layer supports two authentication methods:
Native Authentication
Username/password authentication with session cookies. Users can register and log in directly through the Control Layer UI or API.
Proxy Header Authentication
Accepts user identity from HTTP headers set by an upstream authentication proxy (e.g., oauth2-proxy, Vouch, Authentik, Auth0).
Header Configuration
You can configure authentication in two ways:
Single Header Mode (Simple)
- Send only
x-doubleword-userheader with the user's email - The email must be unique per user
- Example:
x-doubleword-user: "user@example.com"
Dual Header Mode (Federated Identity)
- Send both
x-doubleword-user(unique identifier from IdP) andx-doubleword-email(email address) - Allows multiple accounts with the same email from different identity providers
- The combination of (email, external_user_id) must be unique
- Examples:
- GitHub user:
x-doubleword-user: "github|user123",x-doubleword-email: "user@example.com" - Google user:
x-doubleword-user: "google-oauth2|456",x-doubleword-email: "user@example.com"
- GitHub user:
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.
Configuration
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
User Roles and Permissions
Control Layer uses an additive role-based access control system where users can have multiple roles that combine to provide different levels of access.
Role Types
StandardUser (Base Role)
- Required for all users - Cannot be removed
- Enables basic authentication and login functionality
- Provides access to user's own profile and data
- Allows model access, API key creation, and playground usage
- Foundation role that all other roles build upon
PlatformManager
- Administrative access to most platform functionality
- Can create, update, and delete users
- Can manage groups and group memberships
- Can control access to models and manage inference endpoints
- Can configure system settings
- Cannot view private request data (requires RequestViewer)
RequestViewer
- Read-only access to request logs and analytics
- Can view all requests that have transited the gateway
- Useful for auditing, monitoring, and analytics purposes
- Often combined with other roles for full administrative access
Role Combinations
Roles are additive, meaning users gain the combined permissions of all their assigned roles:
- StandardUser only: Basic user with profile access and model usage
- StandardUser + PlatformManager: Full administrative access except request viewing
- StandardUser + RequestViewer: Basic user who can also view request logs
- StandardUser + PlatformManager + RequestViewer: Full system administrator with all permissions
Role Management
- All users automatically receive and retain the
StandardUserrole - Additional roles can be assigned/removed via the admin interface
- The system automatically ensures
StandardUseris preserved during role updates - Role changes take effect immediately without requiring user re-authentication, unless using native auth with jwts, whereby a user needs to logout and back for API access effects to take place
Troubleshooting
Database connection errors
- Ensure PostgreSQL is running:
brew services start postgresql - Check DATABASE_URL in
.envfile - Verify database exists:
psql -l | grep control_layer
Migration 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)
Database Schema
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
API Endpoints
- See OpenAPI docs at
/admin/docswhen running
Dependencies
~113MB
~2M SLoC