#endpoint #proxy #model #observability #control #server-api #postgresql #authentication #llm

bin+lib dwctl

The Doubleword Control Layer - A self-hostable observability and analytics platform for LLM applications

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

MIT/Apache

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-user header 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) and x-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"

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 StandardUser role
  • Additional roles can be assigned/removed via the admin interface
  • The system automatically ensures StandardUser is 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 .env file
  • 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 tables
  • 002_listen_notify.sql - PostgreSQL notify triggers
  • 003_make_hosted_on_not_null.sql - Schema updates

API Endpoints

  • See OpenAPI docs at /admin/docs when running

Dependencies

~113MB
~2M SLoC